揭开“栈和队列”的神秘面纱

 

前言

在线性表中不止有顺序表和链表,今天的主角就如标题所说--->认识栈和队列。把他们俩放一起总结是有原因的,还请看官听我娓娓道来~

 

什么是栈? 

栈(stack)是限定仅在表尾进行插入和删除操作的线性表

咱可以把栈理解成一个桶,栈底是固定的,放东西进去被称作“进栈”,拿东西被称作“出栈”, 所以栈也是按照后进先出的原则进行操作。

栈的基本方法有以下几种: 

 push()将元素入栈
 pop()将栈顶元素出栈并返回
 peek()获取栈顶元素
size()获取栈中有效元素个数
empty()检测栈是否为空
import java.util.Stack;

public class MyStack {
    public static void main(String[] args) {
        Stack<Integer> stack =new Stack<>();
        //push入栈
        stack.push(12);
        stack.push(23);
        stack.push(34);
        //peek获取栈顶元素,但不移除
        int ret=stack.peek();
        System.out.println(ret);
        //pop移除栈顶元素
        int ret1=stack.pop();
        System.out.println(ret1);//注意看这里就发生了变化
        System.out.println(stack.peek());
        //获取栈的长度
        System.out.println(stack.size());
        //判断栈空
        if(stack.empty()){
            System.out.println("栈已经空");
        }else{
            System.out.println("栈不为空");
        }

    }
}

运行结果如图所示,搭配画图食用效果更佳

栈的模拟实现 

不要忘记栈也是线性表,我们是可以用数组来实现栈的,那么同样也可以用usedSize来记录

 首先初始化好一个数组,和顺序表是类似的,不记得的可以先回忆一下-->http://t.csdnimg.cn/t0Rjd

import java.util.Arrays;
public class MyStack {
    int []array;
    int usedSize=0;

    public MyStack(int[] array, int usedSize) {
        this.array = array;
        this.usedSize = usedSize;
    }

入栈也是要看满没满,是否需要扩容,道理和模拟实现顺序表是一样的

    //push入栈
    public void push(int val) {
        if (isFull()) {
            int[] newArray = Arrays.copyOf(array, array.length * 2);
            array = newArray;
        }
        array[usedSize] = val;
        usedSize++;
    }
    public boolean isFull(){
        return usedSize==array.length;
    }
   //peek获取栈顶元素,但不移除
    public int peek(){
        if(Empty()){
            return -1;
        }
        return array[usedSize-1];
    }

要记住peek和pop的区别:peek只是获取栈顶元素,pop可是会移除的 

  //pop移除栈顶元素
    public int pop(){
        if(Empty()){
            return -1;
        }
        int oldval=array[usedSize-1];
        usedSize--;
        return oldval;
    }
    //获取栈的长度
    public int size(){
        return usedSize;
    }
    //判断栈空
   public boolean Empty(){
        return usedSize==0;
   }
}

最后加上测试类,运行结果如图~

栈的运用

根据它的特性---数据都是后进先出的,来看几道题练练手

1.括号匹配

括号匹配icon-default.png?t=N7T8https://leetcode.cn/problems/valid-parentheses/description/

思考:从题目中就可以看出括号必须类型是一样的,这样左括号和右括号才会匹配,既然如此,顺着走下去,在遍历的过程中咱们把所有类型的左括号全部入栈,遇到不是左边的,便开始出栈去匹配左右括号,如果最后全部对上,那么最后栈必定为空,返回true。

但是左右括号不匹配是有两种情况,一种是还没遍历完字符串时,遇到右括号,栈已经空了,比如:()))))))。还有一种就是遍历结束后栈内还有元素,那就是典型的不匹配了

class Solution {
     public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        if(s==null||s.length()==0){
            return true;
        }
        for (int i = 0; i <s.length() ; i++) {
            char s1=s.charAt(i);
            if(s1=='{'||s1=='['||s1=='('){
                stack.push(s1);
            }else{
                if(stack.empty()){//右括号多于左括号
                    return false;
                }
                    char s2 = stack.pop();//记录出栈的元素
                    if ((s2 == '{' && s1 != '}') || (s2 == '(' && s1 != ')') || (s2 == '[' && s1 != ']')) {
                        return false;
                    }
            }
        }
        return stack.empty();
    }
}

2.逆波兰表达式求值

逆波兰表达式求值icon-default.png?t=N7T8https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/

思考:观察实例1便能发现运算符写在操作数之后就是这个“逆波兰表达式”,据逆波兰表达式的特性,比如1+2,变成2 1 + ,栈便能解决,比如先将1入栈,2入栈,如果第三次入栈的是运算符号,则将前两次的出栈,然后进行实际的运算,将结果压入栈中。

所以整体符思路就出来了:

1.创建一个栈来存储操作数
2.遍历逆波兰表达式数组的每个元素
3.若当前元素为操作数,则将其压入栈中
4.若当前元素为运算符,则从栈中弹出两个操作数,执行相应的运算,并将结果压入栈中
5.遍历结束后,栈中仅剩下一个元素,即为整个表达式的计算结果

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack= new Stack<>();
        for(int i=0;i<tokens.length;i++){//遍历字符串
                  String temp=tokens[i];
            if(!isOperation(temp)){
                Integer val=Integer.valueOf(temp);//类型转化成整型
                stack.push(val); //不是运算符就入栈          }
         else{
                Integer val2 = stack.pop();
                Integer val1 = stack.pop();//否则弹出两个进行实际的运算
            switch(temp){//运算符进行匹配
                case "+":
                stack.push(val1+val2);
                break;

                case "-":
                stack.push(val1-val2);
                break;

                case "*":
                stack.push(val1*val2);
                break;

                case "/":
                stack.push(val1/val2);
                break;          
            }
        }
    }
    return stack.pop();
}
//遍历字符串的过程判断是否遇到运算符
public boolean isOperation(String s){
    if(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")){
        return true;
    }else{
        return false;
    }
}
}

3.栈的压入,弹出顺序匹配

栈的压入、弹出序列匹配icon-default.png?t=N7T8https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&&tqId=11174&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

思考:要判断出栈和入栈是否匹配,可以用上辅助栈,将 pushV 中的元素依次入辅助栈,在遍历 pushV 数组的过程中,判断popV数组对应的下标元素是否一样,一样的便出栈,popV遍历下一个,不一样的就pushV 指向下一个元素,直到栈顶元素不再与 popV 数组中对应位置的元素相等或者栈为空为止。最终,如果整个出栈过程符合规则,栈将会为空,此时返回 true;否则,返回 false,配合动图理解更好~

public boolean IsPopOrder (int[] pushV, int[] popV) {
        if (pushV == null || popV == null || pushV.length != popV.length) {
            return false;//排除一些常见情况
        }
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        for (int i = 0; i < pushV.length; i++) {
            stack.push(pushV[i]);入栈
            while (j < popV.length && !stack.empty()&&  popV[j] == stack.peek()) {
                stack.pop();//出栈
                j++;
            }
        }
        return stack.empty();
    }
}

4.模拟实现最小栈

最小栈icon-default.png?t=N7T8https://leetcode.cn/problems/min-stack/description/

思路:这道题需要实现最小栈,那这道题单靠一个栈,不太好实现,那我们就定义一个辅助栈,把这个辅助栈当做最小栈,

1.在push 的过程中,如果都是空栈,两个栈都入,如果不是,那显而易见只有最小的才会被存进辅助栈中,于是val值和辅助栈的栈顶比较,只有更小的元素才会存进辅助栈

2.在出栈的过程中,两个栈都要出,只要保证出的是一样的元素即可

3.在获取栈顶元素但不出栈的过程中,只要返回普通栈的即可,因为push的过程中,元素可能不会入辅助栈

4.在获取最小元素的过程中,这是便可直接返回辅助栈的栈顶元素,这也是设置辅助的原因。

配合图片和代码理解效果更佳哦~

class MinStack {
    public Stack<Integer> stack;
    public Stack<Integer> minstack;

    public MinStack() {
        stack = new Stack<>();
        minstack = new Stack<>();

    }

    public void push(int val) {
        stack.push(val);
        if (minstack.empty()) {//第一次入栈
            minstack.push(val);
        }else{
        Integer peekval = minstack.peek();
        if (val <= peekval) {//只有更小才入栈
            minstack.push(val);
        }
    }
    }

    public void pop() {
        if (stack.empty()) {
            return;
        }
            int popVal = stack.pop();
            if (popVal == minstack.peek()) {//只要移除的元素和辅助栈的栈顶元素一致就能删
                minstack.pop();
            }
        }
    

    public int top() {
        if (minstack.empty()) {
            return -1;
        } else {
            return stack.peek();
        }

    }

    public int getMin() {
        if (minstack.empty()) {
            return -1;
        } else {
            return minstack.peek();
        }
    }
}

什么是队列?

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表

咱可以把它理解成排队打饭的过程,进行排队(插入)操作的一端称为队尾, 进行打完饭走人(删除)操作的一端称为队头 ,配合下图就好理解多了~

队列的基本方法有以下几点:

offer()入队
poll()出队并返回出队元素——>就是队头
peek()获取对头元素
size()获取队列的元素个数
isEmpty()判断队列是否为空

咱们上机试试:

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

public class MyQueue {
    public static void main(String[] args) {
        Queue<Integer> queue=new LinkedList<>();
        //入队列
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);
        int ret=queue.peek();//获取对头元素
        System.out.println(ret);
        System.out.println(queue.size());//获取队列元素个数
        System.out.println(queue.isEmpty());//判断队列是否为空
        System.out.println(queue.poll());//出队列

    }
}

搭配图片就更好理解了:

队列的模拟实现

这里用到的是链式结构去模拟实现,要注意,看下图理解更好~

先定义好一个双向链表


    class ListNode{
        public int val; // 节点的值
        public ListNode prev; // 前驱节点
        public ListNode next; // 后继节点

        // 节点构造函数
        public ListNode(int val){
            this.val=val;
        }
    }

    public ListNode head; // 头结点
    public ListNode last; // 尾结点

offer()入队----类似尾插

public void offer(int val){//尾插
    ListNode node=new ListNode(val); 
    if(head==null){ 
        head=last=node; // 将新节点作为唯一节点,即头结点和尾结点都指向它
    }else {
        last.next = node; // 将尾结点的后继节点指向新节点
        node.prev = last; // 将新节点的前驱节点指向原尾结点
        last = last.next; // 更新尾结点为新节点
    }
}

poll()出队----类似头删

public int poll(){
        if(head==null){
            return -1; 
        }
        int ret=head.val; 
        if(head.next==null){ // 如果头结点后面没有节点,即队列只有一个节点
            head=null; // 此时就是清空队列
        }else {
            head=head.next; // 更新头结点为原头结点的后继节点
            head.prev=null; // 清空新头结点的前驱节点
        }
        return ret; 
    }

peek()-----返回头结点的值即可

public int peek(){
        if(head==null){ 
            return -1;
        }
        return head.val; // 返回头结点的值
    }

isEmpty()----看头结点空不空

public boolean isEmpty(){
        return head==null; // 头结点为空表示队列为空
    }

最后加上一个测试类,看运行结果就模拟实现成功了~

循环队列

那队列用顺序结构实现是什么样呢?-------->就是循环队列

看上面的图解是不是理解了很多?就是卷起来~这样的话就能循环利用数组空间了

那接下来就用数组来模拟实现它吧!

设计循环队列icon-default.png?t=N7T8https://leetcode.cn/problems/design-circular-queue/description/

思考:既然是循环,首尾如何连接呢?这个又是数组,不能像链表一样去改变指向,这时就有一个公式:first=(first+1)%len;这样的走向是远远好于first++的,这样就不用考虑越界的问题了

于是先用数组去定义这个循环队列,定义好首和尾

public class MyCircularQueue {
    public int[] elem;
    public int first;
    public int last;

    public MyCircularQueue(int k) {
        elem = new int[k];
    }

 入队和出队原理是一样的,要考虑循环,所以不能直接++;

    public boolean enQueue(int value) {//入队
        if (isFull()) {
            return false;
        }
        elem[last] = value;//不为空直接入队
        last = (last + 1) % elem.length;//更新队尾指针,考虑循环情况
        return true;
    }

    public boolean deQueue() {//出队
        if (isEmpty()) {
            return false;
        }
        first = (first + 1) % elem.length;//出队就是first移动,道理是一样的
        return true;
    }

 队头元素直接获取即可,队尾就要注意了,一般情况下,直接获取下标是last-1的元素即可,因为这里浪费了一个空间,好区分空和满,但如果last刚好指向0,数组下标是没有-1的,所以这里要做判断,如果是last==0,返回的其实是最后一个元素,下标就是len-1的元素

  public int Front() {//获取队头元素
        if (isEmpty()) {
            return -1;
        }
        return elem[first];
    }

    public int Rear() {//获取队尾元素
        if (isEmpty()) {
            return -1;
        }
        int index = last == 0 ? //要考虑last是否是0
              elem.length - 1 : last - 1;
        return elem[index];
    }

空和满是首先考虑的问题,以浪费一个空间为代价进行区分

// 判断队列是否为空
    public boolean isEmpty() {
        return first == last; // 队头指针与队尾指针相等时,队列为空
    }

    // 判断队列是否满
    public boolean isFull() {
        return (last + 1) % elem.length == first; // 队尾的下一个是队头时,队列为满
    }

双端队列

直接上图理解效果

双端队列(deque)是一种具有两端插入和删除操作的数据结构,即可以在队列的两端进行插入和删除元素。这种数据结构可以看作是既具备栈的特点,又具备队列的特点

所以双端队列所具备的方法也是二者兼备,可以用链式结构或顺序结构实现都可以哦~

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

使用 ArrayDeque作为线性实现: 

import java.util.Deque;
import java.util.ArrayDeque;

public class Main {
    public static void main(String[] args) {
        Deque<Integer> stack = new ArrayDeque<>();

        stack.push(1); 
        stack.push(2);
        stack.push(3); 

        System.out.println(stack.pop()); // 从栈顶弹出元素
        System.out.println(stack.peek()); //查看栈顶元素但不弹出
    }
}

使用 LinkedList 作为链式实现: 

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

public class Main {
    public static void main(String[] args) {
        Deque<Integer> queue = new LinkedList<>();
        // 在队尾添加元素
        queue.addLast(1); 
        queue.addLast(2); 
        queue.addLast(3); 

        System.out.println(queue.removeFirst()); //从队头移除元素
        System.out.println(queue.peekFirst()); //查看队头元素但不移除
    }
}

栈和队列的相互转化

无论是如何转化,单靠一个是肯定不行的,所以肯定要有辅助栈或队列的

1.用队列模拟实现栈

用队列实现栈icon-default.png?t=N7T8https://leetcode.cn/problems/implement-stack-using-queues/description/

思考:这道题是要咱用队列去模拟实现栈的功能,单单一个队列肯定不行,那就两个。一个做存储元素,一个来辅助。

class MyStack {
    public Queue<Integer> queue1; // 第一个队列
    public Queue<Integer> queue2; // 第二个队列
    
    public MyStack() {
        queue1 = new LinkedList<>(); // 初始化第一个队列
        queue2 = new LinkedList<>(); // 初始化第二个队列
    }

首先入栈push(),如果两个队列都是空,直接默认放入存储元素的队列即可,而在一般情况下,谁不为空就放谁那里,毕竟不为空就意味着有元素已经放入,直接入即可。

// 将元素压入栈顶
    public void push(int x) {
        if(empty()){ // 如果栈为空,直接将元素加入第一个队列
            queue1.offer(x);
        }
        else if (queue2.isEmpty()) { // 如果第二个队列为空,将元素加入第一个队列
            queue1.offer(x);
        } else { // 否则将元素加入第二个队列
            queue2.offer(x);
        }
    }

重点看出栈pop(),栈的特性是“先进后出,用队列去模拟实现这个功能,对于要出栈的元素,要把栈中除了最后元素以外的所有元素都弹出到辅助栈中去接收,这段详细解释下~

因为栈出栈只能是栈顶元素,而队列的特性先进先出,于是栈顶元素以下的元素都可以弹出到辅助队列中去,原队列中剩下的最后一个元素便是栈顶元素

当然两个队列都要考虑,哪个是存储元素的,哪个又是辅助的。 

// 弹出栈顶元素并返回
    public int pop() {
        if (empty()) {
            return -1; // 如果栈为空,返回-1
        }
        if (!queue1.isEmpty()) {
            int size = queue1.size();
            for (int i = 0; i < size - 1; i++) { // 将 queue1 中除最后一个元素外的其他元素转移到 queue2 中
                queue2.offer(queue1.poll());
            }
            return queue1.poll(); // 返回 queue1 中最后一个元素,即为栈顶元素
        } else {
            int size = queue2.size();
            for (int i = 0; i < size - 1; i++) { // 将 queue2 中除最后一个元素外的其他元素转移到 queue1 中
                queue1.offer(queue2.poll());
            }
            return queue2.poll(); // 返回 queue2 中最后一个元素,即为栈顶元素
        }
    }

然后是top(),和pop()很类似,但并不移除,道理是一样的,只不过要把所有元素都弹出到辅助队列中去,于是要定义一个变量去存储最后一次弹出元素,那就是栈顶元素

// 返回栈顶元素但不弹出
    public int top() {
        if(empty()){
            return -1; // 如果栈为空,返回-1
        }
        int ret = -1;
        if(!queue1.isEmpty()){
            int size = queue1.size();
            for (int i = 0; i < size; i++) {
                ret = queue1.poll(); // 将 queue1 中的元素逐个转移到 queue2 中,并记录最后一个元素
                queue2.offer(ret);
            }
            return ret; // 返回 queue1 中的最后一个元素,即为栈顶元素
        } else {
            int size = queue2.size();
            for (int i = 0; i < size; i++) {
                ret = queue2.poll(); // 将 queue2 中的元素逐个转移到 queue1 中,并记录最后一个元素
                queue1.offer(ret);
            }
            return ret; // 返回 queue2 中的最后一个元素,即为栈顶元素
        }
    }

最后就是判断是否为空,这个最好写了,只要两个队列都是空,也就代表栈是空

public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
        }

2.用栈模拟实现队列

用栈实现队列icon-default.png?t=N7T8https://leetcode.cn/problems/implement-queue-using-stacks/description/

有了第一题的经验,这道题也好写了。模仿着来:

首先定义出两个队列,一个存储元素,一个进行辅助

class MyQueue {

    public Stack<Integer> stack1; // 栈1
    public Stack<Integer> stack2; // 栈2

    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

然后是入队offer(),这个直接入存储元素的栈就行,这里就默认是stack1,这里还设置将栈2 的元素倒入栈1,这是为了保证栈1一直都是存储元素的栈:

public void push(int x) {
while (!stack2.isEmpty()) { // 将栈2中的元素倒入栈1
            stack1.push(stack2.pop());
        }
        stack1.push(x); // 将新元素压入栈1
    }

出队poll():因为其先进先出的特性,用栈模拟的时候弹出就是栈底元素,这个好办,直接把栈1的所有元素都倒入栈2去,然后直接去弹出栈2的栈顶元素,这样就移除成功了,最后别忘了把栈2的元素倒回栈1去,配合图片理解效果更佳~

public int pop() {
        while (!stack1.isEmpty()) { // 将栈1中的元素倒入栈2
            stack2.push(stack1.pop());
        }
        return stack2.pop(); // 弹出栈2顶部元素
    }

返回对头元素peek():这个和poll是一样的,只不过用变量接受就好

public int peek() {
        while (!stack1.isEmpty()) { // 将栈1中的元素倒入栈2
            stack2.push(stack1.pop());
        }
        int peek = stack2.peek(); // 获取栈2顶部元素但不弹出
        while (!stack2.isEmpty()) { // 将栈2中的元素倒回栈1
            stack1.push(stack2.pop());
        }
        return peek;
    }

判断是否为空也是一样empty(),直接上代码~

public boolean empty() {
        return stack1.empty() && stack2.empty();
    }

这就是本篇文章的全部内容啦~如果觉得有收获的,希望可以点点赞支持下=V=!

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

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

相关文章

qt自定义窗口在拖动过程中出现抖动且拖动后位置看上去不对

自定义窗口拖动 引言开发环境关键性代码运行结果原因分析改进代码运行结果globalPos()globalPosition()再次修改代码运行结果区别 引言 本文旨在一个问题的记录&#xff1a;自定义窗口拖动的过程中&#xff0c;窗口不能很好的跟随鼠标移动&#xff0c;此外会出现窗口拖动时抖动…

C语言数据结构(11)——归并排序

欢迎来到博主的专栏C语言数据结构 博主ID&#xff1a;代码小豪 文章目录 归并排序两个有序数组的合并归并归并排序 归并排序的代码 归并排序 两个有序数组的合并 当前有两个有序数组arr1和arr2&#xff0c;我们创建一个可以容纳arr1和arr2同等元素个数的新数组arr。 让一个…

蓝桥杯 经验技巧篇

1. 注意事项 &#x1f468;‍&#x1f3eb; 官方通知 &#x1f468;‍&#x1f3eb; 资料文档 时间&#xff1a;4月13日 9:00~13:00 &#xff08;时长 4小时&#xff09;物品 准考证&#xff08;赛前一周开放下载&#xff0c;自行打印&#xff09;学生证身份证笔、水、外套&a…

DDIM,多样性与运行效率之间的trade off

DDPM的重大缺陷在于其在反向扩散的过程中需要逐步从 x t x_t xt​倒推到 x 0 x_0 x0​&#xff0c;因此其推理速度非常缓慢。相反&#xff0c;DDPM的训练过程是很快的&#xff0c;可以直接根据 x 0 x_0 x0​到 x t x_t xt​添加的高斯噪声 ϵ \epsilon ϵ完成一次训练。 为了解…

springboot整合ShardingSphere分库分表并插入1kw条记录

目录 一&#xff0c;数据分片 二&#xff0c;水平分片 三&#xff0c;创建数据库表 四&#xff0c;springboot项目导入依赖 五&#xff0c;创建类 六&#xff0c;bug bug放到最后了。 一&#xff0c;数据分片 数据分片指按照某个维度将存放在单一数据库中的数据分散地存…

(学习日记)2024.04.06:UCOSIII第三十四节:互斥量函数接口讲解

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【Hadoop技术框架-MapReduce和Yarn的详细描述和部署】

前言&#xff1a; &#x1f49e;&#x1f49e;大家好&#xff0c;我是书生♡&#xff0c;今天的内容主要是Hadoop的后两个组件&#xff1a;MapReduce和yarn的相关内容。同时还有Hadoop的完整流程。希望对大家有所帮助。感谢大家关注点赞。 &#x1f49e;&#x1f49e;前路漫漫&…

香港科技大学(广州)智能制造学域可持续能源与环境学域博士招生宣讲会——重庆大学专场(暨全额奖学金政策)

两个学域代表教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01;可带简历现场咨询和面试&#xff01; &#x1f4b0;一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; 报名链接&#xff1a;https://www.wjx.top/vm/wmuN2ea.aspx# 地点&#xff1a;重庆大学A区…

观《你想活出怎样的人生》有感

《你想活出怎样的人生》 四月六号&#xff0c;和赵茜小美女观看了宫崎骏导演拍的《你想活出怎样的人生》&#xff0c;感受颇丰&#xff0c;特此写一篇文章以记之。 电影简介 《你想活出怎样的人生》是宫崎骏执导的动画电影&#xff0c;不仅是宫崎骏的复出之作&#xff0c;也…

ARP寻址过程

当知道目标的IP但是不知道目标的Mac地址的时候就需要借助ARP寻址获取目标的Mac地址&#xff0c;传输层借助四元组&#xff08;源IP源端口&#xff1a;目标IP目标端口&#xff09;匹配&#xff0c;网络层借助IP匹配&#xff0c;数据链路层则根据Mac地址匹配&#xff0c;数据传输…

77、WAF攻防——权限控制代码免杀异或运算变量覆盖混淆加密传参

文章目录 WAF规则webshell免杀变异 WAF规则 函数匹配 工具指纹 webshell免杀变异 php 传参带入 eval可以用assert来替换,assert也可以将字符串当作php代码执行漏洞 php 变量覆盖 php 加密 使用加密算法对php后门进行加密 php 异或运算 简化:无字符webshellP 无数字字母rc…

Open3D(C++) 鲁棒损失函数优化的ICP算法

目录 一、损失函数1、关于2、损失函数3、Open3D实现二、代码实现三、结果展示1、配准前1、配准后本文由CSDN点云侠原创,

11、子串-滑动窗口最大值

题解&#xff1a; 双端队列是一种特殊的队列&#xff0c;允许你在队列的两端进行插入和删除操作。在滑动窗口问题中&#xff0c;我们使用它来存储可能是当前窗口最大值的元素的索引。 维护队列的顺序&#xff1a; 当新元素进入窗口时&#xff0c;我们将它与队列尾部的元素进…

307k star, 免费的编程书籍 free-programming-books

307k star, 免费的编程书籍 free-programming-books 分类 开源分享 项目名: free-programming-books -- 各种编程语言免费学习资源 Github 开源地址&#xff1a; https://github.com/EbookFoundation/free-programming-books 查找页面&#xff08;英文&#xff09;&#xff…

在线代码生成器Mybaitis和Mybaitis Plus

功能 支持根据提供的数据信息自动找表和表字段可以单独生成某个文件可以按需生成多个文件(打包为 zip)常用的 vo 和 dto 支持字段自定义(支持多表字段合并)在非包的场景可以不输入 root 包支持批量多表生成支持 lombok 和 swaggerMybaitis和Mybaitis Plus 在页面样式上基本一样…

java流式计算Stream

java流式计算Stream 流(Stream)到底是什么呢? 是数据渠道&#xff0c;用于操作数据源&#xff08;集合、数组等&#xff09;所生成的元素序列。 “集合讲的是数据&#xff0c;流讲的是计算! ” 特点&#xff1a; Stream自己不会存储元素。 Stream不会改变源对象。相反&#x…

分享一份适合练手的软件测试实战项目

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

面试算法-152-螺旋矩阵

题目 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 解 class Solution {public List<Integ…

串口和 蓝牙模块HC08

串口基本认知 串行接口简称串口&#xff0c;也称 串行通信 接口或 串行通讯接口 &#xff08;通常指 COM 接口 &#xff09;&#xff0c;是采用串行通信方 式的 扩展接口 。串行 接口&#xff08;Serial Interface &#xff09;是指数据一位一位地顺序传送。其特点是 通信线路…

vulhub打靶记录——Corrosion2

文章目录 主机发现端口扫描ssh—22search openssh EXP web服务—8080目录扫描登录tomcat后台 提权切换用户查看用户权限寻找SUID命令破解登录密文 总结 主机发现 使用nmap扫描局域网内存活的主机&#xff0c;命令如下&#xff1a; nmap -sP 192.168.151.0/24192.168.151.1&am…