- 栈
1.1概念
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的元素遵循先进后出的原则。
入栈:栈的插入操作叫做入栈/压栈/进栈
出栈:栈的删除操作叫做出栈
入数据和出数据都是在栈顶
1.2实现
1.2.1顺序表实现
顺序表使用尾插操作来表示“入栈”,尾删来表示“出栈”,根据下标获取元素的操作“取栈顶元素”。
如果使用头插来表示“入栈”,头删来表示“出栈”,是否可行?
可以使用,但效率较低
package java03_09;
//使用顺序表实现栈的一系列操作
public class MyStack {
private int[] data = new int[100];
private int size = 0;
//使用尾插来表示入栈
public void push(int val) {
if(size >= data.length) {
return;
}
data[size] = val;
size++;
}
//尾删表示出栈
public Integer pop(){
if(size == 0) {
return null;
}
int ret = data[size - 1];
size--;
return ret;
}
//取栈顶元素
public Integer peek() {
if(size == 0) {
return null;
}
return data[size - 1];
}
}
1.2.2链表实现
链表使用头插操作来表示“入栈”,头删操作来表示“出栈”,直接取到头结点,就是“取栈顶元素”
package java03_09;
class Node{
int val;
Node next;
public Node(int val){
this.val = val;
}
}
//链表实现栈的一系列操作
public class MyStack2 {
private Node head = null;
//头插表示入栈
public void push(int val){
Node newNode = new Node(val);
if(head == null) {
head = newNode;
return;
}
newNode.next = head;
head = newNode;
}
//头删表示出栈
public Integer pop() {
if (head == null) {
return null;
}
if (head.next == null) {
int ret = head.val;
head = null;
return ret;
}
int ret = head.val;
head = head.next;
return ret;
}
//取栈顶元素
public Integer peek(){
if(head == null) {
return null;
}
return head.val;
}
如果使用尾插来表示“入栈”,尾删来表示“出栈”,是否可行?
可以做到高效的实现,但是需要记录额外的信息,代码也会更复杂。
1.3栈的核心操作
1.入栈:把元素放到栈里面
2.出栈:把最后进来的元素给删掉
3.取栈顶元素:获取到最后一个进来元素的结果
2. 队列
2.1 概念
队列:只允许在一端进行插入数据,在另一端进行删除数据操作的特殊线性表。具有先进先出的原则
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队首
2.2 实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率较低
顺序表:可以使用特殊的方式来实现
链表:使用尾插操作来表示“入队列”,使用头删操作来表示“出队列”,直接获取到头结点,就是取队首元素
package java03_09;
class Node1{
int val;
Node1 next;
public Node1(int val){
this.val = val;
}
}
//使用链表来完成队列的一系列操作
public class MyQueue {
private Node1 head = null;
private Node1 tail = null;
//尾插操作表示“入队列”
public boolean offer(int val) {
Node1 newNode = new Node1(val);
if (head == null) {
head = newNode;
tail = newNode;
return true;
}
tail.next = newNode;
tail = tail.next;
return true;
}
//头删操作表示“出队列”
public Integer poll() {
if (head == null) {
return null;
}
int ret = head.val;
if (head.next == null) {
head = null;
return ret;
}
head = head.next;
return ret;
}
//取队首元素
public Integer peek() {
if (head == null) {
return null;
}
return head.val;
}
}
2.3 循环队列
队列的有效元素的区间[head,tail)
环形队列这个版本的队列相比于链表版本来说,更快,局限性是空间是固定大小的,扩容成本高
入队列:就把新的元素给放到tail对应的下标上,同时tail++
出队列:就把head++,也就意味着把原来的head指向的元素就排除到有效区间之外了
package java03_09;
//使用数组来实现循环队列的一系列操作
public class MyQueue2<head> {
private int[] data = new int[100];
private int head = 0;
private int tail = 0;
private int size = 0;
//入队列
public boolean offer(int val) {
if (size == data.length) {
//这时队列已满
return false;
}
//把新元素放到tail对应的下标上
data[tail] = val;
tail++;
//一旦tail到达了数组的末尾,这时就应让tail从0开始
if (tail == data.length) {
tail = 0;
}
size++;
return true;
}
//出队列
public Integer poll() {
if (size == 0) {
return null;
}
int ret = data[head];
head++;
if (head == data.length) {
head = 0;
}
size--;
return ret;
}
//取队首元素
public Integer peek() {
if (head == 0) {
return null;
}
return data[head];
}
}
环形队列为空的时候,head和tail的位置是重合的;环形队列为满的时候,head和tail的位置也是重合的。如何解决此问题
环形队列如何区分空和满
方案:
- 不要把这个环形队列压榨的那么干净,故意浪费一个空间
和tail用head重合表示空队列
使用tail==head-1表示满队列 - 不浪费空间,专门搞一个size变量记录队列的元素个数。size0就是空,size数组长度就是满
2.4 双端队列deque
两端都可以入队列和出队列