Stack详解(含动画演示)

news2025/1/23 2:22:39

目录

  • Stack详解
    • 1、栈数据结构动画演示
    • 2、Stack的继承体系
    • 3、Stack的push (入栈)方法
    • 4、Stack的pop (出栈)方法
    • 5、Stack是如何利用Vector实现栈数据结构的?
    • 6、自己实现栈(不借助JDK提供的集合)
    • 7、自己实现栈(借助JDK提供的集合)
      • 利用 ArrayDeque 实现高性能的非线程安全的栈。
      • 利用 ConcurrentLinkedDeque实现高性能的线程安全的栈。
    • 8、栈的应用场景
      • ①、浏览器的前进、后退功能实现
      • ②、表达式求值 (实现简单计算器功能)
      • ③、语法解析
      • ④、栈排序
      • ⑤、实现特殊算法

Stack详解

基于JDK8

1、栈数据结构动画演示

在这里插入图片描述

从动画中可以看出,栈这种数据结构 只能从同一头进,从同一头出。
比较正式的说法叫: 后进先出(LIFO, Last In First Out)。

2、Stack的继承体系

public class Stack<E> extends Vector<E>

在这里插入图片描述
可以看到JDK提供的 Stack 是利用 Vector实现的。所以Stack 类也是线程安全的。

3、Stack的push (入栈)方法

public E push(E item) {
    // 调用 addElement 方法将 item 添加到 Vector 的末尾
    addElement(item);

    // 返回被添加的元素 item
    return item;
}
public synchronized void addElement(E obj) {
    // 修改计数器增加,用于记录 Vector 被修改的次数
    modCount++;

    // 确保 Vector 有足够的容量来容纳新元素
    ensureCapacityHelper(elementCount + 1);

    // 将新元素添加到 elementData 数组的 elementCount 位置
    elementData[elementCount++] = obj;
}

private void ensureCapacityHelper(int minCapacity) {
    // 检查是否需要扩容,如果当前容量小于 minCapacity 则进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}


protected Object[] elementData;

private void grow(int minCapacity) {
    // 当前数组的容量
    int oldCapacity = elementData.length;

    // 新的容量,如果 capacityIncrement 大于 0 则按此增量扩容,否则容量翻倍
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);

    // 确保新的容量不小于最小所需的容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;

    // 检查新的容量是否超过最大允许的数组大小
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // 将元素数组扩容到新的容量
    elementData = Arrays.copyOf(elementData, newCapacity);
}

上面只有push方法是Stack提供的,其余方法都是Vector类提供的。

4、Stack的pop (出栈)方法

public synchronized E pop() {
    E obj;
    int len = size();

    // 获取栈顶元素
    obj = peek();

    // 移除栈顶元素
    removeElementAt(len - 1);

    // 返回被移除的栈顶元素
    return obj;
}

public synchronized E peek() {
    int len = size();

    // 如果栈为空,抛出 EmptyStackException 异常
    if (len == 0)
        throw new EmptyStackException();

    // 返回栈顶元素(位于 elementData 数组的最后一个有效位置)
    return elementAt(len - 1);
}

public synchronized void removeElementAt(int index) {
    // 增加修改计数器
    modCount++;

    // 如果索引超出当前元素数量,抛出 ArrayIndexOutOfBoundsException 异常
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
    }
    // 如果索引为负数,抛出 ArrayIndexOutOfBoundsException 异常
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }

    // 计算需要移动的元素个数
    int j = elementCount - index - 1;

    // 如果有需要移动的元素
    if (j > 0) {
        // 将 elementData 数组中 index + 1 到 elementCount - 1 的元素左移一位
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }

    // 减少元素数量
    elementCount--;

    // 将最后一个元素设为 null 以便垃圾回收
    elementData[elementCount] = null;
}

pop (出栈)方法利用到了peek(获取栈顶元素)方法。

5、Stack是如何利用Vector实现栈数据结构的?

上面已经用动画演示了栈数据结构的特点:栈这种数据结构 只能从同一头进,从同一头出。

栈的主要操作包括以下几种:

  • 压栈(push):将元素添加到栈的顶部。
  • 出栈(pop):移除并返回栈顶元素。
  • 查看栈顶元素(peek):返回栈顶元素但不移除它。

Stack 类通过继承 Vector,并添加自己的方法,实现了这些操作。
push 方法
push 方法将元素压入栈顶。它实际上调用了 Vector 的 addElement 方法,将元素添加到 Vector 的末尾

pop 方法
pop 方法移除并返回栈顶元素。它先调用 peek 方法获取栈顶元素,然后调用 removeElementAt 方法移除该元素。

peek 方法
peek 方法返回栈顶元素但不移除它。它直接访问 Vector 的最后一个元素。

Vector和ArrayList类似,底层都是使用Object数组来存储元素的。Stack通过操作数组的末尾,比如push只把元素添加到数组的末尾,pop只移除数组末尾的元素。

在这里插入图片描述

6、自己实现栈(不借助JDK提供的集合)

主要利用Object 数组和Arrays.copyOf方法

class MyStack<T>{

    // 默认容量
    private final Integer defaultCapacity = 10;
    // 存储元素的数组
    private Object[] elementData;
    // 真正存储的元素个数
    private int elementCount;

    public MyStack() {
        this.elementData = new Object[10];
    }


    public void push(T element){
        // 元素个数大于默认容量就复制数组
        if(gtDefaultCapacity(elementCount + 1)){
            // 创建一个新的数组,长度为当前数组长度加1
            Object[] newElements = Arrays.copyOf(elementData, elementCount + 1);
            newElements[elementCount++] = element;
            elementData = newElements;
        }else {
            // 否则就直接添加元素到数组末尾
            this.elementData[elementCount++] = element;
        }
    }

    // 获取栈顶元素
    @SuppressWarnings(value = "unchecked")
    public T peek(){
        if(elementCount<=0){
            throw new RuntimeException("当前栈为空!");
        }
        return (T)elementData[elementCount-1];
    }

    // 删除栈顶元素
    @SuppressWarnings(value = "unchecked")
    public T pop(){
        if(elementCount<=0){
            throw new RuntimeException("当前栈为空!");
        }
        Object temp = elementData[elementCount - 1];
        elementData = Arrays.copyOf(elementData, elementCount - 1);
        elementCount--;
        return (T) temp;
    }


    // 判断是否大于默认容量
    private boolean gtDefaultCapacity(int length){
        return length > defaultCapacity;
    }

    // 获取栈大小
    public int size(){
        return elementCount;
    }

    // 判断栈是否为空
    public boolean isEmpty(){
        return elementCount <= 0;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        for (int i =elementCount - 1 ; i >= 0; i--) {
            builder.append(elementData[i].toString()).append("  ");
        }
        return builder.toString();
    }

}

测试:

public static void main(String[] args) throws Exception {
        MyStack<String> myStack = new MyStack<>();
        myStack.push("秀逗");
        myStack.push("四眼");
        myStack.push("大黄");

        System.out.println(myStack.peek());  //  大黄
        System.out.println(myStack.pop());  // 大黄
        System.out.println(myStack.size()); // 2
        System.out.println(myStack.isEmpty()); // false
		System.out.println(myStack);  // 四眼  秀逗

        System.out.println(myStack.pop()); //  四眼  
        System.out.println(myStack.pop());//  秀逗 
        System.out.println(myStack.pop()); // java.lang.RuntimeException: 当前栈为空!
       
    }

我们利用Object 数组和Arrays.copyOf方法实现了一个简单的栈,这个栈包含基本的 push、peek、pop、size、isEmpty方法,并模拟出栈顺序重写了 toString。

7、自己实现栈(借助JDK提供的集合)

上面我们自己利用Object 数组和Arrays.copyOf方法实现了简单的栈,我们默认设置了10的容量,如果超过10个元素,我们利用Arrays.copyOf完成数组的复制。 这样做是非常耗费性能的,并且我们自己实现的栈是非线程安全的。
下面我们利用JDK现有的集合来实现栈。

利用 ArrayDeque 实现高性能的非线程安全的栈。

class MyStack<T>{
	// 使用 ArrayDeque 作为底层数据结构来存储栈元素
    private ArrayDeque<T> arrayDeque;

	// 构造方法,初始化 ArrayDeque
    public MyStack() {
        this.arrayDeque = new ArrayDeque<T>();
    }
    
	// 将元素压入栈顶,使用 addFirst 方法将元素添加到双端队列的头部
    public void push(T elementData){
        arrayDeque.addFirst(elementData);
    }


    public void peek(){
        arrayDeque.getFirst();
    }

    public T pop(){
       return arrayDeque.removeFirst();
    }

    public int size(){
        return arrayDeque.size();
    }

    public boolean isEmpty(){
        return arrayDeque.isEmpty();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        // 这里不需要反序迭代  因为我们的push是往头部添加元素 直接正序迭代就是出栈顺序
        Iterator<T> iterator = arrayDeque.iterator();
        while (iterator.hasNext()){
            builder.append(iterator.next().toString()).append("  ");
        }
        return builder.toString();
    }
}

利用 ConcurrentLinkedDeque实现高性能的线程安全的栈。

class MyStack<T>{

    private ConcurrentLinkedDeque<T> concurrentLinkedDeque;

    public MyStack() {
        this.concurrentLinkedDeque = new ConcurrentLinkedDeque<T>();
    }

    public void push(T elementData){
        concurrentLinkedDeque.addFirst(elementData);
    }

    public void peek(){
        concurrentLinkedDeque.getFirst();
    }

    public T pop(){
       return concurrentLinkedDeque.removeFirst();
    }

    public int size(){
        return concurrentLinkedDeque.size();
    }

    public boolean isEmpty(){
        return concurrentLinkedDeque.isEmpty();
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        // 这里不需要反序迭代  因为我们的push是往头部添加元素 直接正序迭代就是出栈顺序
        Iterator<T> iterator = concurrentLinkedDeque.iterator();
        while (iterator.hasNext()){
            builder.append(iterator.next().toString()).append("  ");
        }
        return builder.toString();
    }
}

ConcurrentLinkedDeque 的高性能线程安全设计,涉及到乐观并发控制策略比如CAS,延迟回收,分段锁等机制。
后面总结并发编程知识的时候再详细分析下ConcurrentLinkedDeque 的设计。

8、栈的应用场景

栈虽然是一种后进先出(LIFO, Last In First Out)的数据结构,但是却有广泛的应用场景。
下面是几个常见的例子:

①、浏览器的前进、后退功能实现

在这里插入图片描述
可以用两个栈分别存储前进和后退的页面
代码示例:

import java.util.Stack;

public class TestA {
    public static void main(String[] args) {
        Browser browser = new Browser();
        browser.visit("百度");
        browser.visit("谷歌");

        browser.back();  //  百度
        browser.forward();  // 谷歌
    }
}


class Browser {
    // 保存前进页面的栈,用于实现前进功能
    private Stack<String> forwardStack;
    // 保存后退页面的栈,用于实现后退功能
    private Stack<String> backStack;
    // 当前浏览的页面
    private String currentPage;

    // 初始化前进栈、后退栈和当前页面
    public Browser() {
        this.forwardStack = new Stack<>();
        this.backStack = new Stack<>();
        // 初始化默认页面为 "Home"
        this.currentPage = "Home";
    }

    /**
     * 访问新页面
     * @param page 新访问的页面
     */
    public void visit(String page) {
        if (page == null) {
            throw new RuntimeException("页面不能为空");
        }
        // 将当前页面加入后退栈,因为我们将访问新的页面
        backStack.push(currentPage);
        // 更新当前页面为新访问的页面
        currentPage = page;
        // 打印当前浏览页面
        System.out.println("当前浏览页面:" + currentPage);
        // 清空前进栈,因为访问新页面时前进栈失效
        forwardStack.clear();
    }

    /**
     * 前进到下一页面
     */
    public void forward() {
        System.out.println("前进");
        // 如果前进栈不为空
        if (!forwardStack.isEmpty()) {
            // 将当前页面加入后退栈
            backStack.push(currentPage);
            // 从前进栈弹出页面作为当前页面
            currentPage = forwardStack.pop();
            // 打印当前浏览页面
            System.out.println("当前浏览页面:" + currentPage);
        } else {
            // 前进栈为空,无法前进
            System.out.println("当前无前进页面");
        }
    }

    /**
     * 后退到上一页面
     */
    public void back() {
        System.out.println("后退");
        // 如果后退栈不为空
        if (!backStack.isEmpty()) {
            // 将当前页面加入前进栈
            forwardStack.push(currentPage);
            // 从后退栈弹出页面作为当前页面
            currentPage = backStack.pop();
            // 打印当前浏览页面
            System.out.println("当前浏览页面:" + currentPage);
        } else {
            // 后退栈为空,无法后退
            System.out.println("当前无后退页面");
        }
    }

    /**
     * 获取当前浏览的页面
     * @return 当前页面
     */
    public String getCurrentPage() {
        return currentPage;
    }
}

运行结果:

当前浏览页面:百度
当前浏览页面:谷歌
后退
当前浏览页面:百度
前进
当前浏览页面:谷歌

动画示例
在这里插入图片描述

②、表达式求值 (实现简单计算器功能)

栈常用于表达式求值,如中缀表达式转后缀表达式(逆波兰表达式)和逆波兰表达式求值。

先了解几个表达式的基本概念:
一般来说,数学表达式的表示方法主要有以下三种:

中缀表达式 (Infix Notation)
前缀表达式 (Prefix Notation) 又叫 波兰式 (Polish Notation)
后缀表达式 (Postfix Notation) 又叫 逆波兰式 (Reverse Polish Notation)

表达式类型示例直观性优先级标识计算机实现别名
中缀表达式(A + B) * C需要括号复杂
前缀表达式* + A B C不需要简单波兰式
后缀表达式A B + C *不需要简单逆波兰式

特点比较:

  • 中缀表达式 是我们日常使用的表达方式,运算符位于操作数之间,直观但需要括号来明确优先级,计算机实现较复杂。
  • 前缀表达式(波兰式)运算符位于操作数之前,不需要括号来确定优先级,适合树结构计算,计算机实现简单但不直观。
  • 后缀表达式(逆波兰式)运算符位于操作数之后,同样不需要括号来确定优先级,适合栈结构计算,计算机实现简单但不直观。

我们可以利用栈实现计算器功能 (实际上就是中缀表达式转后缀表达式,再利用栈实现后缀表达式的计算):

import java.util.Scanner;
import java.util.Stack;

public class TestA {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("请输入一个中缀表达式 (输入 'exit' 退出): ");
            String infixExpression = scanner.nextLine();

            if (infixExpression.equalsIgnoreCase("exit")) {
                break; // 输入 'exit' 退出循环
            }

            try {
                String postfixExpression = infixToPostfix(infixExpression);
                System.out.println("后缀表达式: " + postfixExpression);
                double result = evaluatePostfix(postfixExpression);
                if (result % 1 == 0) { // 判断结果是否为整数
                    System.out.println("计算结果: " + (int) result); // 整数结果去掉小数部分
                } else {
                    System.out.println("计算结果: " + result);
                }
            } catch (Exception e) {
                System.out.println("表达式无效: " + e.getMessage());
            }
        }
        scanner.close();
    }

    // 中缀表达式转后缀表达式
    public static String infixToPostfix(String infix) {
        Stack<Character> stack = new Stack<>();
        StringBuilder postfix = new StringBuilder();

        for (int i = 0; i < infix.length(); i++) {
            char ch = infix.charAt(i);

            if (Character.isDigit(ch)) {
                // 处理多位数字和小数
                while (i < infix.length() && (Character.isDigit(infix.charAt(i)) || infix.charAt(i) == '.')) {
                    postfix.append(infix.charAt(i++));
                }
                postfix.append(' ');
                i--; // 调整索引,以便下一次循环从正确位置开始
            } else if (ch == '(') {
                stack.push(ch);
            } else if (ch == ')') {
                // 将栈中的元素弹出直到遇到 '('
                while (!stack.isEmpty() && stack.peek() != '(') {
                    postfix.append(stack.pop()).append(' ');
                }
                stack.pop(); // 弹出 '('
            } else if (isOperator(ch)) {
                // 当栈不为空且当前运算符的优先级小于等于栈顶运算符的优先级时,弹出栈顶运算符并加入后缀表达式
                while (!stack.isEmpty() && precedence(stack.peek()) >= precedence(ch)) {
                    postfix.append(stack.pop()).append(' ');
                }
                stack.push(ch); // 将当前运算符入栈
            }
        }

        // 将栈中剩余的运算符全部加入后缀表达式
        while (!stack.isEmpty()) {
            postfix.append(stack.pop()).append(' ');
        }

        return postfix.toString().trim(); // 返回后缀表达式
    }

    // 计算后缀表达式
    public static double evaluatePostfix(String postfix) {
        Stack<Double> stack = new Stack<>();

        for (int i = 0; i < postfix.length(); i++) {
            char ch = postfix.charAt(i);

            if (Character.isDigit(ch)) {
                // 处理多位数字和小数
                StringBuilder number = new StringBuilder();
                while (i < postfix.length() && (Character.isDigit(postfix.charAt(i)) || postfix.charAt(i) == '.')) {
                    number.append(postfix.charAt(i++));
                }
                stack.push(Double.parseDouble(number.toString())); // 将数字入栈
                i--; // 调整索引,以便下一次循环从正确位置开始
            } else if (isOperator(ch)) {
                double b = stack.pop();
                double a = stack.pop();
                // 根据运算符进行计算并将结果入栈
                switch (ch) {
                    case '+': stack.push(a + b); break;
                    case '-': stack.push(a - b); break;
                    case '*': stack.push(a * b); break;
                    case '/': stack.push(a / b); break;
                }
            }
        }

        return stack.pop(); // 返回计算结果
    }

    // 判断是否是运算符
    public static boolean isOperator(char ch) {
        return ch == '+' || ch == '-' || ch == '*' || ch == '/';
    }

    // 返回运算符优先级
    public static int precedence(char ch) {
        switch (ch) {
            case '+':
            case '-': return 1;
            case '*':
            case '/': return 2;
        }
        return -1; // 如果不是运算符,则返回 -1
    }
}

输入:3*(1+2)
输出:

后缀表达式: 3 1 2 + *
计算结果: 9

栈在计算后缀表达式结果时的步骤:

  • 1.创建一个空栈,用于存储操作数。
  • 2.从左到右遍历后缀表达式的每个字符。
  • 3.如果当前字符是操作数(数字),则将其转换为对应的数值,并压入栈中。
  • 4.如果当前字符是运算符(+、-、*、/):
          从栈中弹出两个操作数,分别记为 a 和 b(注意:b 是栈顶元素,a 是次顶元素)。
          根据当前运算符进行计算:根据后缀表达式的特性,b 是操作符 a 的右操作数,a 是左操作数。
  • 5.将计算结果压入栈中。
  • 6.遍历完后缀表达式后,栈中剩余的唯一元素即为最终的计算结果。

计算过程动画演示:
在这里插入图片描述

③、语法解析

括号匹配

import java.util.Stack;

public class TestA {

    public static void main(String[] args) {
        String s = "{[(1234)]}";
        // 调用isValid方法并输出结果,应该输出true,因为输入的括号字符串是有效的
        System.out.println(isValid(s)); // 输出: true
    }

    // 判断括号字符串是否有效的方法
    public static boolean isValid(String s) {
        // 创建一个栈来存放左括号
        Stack<Character> stack = new Stack<>();

        // 遍历输入的字符串中的每一个字符
        for (char c : s.toCharArray()) {
            // 如果是左括号,则压入栈中
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else if (c == ')' || c == '}' || c == ']') { // 只处理右括号
                // 如果是右括号且栈为空,说明没有匹配的左括号,返回false
                if (stack.isEmpty()) return false;

                // 弹出栈顶的左括号
                char top = stack.pop();
                // 判断栈顶的左括号和当前的右括号是否匹配,如果不匹配,返回false
                if (c == ')' && top != '(' || c == '}' && top != '{' || c == ']' && top != '[') {
                    return false;
                }
            }
        }

        // 如果栈为空,说明所有的括号都匹配,返回true;否则返回false
        return stack.isEmpty();
    }
}

④、栈排序

用一个栈对数据进行排序

import java.util.*;

public class TestA {

    public static void main(String[] args) {
        // 创建一个LinkedList并添加一些整数元素
        List<Integer> intCollection = new ArrayList<>();
        intCollection.add(5);
        intCollection.add(6);
        intCollection.add(3);
        intCollection.add(1);
        intCollection.add(2);
        intCollection.add(4);

        // 输出排序前的整数集合
        System.out.println("整数集合排序前: " + intCollection);
        // 调用sortCollection方法对集合进行排序
        Collection<Integer> sortedIntCollection = sortCollection(intCollection);
        // 输出排序后的整数集合
        System.out.println("整数集合排序后: " + sortedIntCollection);

    }

    // 对集合进行排序的方法,使用泛型并要求元素实现Comparable接口
    public static <T extends Comparable<T>> Collection<T> sortCollection(Collection<T> collection) {
        // 创建一个栈用于排序
        Stack<T> stack = new Stack<>();
        // 将集合中的元素压入栈中
        for (T element : collection) {
            stack.push(element);
        }

        // 创建一个临时栈用于辅助排序
        Stack<T> tempStack = new Stack<>();
        // 当输入栈不为空时进行排序
        while (!stack.isEmpty()) {
            // 弹出输入栈的栈顶元素
            T temp = stack.pop();
            // 将临时栈中所有大于temp的元素弹出并压入输入栈
            while (!tempStack.isEmpty() && tempStack.peek().compareTo(temp) < 0) {
                stack.push(tempStack.pop());
            }
            // 将temp压入临时栈
            tempStack.push(temp);
        }

        // 将临时栈中的元素全部弹出并存入一个新集合
        Collection<T> sortedCollection = new LinkedList<>();
        while (!tempStack.isEmpty()) {
            sortedCollection.add(tempStack.pop());
        }

        return sortedCollection;
    }
}

⑤、实现特殊算法

深度优先搜索(DFS)
二叉树的非递归前序遍历

回溯算法
例如求解迷宫问题、N皇后问题等。
算法类的暂时先不在这边篇文章内写了。后续会单独再写一些相关的内容。

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

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

相关文章

深入理解 Vue Router 及其 `router` 和 `route` 变量

深入理解 Vue Router 及其 router 和 route 变量 在使用 Vue.js 进行单页面应用开发时&#xff0c;Vue Router 是一个不可或缺的工具。它使得我们可以轻松地管理应用中的路由&#xff0c;提供了流畅的用户体验。然而&#xff0c;在实际开发中&#xff0c;许多开发者可能会混淆…

在自己的电脑上搭建我的世界Java版服务器

很多朋友&#xff0c;喜欢玩Minecraft&#xff0c;也希望搭建一个服务器&#xff0c;用于和小伙伴联机&#xff1b; 并且&#xff0c;拥有服务器后&#xff0c;即使所有玩家都下线&#xff0c;“世界”依旧在运行&#xff0c;玩家可以随时参与其中&#xff0c;说不定一上线&am…

2. 音视频H264

视频软件基本流程 1.什么是H264 H.264是由ITU-T视频编码专家组&#xff08;VCEG&#xff09;和ISO/IEC动态图像专家组&#xff08;MPEG&#xff09;联合组成的联合视频组&#xff08;JVT&#xff0c;Joint Video Team&#xff09;提出的高度压缩数字视频编解码器标准 H265又名高…

我国含氢硅油产量逐渐增长 市场集中度较高

我国含氢硅油产量逐渐增长 市场集中度较高 含氢硅油又称为烷基硅油&#xff0c;是一种有机硅化合物&#xff0c;在常温常压下多表现为一种无色透明液体。与其他类型的硅油相比&#xff0c;含氢硅油具有良好的滋润性、疏水性、润滑性、化学稳定性等优点。经过多年发展&#xff…

【C语言】10.C语言指针(5)

文章目录 1.sizeof和strlen的对比1.1 sizeof1.2 strlen1.3 sizeof 和 strlen的对⽐ 2.数组和指针笔试题解析2.1 ⼀维数组2.2 字符数组2.3 ⼆维数组 3.指针运算笔试题解析3.1 题⽬13.2 题⽬23.3 题⽬33.4 题⽬43.5 题⽬53.6 题⽬63.7 题⽬7 1.sizeof和strlen的对比 1.1 sizeof …

正则表达式:从左到右一个个去匹配,api帮助文档搜:Pattern

正则匹配字符&#xff1a; \:在java里面是转义字符的意思&#xff0c;在java里面可以理解为&#xff1a;\\\ “你”匹配一个&#xff1a;. a匹配一个&#xff1a;. 匹配多个的情况&#xff1a; 正则表达式练习&#xff1a; 忽略大小写的书写方式&#xff1a;(?i) 正则表达式在…

【仿真建模-anylogic】Dynamic Event原理解析

Author&#xff1a;赵志乾 Date&#xff1a;2024-06-12 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 类图 2. 原理解析 EventOriginator是Anylogic中各类事件的父类&#xff0c;对外暴露的接口主要有: 函数功能boolean isActive()判定…

泛微OA E9 浏览框显示的数据根据表单字段过滤

一、实现效果&#xff1a;如图所示&#xff0c;字段“物品名称”浏览框显示的数据根据“类型”字段进行过滤。 二、实现方法&#xff1a; 1、建模引擎-应用建模-浏览框-浏览框列表中单击“办公耗材”-“浏览框列表”-“操作”-“编辑” 2、sql语句中根据OA自带是示例增加where…

autoware lidar-centerpoint 点云在rviz上叠加显示问题

在使用自采数据包放入autoware中的lidar_centerpoint上进行检测时发现&#xff0c;在rviz可视化上出现问题&#xff1a;多帧点云在一个位置上不断叠加&#xff0c;不能正常随时间显示。 如下图所示&#xff1a; 解决方法&#xff1a; 出现上述问题是因为autoware默认使用的是…

多用户竞拍商城系统 挂售转卖竞拍商城系统源码 竞拍系统 竞拍系统开发定制 转拍闪拍系统 后端PHP+前端UNIAPP源码+教程

挂售转卖竞拍商城系统源码/竞拍系统/转拍闪拍系统/后端PHP前端UNIAPP源码 玩法简介 ①、后台可添加商品进行挂单 ②、后台设置场次以及场次开始时间 ③、用户抢单 ④、抢单以后可选择提货或者转售 ⑤、玩家寄售需按照后台设置百分比进行加价 ⑥、玩家寄售需支付手续费(余额支付…

图的存储表示

目录 概述 图的定义 图的存储结构 1&#xff09;邻接矩阵 2&#xff09;邻接表 3&#xff09;十字链表 4&#xff09;邻接多重表 概述 数据结构分为两类&#xff0c;一类是具有递归结构的数据结构&#xff0c;也就是可以给出一个递归定义的数据结构&#xff0c;一类是非递归结构…

你好!Python

目录 写在前面 编辑推荐 内容简介 作者简介 前言 推荐理由 写在后面 写在前面 本期博主给大家推荐一本全新出版的Python图书&#xff0c;感兴趣的小伙伴一起来看看吧&#xff01; 《你好&#xff01;Python 全彩印刷 从零开始学 语言轻松幽默 版式精美 视频讲解 提供代…

成功的管理者必做的10件事

管理者是团队的核心&#xff0c;他们不仅要有出色的领导能力&#xff0c;还要具备应对各种挑战的智慧和勇气。以下是成功的管理者必做的10件事&#xff1a; 1、设定明确的目标 管理者要为团队设定清晰、可衡量且具有挑战性的目标&#xff0c;这些目标不仅与组织的长期愿景相…

高考志愿填报,如何分析自己的优势特长?

据不完全统计&#xff0c;80%的高三学生&#xff0c;不清楚自己要报什么专业&#xff0c;以及将来从事哪种职业&#xff1f;有的人则是完全听从父母的安排&#xff0c;有人听老师的建议&#xff0c;有人追热门&#xff0c;有人查什么专业可以拿高薪.... 而现实就是&#xff1a…

水产养殖监测站的工作原理

TH-LSZ06水产养殖监测站是保障水产质量安全的重要设施&#xff0c;监测水产养殖环境&#xff1a;负责监测水产养殖基地的水质、底泥、养殖物质等&#xff0c;确保养殖环境的适宜性和安全性。通过对养殖环境的实时监测&#xff0c;可以及时发现和预警水产疾病和污染问题&#xf…

pdf文件怎么改变大小?在线快速压缩pdf的方法

pdf作为一种常用的文件格式&#xff0c;使用这种文件类型的好处在于不仅拥有更好的兼容性&#xff0c;还可以设置密码来保证安全性&#xff0c;防止未授权用户查看内容&#xff0c;所以现在导出文件展示都会采用这种格式的来做内容展示。当遇到pdf文件过大问题时&#xff0c;想…

Unity | Tilemap系统

目录 一、准备工作 1.插件导入 2.资源导入 二、相关组件介绍 1.Grid组件 2.Tilemap组件 3.Tile 4.Tile Palette 5.Brushes 三、动态创建地图 四、其他功能 1.移动网格上物体 2.拖拽缩放地图 Unity Tilemap系统为2D游戏开发提供了一个直观且功能强大的平台&#xff…

知网学术期刊《数学之友》投稿难度大吗?

知网学术期刊《数学之友》投稿难度的大小&#xff0c;实际上取决于多个因素的综合考量&#xff0c;包括论文的质量、研究方向的匹配度、期刊的审稿标准以及投稿者的学术背景等。 首先&#xff0c;从期刊的定位和特点来看&#xff0c;《数学之友》作为一份专注于数学领域的学术期…

利用R包“Phenotype”对表型值进行检查

首先&#xff0c;你需要确保你已经安装了R和RStudio&#xff08;如果你想用RStudio的话&#xff09;。然后&#xff0c;你可以按照以下步骤进行操作&#xff1a; 加载数据&#xff1a;首先&#xff0c;你需要加载你的表型数据。如果你的数据是以CSV、Excel等格式保存的&#x…

vue2中如何动态渲染组件

vue2中如何动态渲染组件 动态渲染组件代码解读通过函数调用渲染组件 封装一个函数调用的二次确认弹窗如何让外部知道用户点击了取消还是确定呢&#xff1f; 思考小结 vue2 的项目中&#xff0c;main.js 文件中有一个挂载 App.vue 组件的方法&#xff1a; new Vue({name: Root,…