数据结构5.0

发布于:2025-05-10 ⋅ 阅读:(13) ⋅ 点赞:(0)

大家好,今天是队列的知识哦~

目录

一、概念

1.0单链表

2.0双链表

3.0数组

二、队列的方法

1.0 offer方法

 2.0 poll方法

3.0 peek方法

4.0 isEmpty方法

三、队列的题目

1.0 用队列实现栈

2.0 用栈实现队列

3.0 设计循环队列


一、概念

数组 、单链表和双链表都可以实现队列

1.0单链表

入栈操作:单链表是头插  时间复杂度O(1) 出栈操作:单链表是删除头节点

这样实现了 队列 先进先出的特点

2.0双链表

头插 尾删   可以实现队列

尾插 头删   也可以实现队列

只要保证特性满足 队列的特性即可

源码里面是尾插头删

3.0数组

队尾入 队头出 如果放到数组里面很难搞 队尾已经到了数组长度的极限了,队头删了还有空间

我们会发现 如果把数组前面当作队头  数组最后当作队尾 

当队尾一直遍历到数组末尾 不能加数据了 但是随着队头出  数组还是有空间的呀

如果把数组卷起来 头尾相连  得到一个循环数组  这样就能更大化的利用空间了

这里我们的rear补药rear++

用下面这个公式可以是rear循环走:rear=(rear+1)%len  front也一样 不会越界

有关这个部分的内容放到下面的题目里面了  622. 设计循环队列 - 力扣(LeetCode)

二、队列的方法

Queue是个接口,底层是通过链表实现的

方法预览:

1.0 offer方法

入队列 我们用的是双链表  源码里面的offer是用的头插法

所以逻辑和我们双链表的头插几乎一样

这个usedSize 可以搞一个

代码这里是尾插

下面是代码:

public void offer (int e){
ListNode newNode = new ListNode(e);
if(front ==null){
 front = rear = node;
 }else{
 last.next = node;
 node.prev = last;
 last = node;
 }
 uesdSize++;
}

 2.0 poll方法

同理  类似双链表的删除头节点的方法

下面是代码:

 public int poll(){
        if(front == null){
            return -1;
        }
        int ret = front.val;;
        if(front == rear){
            front = null;
            rear = null;
            usedSize--;
        }
        front = front.next;
        front.prev= null;
        usedSize--;
        return ret;
    }

3.0 peek方法

获取队头元素  也就是获取双链表头结点元素

要注意分情况讨论

public int peek(){
        if(front == null){
            return -1;
        }
        return front.val;
    }

4.0 isEmpty方法

public boolean isEmpty(){
 return usedSize == 0;
}

三、队列的题目

1.0 用队列实现栈

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

思路分析:

如何用队列实现栈呢? 如何用队列让先进的后出

方法有很多种 这里我们讲解一个方法  用交换法

两个队列   队列1和队列2  存入的时候 都存到queue1里面

要实现pop方法和top方法的时候  明确我们要删除的是后进的那个元素

我们将队列1添加的元素出队到队列2   剩下最后一个元素 (这个元素就是后进的那个)

用popped记录下后删除  然后交换队列把那些在队列2的元素交换回来

top方法的大体思路同样如此哦 这里就不再赘述了 多犯错误多调试比较快

class MyStack {
    Queue<Integer> queue1;
    Queue<Integer> queue2;
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        queue1.offer(x);
    }
    
    public int pop() {
        while(queue1.size()>1){
            queue2.offer(queue1.poll());
        }
            int popped=queue1.poll();
            Queue<Integer> temp = queue1;
            queue1=queue2;
            queue2=temp;
            return popped;
        }
        // while(queue1.isEmpty()){
        // queue2.offer(queue1.getLast());
        // }
        // queue2.poll();
    
    
    public int top() {
        // return queue2.peek();
        while(queue1.size()>1) {
            queue2.offer(queue1.poll());
        }
         int top = queue1.peek();
         queue2.offer(queue1.poll());
        Queue<Integer> temp = queue1;
        queue1=queue2;
        queue2=temp;
        return top;
    }
    
    public boolean empty() {
        return queue1.isEmpty() && queue2.isEmpty();
    }
    }

2.0 用栈实现队列

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

思路分析:

要达到的目标是用栈模拟实现队列先进先出的结构

栈的结构是先进后出

我们可以用两个栈  一个用来push 一个用来pop和peek

我们要实现先进先出 把栈1里面的元素push到栈2里面

此时栈2的栈顶元素就是先进的元素 然后用栈2的pop方法peek方法就可以达到目的

要注意的点是:判断栈2是否为空

有时候 栈2的元素还没有pop完呢 栈1里面又加入新的元素了

这个时候我们pop也是从栈2里面出 毕竟栈2的元素比栈1早进

然后直到栈2为空了  我们想要pop了 再把栈1的元素push到栈2pop

所以pop方法和peek方法 要先判断栈2是否为空

下面是代码:

class MyQueue {
      private Stack<Integer>  stack1;
      private Stack<Integer> stack2;
    public MyQueue() {
      stack1= new Stack<>();
      stack2= new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x);
    }
    
    public int pop() {
        if(empty()){
            return -1;
        }
        // 为什么要判断 stack是否为空呢 难道还能中途
        //确保stack1 的元素只会在stack为空时倒入    
        //如果stack1里面有元素 stack2里面也有元素 stack里面的早 所以弹stack2
        //stack2为空了再倒入
        if(stack2.isEmpty()){
        while(! stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        }
        return stack2.pop();

    }
    
    public int peek() {
        if(empty()) return -1;
        if(stack2.isEmpty()) {
            while(! stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
        }
        return stack2.peek();
    }
    
    public boolean empty() {
        return stack1.isEmpty()&&stack2.isEmpty();
    }
}

3.0 设计循环队列

622. 设计循环队列 - 力扣(LeetCode)

我们定义了一个数组之后 开始往里面放元素呀 

rear和front都在数组0下标   放入一个元素 队头front不变 队尾rear往后面加

这样直到数组满了  对应上面图片第一张 此时rear从7下标到了0小标 这个时候的处理方法有三种

第一种是  定义usedSize来记录  uedSize和len的比较

第二种是  可以使用标记 定义一个flag类型的变量 相遇的时候flag定义为false或者true

第三种是 浪费一个空间来区分  我们重点讲解这个内容

如果rear的下一个就是front  证明就是满的

rear如何实现 循环着走数组呢? 

rear=(rear+1)%len;   这个公式满足了rear在数组里 面循环  不会出现数组越界异常

最后 front也得因为删除队列元素 而在数组里面循环 当然这个是后话

下面开始各种方法的讲解:

(1)判断满空的方法:为什么是rear=front呢  因为这里的rear和front因为数组是循环的

                                       它们的位置不是固定的在开头 

(2)判断满:用到rear的循环走公式碰到了front

就是要判断 (rear+1)%elem.length == front;

(3)构造器  为什么要k+1 呢   因为我们用的方法是浪费一个空间  所以创建的时候就多一个 

                      这样就能有k个位置存放元素了

(4)入队 enQueue:入队的时候我们首先要判断满了没有  rear位置放上元素和rear位置的更新

         要注意的地方是:我们不能使用rear++ 代码里面用哪个公式是为了 实现rear的循环

(5)出队 deQueue : 出队 我们首先要判断是不是空呢  注意出队的时候 直接更新front节点就可以

         要注意的地方是:front 也得循环着走  所以也用到了那个公式

(6)从队首获得元素:这里的注意点是还是要提前判断循环队列是不是空的呢

(7)从队尾获得元素:这个要注意的是分类讨论   根据情况 

        如果rear的下标不是0  那么返回的是rear前面的元素  rear-1  前面我们也讲过 rear指向下一个

        可插入的位置   

        如果rear的下标是0 那么返回的是 数组长度下标减一  这个特殊情况在图片上面有所呈现

下面是代码:

class MyCircularQueue {
    int rear ;//队尾下表
    int front ;//队头下标
    int elem[] ;
    public MyCircularQueue(int k) {
        //构造器 设置队列的长度为k
         this.elem= new int[k+1];
        // rear= (rear+1)%elem.length;
    }
    
    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        elem[rear]=value;
        //rear++;  这样容易越界
        rear= (rear+1)%elem.length;
        return true;
    }
    
    public boolean deQueue() {
        //出 front往后面走就可以了
        // 空的 不能出
        if(isEmpty()){
            return false;
        }
        //不空 front往后走
        front=(front+1)%elem.length;
        return true;
    }
    
    public int Front() {
         //从队首获得元素
        if (isEmpty()){
            return -1;
        }
        return elem[front];
    }
    
    public int Rear() {
        //得到队尾元素
        if (isEmpty()) {
            return -1;
        }
        int index  = (rear == 0)? elem.length-1 : rear-1;
        return elem[index];
    }
    
    public boolean isEmpty() {
       return front == rear;
    }
    
    public boolean isFull() {
        if((rear+1)%elem.length == front){
            return true;
        }
        return false;
    }
}

以上 就是队列的知识咯~ 遍历一个节点

感谢大家的支持

更多内容还在加载中...........

如有问题欢迎批评指正,祝大家生活愉快、学习顺利!!!


网站公告

今日签到

点亮在社区的每一天
去签到