栈和队列

声明:本文主要作为作者的复习笔记,由于作者水平有限,难免有错误和不准确之处,欢迎读者批评指正。

1. 线性表接口两个常用子类

ArrayList => 底层是基于动态数组的实现
LinkedList => 底层是基于双向链表的实现

  • LinkedList是队列Queue的子类,还是双端队列Deque的子类;
  • LinkedList有一个反向遍历的迭代器,从链表的尾部开始遍历;也有一个正向的迭代器,从链表的头部开始遍历。
  • 使用List接口的方法都一样,都是List接口中定义好的方法;

2. 什么时候选择ArrayList,什么时候选择LinkedList?

不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持;O(1)不支持;O(N)
头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
插入空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除(频繁)

若ArrayList和LinkedList都默认调用add方法进行插入,这两个结构都在进行尾插;其实数组的尾插比链表还快elementData[size++] = val;

3. 栈和队列的关系

栈和队列都是添加和删除元素操作受限的线性表,本质上栈和队列没什么不同,可以相互转换,都是线性表的一种;

栈:树型结构的dfs(深度优先遍历 - 前中后序)

队列:树型结构的bfs(广度优先遍历 - 层序遍历)

应用场景
可以使用栈来模拟队列,同样的,也可以使用队列来模拟栈的实现;

4. 栈

一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

  • 入栈(push)
    栈的插入操作叫做进栈/压栈/入栈;入数据在栈顶。
  • 出栈(pop)
    栈的删除操作叫做出栈;出数据在栈顶。
  • 无论入栈还是出栈,都只能固定在栈顶进行。
  • 查看栈顶元素peek():只取值,不删除。
    入栈出栈

5. 栈的实现根据使用的内部结构不同分为顺序栈和链式栈

  1. 顺序栈:内部还是动态数组,规定只能从数组的末尾进行插入和删除;
    插入 => 入栈
    删除 => 出栈
  2. 链式栈:内部使用链表,默认规定在链表的尾部进行插入/删除;

6. 栈、虚拟机栈、栈帧有什么区别

  1. 栈数据结构的栈:线性表的一种,满足后进先出的特点(LIFO),保存在栈中的元素都是相同数据类型;
  2. 虚拟机栈,操作系统栈:概念性的东西,底层到底是不是用的栈(数据结构),到底用的链式栈还是顺序栈都不是关键;
  3. 栈帧:函数从调用过程到结束的一个体现,一个函数从调用到销毁其中占用的空间,内部的局部变量都统一放在该函数的栈帧中(一种类型,专门描述函数在操作系统内部的调用过程);

7. 队列(FIFO:First In First Out)

操作受限的线性表,只能从队列的一端添加元素(队尾),从队列的另一端删除元素(队首);

队列

  • 入队:向队列中添加元素(offer | push);
  • 出队:从队列中移除元素(poll);

8. 广义上的队列一般有以下几种

  1. 普通队列(FIFO链式队列)— 基于链表实现(单链表足够,尾插头删);
  2. 循环队列,可以从队尾走到队首,使用定长数组来实现;
  3. 优先级队列(非线性结构),一般都是先进先出,但是,优先级较高的元素可以优先出队;内部使用堆这个数据结构来实现(JDK默认使用的是最小堆 — 完全二叉树);

9. 循环队列

循环队列使用定长数组来实现,数组的首尾相连就构成了循环队列(就是走到数组最后一个元素时,下一个访问的就是数组的首地址);循环队列一般用在os生存消费者模型。

10. 循环队列判断是否为空,判断队列是否已满

判断循环队列是否为空:head == tail

判断循环队列是否已满:(tail + 1)% num.length == head;

  • head索引指向队列队首第一个元素的下标,tail索引指向队列最后一个元素的下一个位置下标(新元素就可以使用tail直接入队列);
  • 无论循环队列是空还是已满,head == tail重合了;因此为了区分循环队列到底为空还是已满,循环队列会浪费一个空间;
    例:要保存一个最大支持5个元素的循环队列,则开辟一个长度为6的数组!多余的这个空间就是来判断队列是否已满。

11. 无论是入队还是出队,在循环队列中,下标后移都需要进行取模操作

  • 入队:offer
    tail继续向后走,tail = (tail + 1)% num.length;
  • 出队:poll
    head继续向后走,head = (head + 1)% num.length;

12. 循环队列不断入队和出队中,有效元素以及浪费的空间都在动态变化

在循环队列中,浪费空间的位置不是固定的,每次(tail + 1)% num.length到head之前的那个空间是浪费的。

在任意时刻
int lastIndex = tail == 0 ?num.length - 1 : tail - 1;
队列中的有效元素 [head…lastIndex];

13. 双端队列(Deque)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque是“double ended queue”的简称;

  • 元素可以从队头出队和入队,也可以从队尾出队和入队;
  • 在Java中,只要用栈和队列,统一使用Deque接口!根据实际使用时的场景选择基于数组实现的ArrayDeque或者基于双向链表实现的LinkedList;

Deque接口的使用

//使用Deque作为栈,方法名都不变 push pop peek
Deque<Integer> stack = new LinkedList<>();
stack.push(1);
stack.push(3);
stack.push(5);
stack.push(7);
System.out.println(stack);
//输出7
System.out.println(stack.pop());
//使用Deque作为队列,方法名不变 offer poll peek
Deque<Integer> queue = new ArrayDeque<>();
queue.offer(2);
queue.offer(4);
queue.offer(6);
queue.offer(8);
System.out.println(queue);
//输出2
System.out.println(queue.poll());

14. 常见的线性表:元素之间呈线性排列,一次只能添加一个同类型的元素;

数组,链表,栈,队列,字符串(char[ ] 多个字符呈线性排列);

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值