【代码随想录】+ leetcode hot100:栈与队列算法专题总结、单调栈

大家好,我是此林。

今天分享的是【代码随想录】栈与队列算法专题总结,分享刷算法的心得体会。

1. 用栈实现队列、用队列实现栈

232. 用栈实现队列 - 力扣(LeetCode)

225. 用队列实现栈 - 力扣(LeetCode)

这两题放在一起,比较相似。

1. 用栈实现队列:使用两个栈,根本是让出栈顺序和队列一样

stack1 = new Stack<>(); // 主栈,和队列顺序一致
stack2 = new Stack<>(); // 辅助栈

2. 用队列实现栈:使用两个队列,根本是让出队顺序一样

queue1 = new LinkedList<>(); // 主队列,和栈顺序一致
queue2 = new LinkedList<>(); // 辅助队列

所以无论是队列还是栈,只要重点改push的逻辑即可。

class MyQueue {Stack<Integer> stack1;Stack<Integer> stack2;public MyQueue() {stack1 = new Stack<>();stack2 = new Stack<>();}public void push(int x) {// 重点改这里的逻辑}public int pop() {return stack1.pop();}public int peek() {return stack1.peek();}public boolean empty() {return stack1.isEmpty();}
}
class MyStack {Queue<Integer> queue1;Queue<Integer> queue2;public MyStack() {queue1 = new LinkedList<>();queue2 = new LinkedList<>();}public void push(int x) {// 重点改这里的逻辑}public int pop() {return queue1.poll();}public int top() {return queue1.peek();}public boolean empty() {return queue1.isEmpty();}
}

其他方法,因为 stack1 或者 queue1 和 队列 或者 栈 的顺序一致,所以只需要操作对应方法即可。

用栈实现队列完整代码

class MyQueue {Stack<Integer> stack1;Stack<Integer> stack2;public MyQueue() {stack1 = new Stack<>();stack2 = new Stack<>();}public void push(int x) {while (!stack1.isEmpty()) {stack2.push(stack1.pop());}stack2.push(x);while (!stack2.isEmpty()) {stack1.push(stack2.pop());}}public int pop() {return stack1.pop();}public int peek() {return stack1.peek();}public boolean empty() {return stack1.isEmpty();}
}

用队列实现栈完整代码:

class MyStack {Queue<Integer> queue1;Queue<Integer> queue2;public MyStack() {queue1 = new LinkedList<>();queue2 = new LinkedList<>();}public void push(int x) {queue2.offer(x);while (!queue1.isEmpty()) {queue2.offer(queue1.poll());}Queue<Integer> tmp = queue1;queue1 = queue2;queue2 = tmp;}public int pop() {return queue1.poll();}public int top() {return queue1.peek();}public boolean empty() {return queue1.isEmpty();}
}

2. 有效的括号

20. 有效的括号 - 力扣(LeetCode)

这题本质思路:实现一个栈,

1. 如果是左括号直接入栈,

2. 如果是右括号则和第一个出栈的左括号比对,如果匹配,左括号出栈。

如此循环,最后看栈是否Empty,如果不为空,则非有效括号(false)。

那么左右括号比对,可以写一个HashMap做映射。

class Solution {public boolean isValid(String s) {Map<Character, Character> map = new HashMap<>();map.put(']', '[');map.put(')', '(');map.put('}', '{');Stack<Character> stack = new Stack<>();for (char c : s.toCharArray()) {// 如果 c 是左括号,直接入栈if (c == '(' || c == '[' || c == '{') {stack.push(c);} else {if (!stack.isEmpty() && stack.peek() == map.get(c)) {stack.pop();} else {stack.push(c);}}}return stack.isEmpty();}
}

3. 删除字符串中的所有相邻重复项

1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)

这题和 有效括号 的思路是一样的,甚至更加简单。 

有效括号 还需要定义HashMap来做左右括号的匹配映射,本题只需要判断是否相等即可。

完整代码:

class Solution {public String removeDuplicates(String s) {Stack<Character> stack = new Stack<>();for (char c : s.toCharArray()) {if (!stack.isEmpty() && stack.peek() == c) {stack.pop();} else {stack.push(c);}}String res = "";while (!stack.isEmpty()) {res = stack.pop() + res;}return res;}
}

需要注意的是,最后出栈顺序会颠倒,所以不能直接 res += stack.pop(),

要改成 res = stack.pop() + res。

4. 逆波兰表达式求值

150. 逆波兰表达式求值 - 力扣(LeetCode)

这题其实也不难,

整体思路是遍历 tokens,如果是数字,直接入栈;

如果是运算符(+、-、*、/),那么就 stack.pop() 出栈两次,然后把取得两个数字做对应的运算,得到的结果入栈即可。

完整代码:

class Solution {public int evalRPN(String[] tokens) {Stack<Integer> stack = new Stack<>();for (String s : tokens) {switch (s) {case "+":stack.push(Integer.valueOf(stack.pop()) + Integer.valueOf(stack.pop()));break;case "-":int a = Integer.valueOf(stack.pop());int b = Integer.valueOf(stack.pop());stack.push(b - a);break;case "*":stack.push(Integer.valueOf(stack.pop()) * Integer.valueOf(stack.pop()));break;case "/":a = Integer.valueOf(stack.pop());b = Integer.valueOf(stack.pop());stack.push(b / a);break;default:// 数字直接入栈stack.push(Integer.valueOf(s));}}return stack.pop();}
}

可以看到,一个 switch 解决一切。

需要注意的是,减法和出除法需要颠倒下位置。

虽然这样写思路可能比较清晰,但是代码有点冗余,特别是 Integer.valueOf() 被频繁使用。

改写优化:

class Solution {public int evalRPN(String[] tokens) {Set<String> set = new HashSet<>();set.add("+");set.add("-");set.add("*");set.add("/");Stack<Integer> stack = new Stack<>();for (String token : tokens) {// 如果 token 为数字if (!set.contains(token)) {stack.push(Integer.valueOf(token));} else {Integer a = stack.pop();Integer b = stack.pop();if (token.equals("+")) {stack.push(a + b);} else if (token.equals("-")) {stack.push(b - a);} else if (token.equals("*")) {stack.push(a * b);} else if (token.equals("/")) {stack.push(b / a);}}}return stack.pop();}
}

5. 前 K 个高频元素

347. 前 K 个高频元素 - 力扣(LeetCode)

这题没什么花头,常规思路而已:

1. 遍历nums,然后用HashMap统计每个num的出现次数

2. 根据出现次数对HashMap进行排序

重点在这个排序,因为题目说时间复杂度 必须 优于 O(n log n) 

所以排序用Java里现成的优先队列 PriorityQueue 即可,设置为大顶堆即可。

完整代码:

class Solution {public int[] topKFrequent(int[] nums, int k) {// 统计每个num出现的次数Map<Integer, Integer> map = new HashMap<>();for (int num : nums) {map.put(num, map.getOrDefault(num, 0) + 1);}// 对map进行从大到小排序PriorityQueue<int[]> queue = new PriorityQueue<>((o1, o2) -> o2[1] - o1[1]);for (Map.Entry<Integer, Integer> entry : map.entrySet()) {int[] tmp = new int[2];tmp[0] = entry.getKey();tmp[1] = entry.getValue();queue.offer(tmp);}// 封装TopK结果int[] res = new int[k];for (int i = 0; i < k; i++) {res[i] = queue.poll()[0];}return res;}
}

这里第一部分统计出现的次数没什么好说,

第二部分,PriorityQueue 是 Java 封装好的排序队列,每次加入队列的元素会自动排序,排序规则在构造方法里传入了:(o1, o2) -> o2[1] - o1[1]

这个tmp数组,tmp[0] 代表num,tmp[1] 代表num出现的次数

(o1, o2) -> o2[1] - o1[1] 其实就表示按照num出现的次数从大到小逆序排序。

6. 最小栈

155. 最小栈 - 力扣(LeetCode)

看到题目,第一反应就是新建一个 Stack<Integer> 和 一个 Integer min 变量。

于是,你写了下面的代码:

class MinStack {Stack<Integer> stack;int min = Integer.MAX_VALUE;public MinStack() {stack = new Stack<>();}public void push(int val) {if (val < min) {min = val;}stack.push(val);}public void pop() {stack.pop();}public int top() {return stack.peek();}public int getMin() {return min;}
}

这就有个问题,看下面例子:

入栈 3 
|   |   min = 3
|   |     
|_3_|    
stack   入栈 5 
|   |   min = 3
| 5 |     
|_3_|    
stack  入栈 2 
| 2 |   min = 2
| 5 |     
|_3_|    
stack  出栈 2 
|   |   min = 2?
| 5 |     
|_3_|    
stack  

如果只用一个变量就会遇到一个问题,如果把 min 更新为 2,那么之前的最小值 3 就丢失了。

怎么把 3 保存起来呢?把它在 2 之前压入栈中即可。

入栈 2 ,同时将之前的 min 值 3 入栈,再把 2 入栈,同时更新 min = 2
| 2 |   min = 2
| 3 |  
| 5 |     
|_3_|    
stack  入栈 6 
| 6 |  min = 2
| 2 |   
| 3 |  
| 5 |     
|_3_|    
stack  出栈 6     
| 2 |   min = 2
| 3 |  
| 5 |     
|_3_|    
stack  出栈 2 ,然后再出栈一次,把 3 赋值给 min 即可。    
|   |   min = 3
|   |  
| 5 |     
|_3_|    
stack  

完整代码:

class MinStack {Stack<Integer> stack;int min = Integer.MAX_VALUE;public MinStack() {stack = new Stack<>();}public void push(int val) {// 如果min需要变更了,先把旧的min入栈保存,再入栈新的min,同时更新minif (val <= min) {stack.push(min);min = val;}stack.push(val);}public void pop() {// 如果发现出栈的元素等于min,那么再出栈一次,并把min重新赋值if (stack.pop() == min) {min = stack.pop();}}public int top() {return stack.peek();}public int getMin() {return min;}
}

所以,这里的核心,就是把旧的min压栈做副本保存,再入栈新的min,同时更新min。

后续 pop() 出栈,如果发现出栈的元素等于min,就出栈两次即可。

当然,还有一种官方的解法,他另外用了一个辅助栈,其实思路大差不差,都是为了缓存旧的min。这里可以看下他的思路。

class MinStack {Stack<Integer> stack;Stack<Integer> stack1; // 辅助栈public MinStack() {stack = new Stack<>();stack1 = new Stack<>();stack1.push(Integer.MAX_VALUE);}public void push(int val) {stack.push(val);stack1.push(Math.min(val, stack1.peek()));}public void pop() {stack.pop();stack1.pop();}public int top() {return stack.peek();}public int getMin() {return stack1.peek();}
}

理解算法,重点在于带入例子,去理解他的过程,没有捷径可以走。

7. 字符串解码

394. 字符串解码 - 力扣(LeetCode)

这种题目就是纯模拟题,没有什么技巧可言。

总体思路:

1. 遍历字符串,如果不是 ],即数字或 [,直接入栈。

2. 如果是 ]

3. stack.pop() + while循环 提取出需要重复的字符串 tmp

4. stack.pop() + while循环 提取出需要重复的次数 time

5. 根据 time 和 tmp 重复字符串入栈,最后整合结果。

完整代码:

class Solution {public String decodeString(String s) {Stack<Character> stack = new Stack<>(); // 遍历字符串for (char c : s.toCharArray()) {// 如果不是 ] ,是数字或 [ ,直接入栈if (c != ']') {stack.push(i);} else {// 提取出需要遍历的字符串 tmpString tmp = "";while (stack.peek() != '[') {tmp = stack.pop() + tmp;}// 删掉 [stack.pop();// 提取出需要重复的次数 timeString time = "";while (!stack.isEmpty() && stack.peek() >= '0' && stack.peek() <= '9') {time = stack.pop() + time;}// 根据 tmp 和 time 重复出字符串入栈for (int i = 0; i < Integer.valueOf(time); i++) {for (char t : tmp.toCharArray()) {stack.push(t);}}}}// 整合结果为字符串String res = "";while (!stack.isEmpty()) {res = stack.pop() + res;}return res;}
}

8. 每日温度

从 每日温度 开始,往后题目均为 单调栈 的解题思路。

单调栈 模板:

Stack<Integer> stack = new Stack<>();for (int i = 0; i < arr.length; i++) {while (!stack.isEmpty() && arr[i] > arr[stack.peek()]) {// ...}stack.push(i);}

739. 每日温度 - 力扣(LeetCode)

 这题其实暴力法可以做,

第一个for循环遍历 temperatures,第二个for循环负责去找第一个比当前元素大的位置。

但是,本题用 单调栈 即可。

先明确两个事情:

1. 这个栈里存的元素是 数组下标,而不是实际元素。因为后续要计算间隔天数。

2. 这个栈是单调的,不过不是数组下标单调,而是数组下标对应的 temperature 每次入栈都应该比栈顶的要大。

为了方便理解,我们来理解过程。

首先,明确下面三个变量。

temperatures =  [73,74,75,71,69,72,76,73]  // 题目给出的温度列表Stack<Integer> stack = new Stack<>()  // 单调栈int[] res =  [0,0,0,0,0,0,0,0]  // 最终答案

1. 当 i=0 时,单调栈为空,因此将 0 进栈。

stack=[0(73)]

ans=[0,0,0,0,0,0,0,0]

2. 当 i=1 时,由于 74 大于 73,因此移除栈顶元素 0,赋值 ans[0]:=1−0,将 1 进栈。

stack=[1(74)]

ans=[1,0,0,0,0,0,0,0]

注:ans[0]:=1−0 表示计算天数间隔。

3. 当 i=2 时,由于 75 大于 74,因此移除栈顶元素 1,赋值 ans[1]:=2−1,将 2 进栈。

stack=[2(75)]

ans=[1,1,0,0,0,0,0,0]

4. 当 i=3 时,由于 71 小于 75,因此将 3 进栈。

stack=[2(75),3(71)]

ans=[1,1,0,0,0,0,0,0]

5. 当 i=4 时,由于 69 小于 71,因此将 4 进栈。

stack=[2(75),3(71),4(69)]

ans=[1,1,0,0,0,0,0,0]

6. 当 i=5 时,由于 72 大于 69 和 71,因此依次移除栈顶元素 4 和 3,赋值 ans[4]:=5−4 和 ans[3]:=5−3,将 5 进栈。

stack=[2(75),5(72)]

ans=[1,1,0,2,1,0,0,0]

7. 当 i=6 时,由于 76 大于 72 和 75,因此依次移除栈顶元素 5 和 2,赋值 ans[5]:=6−5 和 ans[2]:=6−2,将 6 进栈。

stack=[6(76)]

ans=[1,1,4,2,1,1,0,0]

8. 当 i=7 时,由于 73 小于 76,因此将 7 进栈。

stack=[6(76),7(73)]

ans=[1,1,4,2,1,1,0,0]

完整代码:

class Solution {public int[] dailyTemperatures(int[] temperatures) {Stack<Integer> stack = new Stack<>();int[] res = new int[temperatures.length];for (int i = 0; i < temperatures.length; i++) {while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {int idx = stack.pop();res[idx] = i - idx;}stack.push(i);}return res;}
}

9. 柱状图中的最大的矩形

84. 柱状图中最大的矩形 - 力扣(LeetCode)

这题也是单调栈的思路。这里进入单调栈的还是数组索引下标,和 每日温度 一样

先说下算法的硬流程。

1. 输入 heights = [2, 1, 5, 6, 2, 3]

2. 加入哨兵(其实就是首尾两个0,方便后续代码):

    heights = [0, 2, 1, 5, 6, 2, 3, 0]

3. 进入遍历 heights 逻辑

完整代码:

class Solution {public int largestRectangleArea(int[] heights) {// 加入哨兵的逻辑int[] newHeights = new int[heights.length + 2];newHeights[0] = newHeights[newHeights.length - 1] = 0;for (int i = 0; i < heights.length; i++) {newHeights[i + 1] = heights[i];}int res = 0;Stack<Integer> stack = new Stack<>();// 遍历 heights 逻辑for (int i = 0; i < newHeights.length; i++) {while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) {int idx = stack.pop();int w = i - stack.peek() - 1; // right - left - 1int h = newHeights[idx];res = Math.max(w * h, res);}stack.push(i);}return res;}
}

遍历 heights,是要以当前高度为基准,寻找最大的宽度 组成最大的矩形面积那就是要找左边第一个小于当前高度的下标left,再找右边第一个小于当前高度的下标right,那宽度就是这两个下标之间的距离了,但是要排除这两个下标,所以是  right - left - 1,用单调栈就可以很方便确定这两个边界了。

10. 下一个更大元素 I

496. 下一个更大元素 I - 力扣(LeetCode)

这题本来想用暴力做法先熟悉下题目的,结果暴力做法直接通过了......

完整代码也贴一下,这里就不多说了。

class Solution {public int[] nextGreaterElement(int[] nums1, int[] nums2) {int[] res = new int[nums1.length];for (int k = 0; k < nums1.length; k++) {OUT:for (int i = 0; i < nums2.length; i++) {if (nums1[k] == nums2[i]) {for (int j = i; j < nums2.length; j++) {if (nums2[j] > nums1[k]) {res[k] = nums2[j];break OUT;}}res[k] = -1;}}}return res;}
}

这题依旧可以用单调栈法。

完整代码:

class Solution {public int[] nextGreaterElement(int[] nums1, int[] nums2) {// 构建HashMap,映射为 nums[i] -> iMap<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums1.length; i++) {map.put(nums1[i], i);}Deque<Integer> stack = new LinkedList<>();int[] res = new int[nums1.length];Arrays.fill(res, -1); // -1 表示不存在// 遍历 nums2for (int x : nums2) {while (!stack.isEmpty() && x > stack.peek()) {// x 是栈顶的下一个更大元素// 既然栈顶已经算出答案,弹出res[map.get(stack.pop())] = x; // 记录结果到res}// 只有 nums1 存在的元素才入栈if (map.containsKey(x)) {stack.push(x);}}return res;}
}

举个例子过程:

nums1 = [4,1,2],  nums2 = [1,3,4,2],  stack = new Stack<>(),   res = [-1, -1, -1]

1. 遍历 nums2

2. 第一个元素 1 在nums1里,此时 stack 为 empty,直接加入 stack。

    stack:栈底 → [1]

3. 第二个元素 3 不在nums1里,元素 3 大于 stack.peek(),记录结果 3 到 res 数组。

    同时 stack.pop(),

    stack:栈底,res = [-1, 3, -1]

4. 第三个元素 4 在nums1里,此时 stack 为 empty,直接加入 stack。

    stack:栈底 → [4]

5. 第四个元素 2 在nums1里,元素 2 小于 stack.peek(),不记录结果。

    stack:栈底 → [4] → [2]

11. 下一个更大元素 II

503. 下一个更大元素 II - 力扣(LeetCode)

和  下一个更大元素 I 不同的是,这里的 nums 变成了 循环数组,也就是下一个更大的元素可能不在后面,但可能在前面!

这里用两次遍历即可解决,在 下一个更大元素 I 的基础上改下即可

完整代码:

class Solution {public int[] nextGreaterElements(int[] nums) {int n = nums.length;// 本题入栈的是数组下标Deque<Integer> stack = new LinkedList<>();int[] res = new int[n];Arrays.fill(res, -1);// 这里改为 n*2for (int i = 0; i < 2 * n; i++) {int x = nums[i % n];while (!stack.isEmpty() && x > nums[stack.peek()]) {res[stack.pop()] = x;}// 只有下标小于n的才入栈if (i < n) {stack.push(i);}}return res;}
}

12. 接雨水

42. 接雨水 - 力扣(LeetCode)

用了单调栈,从此接雨水可以秒了。(真的吗?)

不管怎么说,先看看暴力解法吧,也可以通过,这个是理解题目的基础

class Solution {public int trap(int[] height) {int sum = 0;for (int i = 0; i < height.length; i++) {// 第一个柱子和最后一个柱子不接雨水if (i==0 || i== height.length - 1) continue;int rHeight = height[i]; // 记录右边柱子的最高高度int lHeight = height[i]; // 记录左边柱子的最高高度for (int r = i+1; r < height.length; r++) {if (height[r] > rHeight) rHeight = height[r];}for (int l = i-1; l >= 0; l--) {if(height[l] > lHeight) lHeight = height[l];}int h = Math.min(lHeight, rHeight) - height[i];if (h > 0) sum += h;}return sum;}
}

单调栈法

class Solution {public int trap(int[] height){int size = height.length;Stack<Integer> stack = new Stack<Integer>();stack.push(0);int sum = 0;for (int index = 1; index < size; index++){int stackTop = stack.peek();if (height[index] < height[stackTop]){stack.push(index);}else if (height[index] == height[stackTop]){// 因为相等的相邻墙,左边一个是不可能存放雨水的,所以pop左边的index, push当前的indexstack.pop();stack.push(index);}else{int heightAtIdx = height[index];while (!stack.isEmpty() && (heightAtIdx > height[stackTop])){int mid = stack.pop();if (!stack.isEmpty()){int left = stack.peek();int h = Math.min(height[left], height[index]) - height[mid];int w = index - left - 1;int hold = h * w;if (hold > 0) sum += hold;stackTop = stack.peek();}}stack.push(index);}}return sum;}
}

本文的分享就到这里了,

我是此林,关注我吧,带你看不一样的世界!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2607.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

每次启动服务器都要手动选择启动项

存在的问题 如下图所示&#xff1a; 每次启动服务器的时候&#xff0c;都需要手动将光标选择到第二条&#xff0c;敲回车&#xff0c;才能正常启动系统。从图片可以看到&#xff0c;这是一个 GRUB 启动菜单&#xff0c;显示了三个选项&#xff1a; CentOS Linux (3.10.0-1160.1…

C#.NET BackgroundService 详解

简介 BackgroundService 是 .NET Core 引入的用于实现长时间运行后台任务的基类&#xff0c;位于 Microsoft.Extensions.Hosting 命名空间。它是构建 Worker Service 和后台处理的核心组件。 为什么使用 BackgroundService&#xff1f;优雅的生命周期管理&#xff1a;自动处理启…

数据结构与算法学习(一)

1 字典树&#xff08;前缀树&#xff09; 前缀树是N叉树的一种特殊形式。通常来说&#xff0c;一个前缀树是用来存储字符串的。前缀树的每一个节点代表一个字符串&#xff08;前缀&#xff09;。每一个节点会有多个子节点&#xff0c;通往不同子节点的路径上有着不同的字符。子…

LeetCode Hot 100 二叉树的最大深度

给定一个二叉树 root &#xff0c;返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。示例 1&#xff1a;输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a;输入&#xff1a;root [1,null,2] 输出&#…

Flutter基础(前端教程①①-底部导航栏)

1. 主页面&#xff08;BottomNavBarPage&#xff09;用 _currentBarIndex 记录当前选中的导航索引&#xff08;默认 0&#xff0c;即首页&#xff09;。用 IndexedStack 管理 4 个页面&#xff0c;通过 _currentBarIndex 控制显示哪个页面&#xff08;比如索引 1 就显示 NodePa…

HTML 入门教程:从零开始学习网页开发基础

一、HTML简介 1.1 什么是HTML&#xff1f; HTML全称是Hyper Text Markup Language&#xff08;超文本标记语言&#xff09;&#xff0c;由Tim Berners-Lee和同事Daniel W. Connolly于1990年创立。它是一种用于创建网页的标准标记语言&#xff0c;而不是编程语言。 1.2 HTML的…

LeetCode Hot100【4. 寻找两个正序数组的中位数】

4. 寻找两个正序数组的中位数 自己做 分析 解1&#xff1a;归并思想 class Solution { public:double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int sum 0;double value;queue<double> value2;int i 0, j 0;if ((nums…

JobSet:Kubernetes 分布式任务编排的统一解决方案

JobSet–Kubernetes 分布式任务编排的统一解决方案 在 Kubernetes 生态中&#xff0c;分布式机器学习训练和高性能计算&#xff08;HPC&#xff09;工作负载的编排一直是技术难点。随着大语言模型&#xff08;LLM&#xff09;等大型 AI 模型的兴起&#xff0c;单主机资源已无法…

【电脑】显示器的基础知识

显示器是计算机系统中用于显示图像、文本和其他视觉内容的重要组件。它将电子信号转化为可见的图像&#xff0c;使用户可以直观地查看和操作数据。以下是关于显示器的一些详细知识&#xff1a;1. 显示器的基本类型CRT显示器&#xff08;阴极射线管显示器&#xff09;工作原理&a…

【删库跑路】一次删除pip的所有第三方库

进入命令行&#xff0c;先list看下库存pip list导出所有的第三方库至一文件列表pip freeze >requirements.txt按照列表卸载所有库pip uninstall -r requirements.txt -y再list看下&#xff0c;可见库存已清空

19.如何将 Python 字符串转换为 Slug

如何将 Python 字符串转换为 Slug(URL 友好格式) 什么是 Slug? Slug 是一种 URL 友好、便于人类阅读的字符串。只包含小写字母、数字和连字符(-)。常见于文章标题、商品名等生成的网址路径中。例如: "Hello World!" → "hello-world"1. Slugify 的…

3.2数据库-关系代数-函数依赖-范式

1、关系代数基础1、并U&#xff1a;记录合并&#xff0c;相同记录只显示一次2、交&#xff1a;两张表都有的记录。3、差&#xff1a;S1-S2 表示S1减去S2中也有的数据。笛卡尔积&#xff08;重要&#xff09;1、笛卡尔积&#xff1a;S1*S2 :列是所有列全部加起来&#xff0c;重复…