代码随想录 栈与队列 Java

news2024/11/26 4:27:32

文章目录

  • (简单)232. 用栈实现队列
  • (简单)225. 用队列实现栈
  • (简单)20. 有效的括号
  • (简单)1047. 删除字符串中的所有相邻重复项
  • (中等)150. 逆波兰表达式求值
  • (困难)239. 滑动窗口最大值
  • (中等)347. 前K个高频元素


(简单)232. 用栈实现队列

在这里插入图片描述
用两个栈模拟队列

import java.util.Stack;

class MyQueue {

    private Stack<Integer> s1;
    private Stack<Integer> s2;

    public MyQueue() {
        s1 = new Stack<>();
        s2 = new Stack<>();
    }

    public void push(int x) {
        while (!s2.isEmpty()) {
            s1.push(s2.pop());
        }
        s1.push(x);
    }

    public int pop() {
        while (!s1.isEmpty()) {
            s2.push(s1.pop());
        }
        return s2.pop();
    }

    public int peek() {
        while (!s1.isEmpty()) {
            s2.push(s1.pop());
        }
        return s2.peek();
    }

    public boolean empty() {
        return s1.isEmpty() && s2.isEmpty();
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述
另一种写法

import java.util.Stack;

class MyQueue {

    private Stack<Integer> inStack;
    private Stack<Integer> outStack;

    public MyQueue() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }

    public void push(int x) {
        inStack.push(x);
    }

    public int pop() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.pop();
    }

    public int peek() {
        if (outStack.isEmpty()) {
            in2out();
        }
        return outStack.peek();
    }

    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }

    private void in2out() {
        while (!inStack.isEmpty()) {
            outStack.push(inStack.pop());
        }
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述
来自代码随想录:代码开发上的习惯问题,在工业级别代码开发中,最忌讳的就是实现一个类似的函数,直接把代码粘贴过来改一改就完事了。

这样的项目代码会越来越乱,一定要懂得复用,功能相近的函数要抽象出来,不要大量的复制粘贴,很容易出问题。

工作中如果发现某一个功能自己要经常用,同事们可能也会用到,自己就花时间把这个功能抽象成一个好用的函数或者工具类,不仅方便自己,也方便他人。

(简单)225. 用队列实现栈

在这里插入图片描述
我的思路,没有按照题目要求使用两个队列实现栈,只使用了一个队列(双端队列)

import java.util.Deque;
import java.util.LinkedList;

class MyStack {

    private Deque<Integer> deque;

    public MyStack() {
        deque = new LinkedList<>();
    }

    public void push(int x) {
        deque.addLast(x);
    }

    public int pop() {
        return deque.pollLast();
    }

    public int top() {
        return deque.peekLast();
    }

    public boolean empty() {
        return deque.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述
为了满足栈的特性,即最后入栈的元素最先出栈,在使用队列实现栈时,应满足队列前端的元素是最后入栈的元素。可以使用两个队列来实现栈的操作,其中queue1用于存储栈内的元素,queue2作为入栈操作的辅助队列。

入栈操作时,首先将元素入队到queue2,然后将queue1的全部元素依次出队并入队到queue2,此时queue2的前端的元素即为新入栈的元素,再将queue1和queue2互换,则queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底。

由于每次入栈操作都确保queue1的前端元素为栈顶元素,因此出栈操作和获取栈顶操作都可以简单实现。出栈操作只需要移除queue1的前端元素并返回即可,获取栈顶元素操作只需要获得queue1的前端元素并返回即可。

由于queue1用于存储栈内的元素,判断栈是否为空时,只需要判断queue1是否为空即可。

在这里插入图片描述

import java.util.LinkedList;
import java.util.Queue;

class MyStack {

    Queue<Integer> queue1;
    Queue<Integer> queue2;

    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    public void push(int x) {
        queue2.add(x);
        while (!queue1.isEmpty()) {
            queue2.add(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();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述
复杂度分析:

  • 时间复杂度:入栈操作O(n),其余操作都是O(1),因为n是栈内的元素个数
    • 入栈操作需要将queue1中的n个元素出队,并入队n+1个元素到queue2,共有2n+1次操作(首先是最新那一个元素入队queue2,n个元素从queue1出队,然后n个元素再入队queue2),因此入栈操作的时间复杂度是O(n)
    • 出栈操作对应将queue1的前端元素出队,时间复杂度是O(1)
    • 获得栈顶元素操作对应获取queue1的前端元素,时间复杂度是O(1)
    • 判断栈是否为空操作只需要判断queue1是否为空,时间复杂度O(1)
  • 空间复杂度:O(n),其中n是栈内元素的个数。需要使用两个队列存储栈内的元素。

只使用一个队列来模拟栈

使用一个队列时,为了满足栈的特性,即最后入栈的元素最先出栈,同样需要满足队列前端的元素是最后入栈的元素。

入栈操作:首先获得入栈前的元素个数n,然后将元素入队到队列,再将队列中的前n个元素(即除了新入栈的元素之外的全部元素)依次出队并入队到队列,此时队列的前端的元素即为新入栈的元素,且队列的前端和后端分别对应栈顶和栈底。

由于每次入栈操作都确保队列的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除队列的前端元素并返回即可,获得栈顶元素操作只需要获得队列的前端元素并返回即可(不移除元素)。

import java.util.LinkedList;
import java.util.Queue;

class MyStack {

    Queue<Integer> queue;

    public MyStack() {
        queue = new LinkedList<>();
    }

    public void push(int x) {
        int n = queue.size();
        queue.add(x);
        for (int i = 0; i < n; i++) {
            queue.add(queue.poll());
        }
    }

    public int pop() {
        return queue.poll();
    }

    public int top() {
        return queue.peek();
    }

    public boolean empty() {
        return queue.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述

复杂度分析:

  • 时间复杂度:入栈操作O(n),其余操作都是O(1),其中n是栈内的元素个数
  • 空间复杂度:O(n),其中n是栈内的元素个数。需要使用一个队列存储栈内的元素

(简单)20. 有效的括号

在这里插入图片描述
我的思路:使用栈结构,如果当前遍历到的是括号的左半边,则存入栈中,如果当前遍历到的是括号的右半边,那么就和栈顶元素匹配,如果能匹配上则弹出栈顶元素,继续遍历下一个,如果不匹配,则返回false;如果当前遍历到的是括号的右半边,并且栈为空,也返回false,说明前面没有与之匹配的左半边括号。

import java.util.Stack;

class Solution {
    public boolean isValid(String s) {
        char[] chars = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (char c : chars) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else if (!stack.isEmpty()) {
                if (c == ']' && stack.peek() == '[') {
                    stack.pop();
                } else if (c == '}' && stack.peek() == '{') {
                    stack.pop();
                } else if (c == ')' && stack.peek() == '(') {
                    stack.pop();
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
        return stack.isEmpty();
    }
}

官方给出了更详细的解释:

当遍历给定的字符串时,当遇到一个左括号时,会期望在后续的遍历中有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此可以将这个左括号放入栈顶。

当遇到一个右括号时,需要将一个相同类型的左括号闭合。此时,取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同类型,或者栈中没有左括号,那么字符串s无效,返回false。

为了快速判断括号的类型,使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。

在遍历结束后,如果栈中没有左括号,说明字符串s中所有左括号闭合,返回true,否则返回false。

注意,有效字符串的长度一定是偶数,如果字符串的长度为奇数,直接返回false。

import java.util.HashMap;
import java.util.Stack;

class Solution {
    public boolean isValid(String s) {

        if ((s.length() & 1) == 1) {
            return false;
        }

        char[] chars = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        HashMap<Character, Character> map = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};
        for (char c : chars) {
            //当前c是右括号
            if (map.containsKey(c)) {
                if (stack.isEmpty() || stack.peek() != map.get(c)) {
                    //栈为空,或者匹配不上
                    return false;
                }
                stack.pop();
            } else {
                //c是左括号
                stack.push(c);
            }
        }
        return stack.isEmpty();
    }
}

在这里插入图片描述
复杂度分析:

  • 时间复杂度:O(n),其中n是字符串的长度
  • 空间复杂度:O(n+| ∑ \sum |),其中 ∑ \sum 表示字符集,本题中字符集串只包含6种括号,| ∑ \sum |=6,栈中的字符数量为O(n),而哈希表使用的空间为O(| ∑ \sum |),相加即可得到总空间复杂度

(简单)1047. 删除字符串中的所有相邻重复项

在这里插入图片描述

使用栈去模拟,如果当前字符与栈顶元素相同,则弹出,如果不相同则加入栈中,最后符合条件的字符都会存储在栈中,但是栈的特点是先进后出,所以使用StringBuilder,总是在首位插入字符,还原字符串原来的顺序

import java.util.Stack;

class Solution {
    public String removeDuplicates(String s) {
        Stack<Character> stack = new Stack<>();
        for (char c : s.toCharArray()) {
            if (!stack.isEmpty() && c == stack.peek()) {
                stack.pop();
            } else {
                stack.push(c);
            }
        }
        StringBuilder stringBuilder = new StringBuilder();
        while (!stack.isEmpty()) {
            stringBuilder.insert(0, stack.pop());
        }
        return stringBuilder.toString();
    }
}

在这里插入图片描述

官方解答

当字符串中同时有多组相邻重复项时,我们无论先删除哪一个,都不会影响最终的结果。所以,从左向右顺次处理该字符串即可。

消除一对相邻重复项可能会导致新的相邻重复项出现。所以,需要保存当前还未被删除的字符。用栈是比较合适的。只需要遍历该字符串,如果当前字符和栈顶字符相同,就贪心地将其消去,否则就将其入栈即可。

对于Java语言,String类没有提供相应的接口,则需要在遍历完成字符串后,使用栈中的字符显式地构造出需要被返回的字符串。

用StringBuilder来模拟栈

class Solution {
    public String removeDuplicates(String s) {

        StringBuilder stack = new StringBuilder();
        int top = -1;
        for (char c : s.toCharArray()) {
            if (top >= 0 && stack.charAt(top) == c) {
                stack.deleteCharAt(top);
                --top;
            } else {
                stack.append(c);
                ++top;
            }
        }
        return stack.toString();
    }
}

在这里插入图片描述

(中等)150. 逆波兰表达式求值

在这里插入图片描述
在这里插入图片描述

我的思路:如果是数字的话,就压入栈中,如果是操作符,就取出栈中的两个元素,注意,加法和乘法,哪一个数字在前,哪一个数字在后没有区别,对于减法和除法来说还是有区别的。先取出来的数字在是要放在操作符的右边,而后取出来的需要放在操作符的左边

import java.util.Stack;

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < tokens.length; i++) {
            if ("+".equals(tokens[i]) || "-".equals(tokens[i]) || "*".equals(tokens[i]) || "/".equals(tokens[i])) {
                Integer i2 = stack.pop();
                Integer i1 = stack.pop();
                switch (tokens[i]) {
                    case "+":
                        stack.push(i1 + i2);
                        break;
                    case "-":
                        stack.push(i1 - i2);
                        break;
                    case "*":
                        stack.push(i1 * i2);
                        break;
                    case "/":
                        stack.push(i1 / i2);
                        break;
                }
            } else {
                stack.push(Integer.valueOf(tokens[i]));
            }
        }
        return stack.peek();
    }
}

在这里插入图片描述
复杂度分析:

  • 时间复杂度:O(n),其中n是tokens的长度。需要遍历数组tokens一次,计算逆波兰表达式的值
  • 空间复杂度:O(n),其中n是数组tokens的长度。使用栈存储计算过程中的数,栈内元素个数不会超过逆波兰表达式的长度

官方其他思路,使用数组模拟

使用一个数组模拟栈操作。

使用数组代替栈,需要预先定义数组的长度。对于长度为n的逆波兰表达式,显然栈内元素个数不会超过n,但是将数组的长度定义为n仍然超过了栈内元素个数的上界

对于一个有效的逆波兰表达式,其长度n一定是奇数,且操作数的个数一定比运算符的个数多1个。考虑遇到操作数和运算符时,栈内元素个数如何变化:

  • 如果遇到操作数,则将操作数入栈,因此栈内元素增加1个
  • 如果遇到运算符,则将两个操作数出栈,然后将一个新操作数入栈,因此栈内元素先减少2个再增加1个,结果是栈内元素减少1个

对于一个有效的逆波兰表达式,其长度n一定是奇数,且操作数的个数一定比运算符个数多1个,即包含 n + 1 2 \frac{n+1}{2} 2n+1个操作数和 n − 1 2 \frac{n-1}{2} 2n1个运算符。

最坏情况下, n + 1 2 \frac{n+1}{2} 2n+1个操作数都在表达书的前面, n − 1 2 \frac{n-1}{2} 2n1个运算符都在表达式后面,因此栈内元素最多为 n + 1 2 \frac{n+1}{2} 2n+1。所以定义数组长度是 n + 1 2 \frac{n+1}{2} 2n+1

class Solution {
    public int evalRPN(String[] tokens) {
        int n = tokens.length;
        int[] stack = new int[(n + 1) / 2];
        int index = -1;
        for (int i = 0; i < n; i++) {
            switch (tokens[i]) {
                case "+":
                    --index;
                    stack[index] += stack[index + 1];
                    break;
                case "-":
                    --index;
                    stack[index] -= stack[index + 1];
                    break;
                case "*":
                    --index;
                    stack[index] *= stack[index + 1];
                    break;
                case "/":
                    --index;
                    stack[index] /= stack[index + 1];
                    break;
                default:
                    ++index;
                    stack[index] = Integer.parseInt(tokens[i]);
            }
        }
        return stack[index];
    }
}

在这里插入图片描述

复杂度分析:

  • 时间复杂度:O(n),其中n是tokens的长度。需要遍历数组tokens一次,计算逆波兰表达式的值。
  • 空间复杂度:O(n),其中n是数组tokens的长度。需要创建长度为 n + 1 2 \frac{n+1}{2} 2n+1的数组模拟栈操作。

(困难)239. 滑动窗口最大值

在这里插入图片描述
官方思路

对于每个滑动窗口,可以使用O(k)的时间遍历其中的每一个元素,找出其中的最大值。对于长度为n的数组nums而言,窗口的数量为n-k+1,因此算法的时间复杂度是O((n-k+1)k)=O(nk),会超出时间限制,因此需要进一步优化。

方法一,优先队列

对于【最大值】,使用优先队列,其中的大根堆可以帮助我们实现维护一系列元素中的最大值。

对于本题,初始时,将数组nums的前k个元素放入优先队列中。每当向右移动窗口时,就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。然而这个最大值并不在滑动窗口中,在这种情况下,这个值在数组nums中的位置出现在滑动窗口的左边界的左侧。因此,当我们继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,可以将这个数永久地从优先队列中移除。

不断地移动堆顶的元素,直到其确定出现在滑动窗口中。此时,堆顶元素就是滑动窗口中的最大值。为了方便判断堆顶元素与滑动窗口的位置关系,可以在优先队列中存储二元组(num,index),表示元素num在数组中的下标为index。

根据上述思路,我的代码如下,优先队列中的每一个元素是一个长度为2的数组,下标为0的位置存放的是具体值,下标为1的位置存放该值在nums数组中的下标

import java.util.Comparator;
import java.util.PriorityQueue;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o2[0] - o1[0];
            }
        });
        int n = nums.length;
        int[] res = new int[n - k + 1];
        for (int i = 0; i < k; i++) {
            queue.add(new int[]{nums[i], i});
        }
        res[0] = queue.peek()[0];
        for (int i = k; i < n; i++) {
            while (!queue.isEmpty() && i - queue.peek()[1] >= k) {
                queue.poll();
            }
            queue.add(new int[]{nums[i], i});
            res[i - k + 1] = queue.peek()[0];
        }
        return res;
    }
}

在这里插入图片描述
复杂度分析:

  • 时间复杂度:O(nlogn),其中n是数组nums的长度。在最坏的情况下,数组nums中的元素单调递增,那么最终优先队列中包含了所有元素,没有元素被移除。由于将一个元素放入优先队列的时间复杂度是O(logn),因此总的时间复杂度为O(nlogn)。
  • 空间复杂度:O(n),即为优先队列需要使用的空间。这里所有的空间复杂度分析都不考虑返回的答案需要的O(n)空间,只计算额外的空间使用。

方法二,单调队列

顺着方法一的思路,继续优化。

目标是为了求出滑动窗口的最大值,假设当前的滑动窗口中有两个下标i和j,其中i在j的左侧(i<j)

当滑动窗口向右移时,只要i还在窗口中,j一定也还在窗口中,这是i在j的左侧所保证的。因此,由于nums[j]的存在,nums[i]一定不会是滑动窗口中的最大值,可以将nums[i]永久移除。

使用一个队列存储所有还没有被移除的下标。在队列中,这些下标按照从小到大的顺序被存储,并且它们在nums中对应的值是严格单调递减的。这也就保证了队首的元素一定是当前滑动窗口中的最大值的下标。

当滑动窗口向右移时,需要把一个新的元素放入队列中。为了保持队列的性质,会不断地将新的元素与队尾元素比较,如果前者大于等于后者,则将队尾的元素永久地移除。需要不断进行此操作,直到队列为空或者新的元素小于队尾地元素。

由于队列中的下标对应的元素时严格单调递减的,因此此时队首下标对应地元素就是滑动窗口中的最大值。但是,随着窗口不断地右移,可能当前队首元素已经不在滑动窗口中了,所以需要判断,弹出不在窗口中的元素。

为了可以同时弹出队首和队尾元素,需要使用双端队列。满足这种单调性的队列叫单调队列。

import java.util.LinkedList;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 0; i < k; i++) {
            while (!list.isEmpty() && nums[list.peekLast()] <= nums[i]) {
                list.pollLast();
            }
            list.addLast(i);
        }
        int n = nums.length;
        int[] res = new int[n - k + 1];
        res[0] = nums[list.peekFirst()];
        for (int i = k; i < n; i++) {
            while (!list.isEmpty() && nums[list.peekLast()] <= nums[i]) {
                list.pollLast();
            }
            list.addLast(i);
            while (i - list.peekFirst() >= k) {
                list.pollFirst();
            }
            res[i - k + 1] = nums[list.peekFirst()];
        }
        return res;
    }
}

在这里插入图片描述

(中等)347. 前K个高频元素

在这里插入图片描述

我的思路,使用HashMap来存储数组中的数以及这个数出现的次数,最后按照value来从大到小排序,取出前k个项的key值保存在数组中返回

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }
        ArrayList<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, (o1, o2) -> o2.getValue() - o1.getValue());
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = list.get(i).getKey();
        }
        return res;
    }
}

在这里插入图片描述
不过这种给HashMap排序的方法,由于可能有O(N)个不同的出现次数(其中N为原数组的长度),故总的算法复杂度会达到O(NlogN),不满足题目要求。

使用堆的思想:建立一个小顶堆,然后遍历【出现次数数组】

  • 如果堆中的元素个数小于k,就可以直接插入堆中
  • 如果堆中的元素个数等于k,则检查堆顶与当前出现次数的大小。如果堆顶更大,说明至少有k个数字的出现次数比当前值大,故舍弃当前值;否则,就弹出堆顶,并将当前值插入堆中

遍历完成后,堆中的元素就代表了【出现次数数组】中前k大的值。

class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
        }

        //int[]数组的第一个元素代表数组的值,第二个元素代表了该值出现的次数
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int num = entry.getKey();
            int count = entry.getValue();
            if (queue.size() < k) {
                queue.add(new int[]{num, count});
            } else {
                if (queue.peek()[1] < count) {
                    queue.poll();
                    queue.add(new int[]{num, count});
                }
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            res[i] = queue.poll()[0];
        }
        return res;
    }
}

在这里插入图片描述
复杂度分析:

  • 时间复杂度:O(Nlogk),其中N为数组的长度。首先遍历原数组,并使用哈希表记录出现次数,每个元素需要O(1)的时间,共需O(N)的时间。随后,遍历【出现次数数组】,由于堆大小至多为k,因此每次堆操作需要O(logk)的时间,共需O(Nlogk)的时间。二者之和为O(Nlogk)。
  • 空间复杂度:O(N)。哈希表的大小为O(N),而堆的大小为O(k),共计为O(N)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【红黑树 -- 理论与实现】

目录&#xff1a; 前言红黑树的概念红黑树的性质 插入过程遇到的情况情况1 -- 根节点情况2 -- parent为黑色情况3 -- parent为红色uncle为红色uncle为黑色uncle不存在 插入过程代码实现分析红黑树是否构建成功总结 前言 打怪升级&#xff1a;第88天 红黑树&#xff0c;可以说是…

Xubuntu16.04 系统偶发出现自动登出的问题

继上次发现的这个问题 xubuntu系统偶发自动登出&#xff0c;这次问题又浮现了&#xff0c;我第一时间拷贝了系统日志。 为了减少搜索量&#xff0c;可以先清除之前的系统日志&#xff0c;待问题出现 echo > /var/log/syslog echo > /var/log/kern.logMar 21 15:07:58 au…

面试:解决数字精度丢失

理论上用有限的空间来存储无限的小数是不可能保证精确的&#xff0c;但我们可以处理一下得到我们期望的结果 当你拿到 1.4000000000000001 这样的数据要展示时&#xff0c;建议使用 toPrecision 凑整并 parseFloat 转成数字后再显示&#xff0c;如下&#xff1a; parseFloat(…

为什么新产品没热度,流量分析

很多人反馈新产品上线之后却没有多少热度&#xff0c;这究竟是什么样原因呢?今天来为大家分享下为什么新产品没热度&#xff0c;流量分析。 新产品没有热度其实可以从两个主要方面进行探讨&#xff1a; 一、主观原因 1.缺乏吸引消费者的独特卖点 这个原因可能是新产品太过于普…

9个服务端提升debug效率的IDEA Debugger技巧

不可否认&#xff0c;未来的一到两年中&#xff0c;程序员的编码体验将会发生剧烈的变化。作为一名一线开发&#xff0c;要如何提前准备&#xff0c;来应对这种变化呢&#xff1f; 前言 在AIGC时代&#xff0c;虽然深度学习模型可以仅通过一段注释来生成我们想要的代码&#xf…

.net 混淆工具

obfuscation tools .net 社区有很多混淆工具, 比如这个清单: https://github.com/NotPrab/.NET-Obfuscator 比较有名的商业工具有 .NET REACTOR https://www.eziriz.com/, 开源软件中, 最受欢迎的有: obfuscar https://github.com/obfuscar/obfuscar老版 ConfuserEx https://gi…

代码危机!如何利用自定义异常应对复杂业务逻辑

大家好&#xff0c;我是小米&#xff0c;在这篇文章中&#xff0c;我将和大家分享关于自定义异常的使用场景以及一个实际的电商项目案例。自定义异常在软件开发中起到了重要的作用&#xff0c;能够帮助我们更好地管理和处理各种异常情况。让我们一起来看看各个场景下如何使用自…

85.建立主体页面-第一部分

记住我们之前画的草图&#xff0c;根据我们的草图来构建初始的页面 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta n…

202313读书笔记|《山居七年》——我只想在广袤璀璨的星河里享受生的鲜活,独自飞,游走

202313读书笔记|《山居七年》——我只想在广袤璀璨的星河里享受生的鲜活&#xff0c;独自飞&#xff0c;游走 《山居七年》 作者张二冬&#xff0c;选择隐士山居是一种很自由随性的生活态度&#xff0c;我觉得这不是普通人可以拥有的&#xff0c;比如我&#xff0c;并未入世也…

Nginx学习1--介绍和安装

文章目录 官方网站常用功能核心组成下载安装源码安装linux包安装ubuntu安装docker安装 官方网站 http://nginx.org/ 官方文档 常用功能 静态资源部署处理静态文件、处理索引文件以及支持自动索引&#xff1b; Rewrite地址重写正则表达式 反向代理提供反向代理服务器&#xf…

大数据:配置云服务器,主机名映射和SSH免密登录,创建Hadoop用户,复制免密登录

大数据&#xff1a;配置云服务器&#xff0c;主机名映射和SSH免密登录&#xff0c;创建Hadoop用户&#xff0c;复制免密登录 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 …

十、Camera 启动流程分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、Camera 启动流程概览二、Launcher 跟Camera APP 交互三、Camera app 与FWK Camera Service 交互四、Camera FWK 与 Camera HAL 交互五、Camera FWK…

35岁,阿里6年,被打了低潜,已生无可恋,纠结该不该出去,40岁的人能不能给点建议?...

35岁是个转折点&#xff0c;许多人都在35岁时陷入迷茫&#xff0c;比如下面这位网友&#xff1a; 35岁&#xff0c;在阿里已经6年了&#xff0c;现在要混是可以混下去&#xff0c;但发展肯定是没有了&#xff0c;已经被老板打了低潜。目前被这个业务摩擦得已经生无可恋&#xf…

APP开发中的UI设计

UI设计是 APP开发中一个必不可少的部分&#xff0c;用户体验也是影响 APP产品成功与否的重要因素&#xff0c;用户体验包括用户的使用感受和操作感受。就 UI设计来说&#xff0c;它的主要内容有&#xff1a;界面、图标、颜色、字体、布局、页面布局等。 一个好的 UI设计可以增加…

通过extundelete实现CentOS6 ext4文件系统误删除文件的恢复

1.介质下载 路径&#xff1a; 链接&#xff1a;extundelete 提取码&#xff1a;ztj0 版本&#xff1a;extundelete.0.2.4 2.实验环境 1.CentOS6.8 系统版本命令&#xff1a; cat /etc/redhat-release 2.磁盘&#xff1a;/dev/sdb 磁盘查看命令&#xff1a; lsblk |g…

mysql数据备份-主从同步恢复

【1】数据库备份和恢复 1、xtrabackup-物理备份 全量备份主库数据&#xff1a;xtrabackup --backup --userroot --passwordxxx --port3306 --target-dir/comProject/backup/db/full_20220831 将full_20220831文件上传到从库服务器上 关闭存库服务&#xff1a;service mysqld …

将一维数组作为一列合并到二维数组中的numpy.column_stack()方法

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将一维数组作为一列合并到二维数组中 numpy.column_stack() 选择题 关于以下代码说法错误的一项是? import numpy as np a np.array([0,0]) b np.array([[1,2],[3,4]]) print("【显示…

C++ 线性数据结构系列之低调而强大的单调栈

1. 前言 单调栈是在栈基础上进行变化后的数据结构。除了遵循栈的先进后出的存储理念&#xff0c;在存储过程中还需保持栈中数据的有序性。 根据栈中数据排序的不同&#xff0c;单调栈分为&#xff1a; 单调递增栈&#xff1a;从栈顶部向栈的底部&#xff0c;数据呈递增排序。…

【JY】ABAQUS正交各向异性弹性本构模型

写在前文 材料的线弹性本构模型能够很好的描述处于工作荷载水平下的材料性能情况&#xff0c;后续材料的塑性理论也需要在弹性本构模型的基础上进行开展。由于砌体结构所采用的砌体材料具有明显的正交各项异性&#xff0c;故先从正交各向异性弹性入手&#xff0c;根据弹性理论中…

Java基础篇 | Java开发环境的搭建

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色…