栈
-
栈(Stack):只允许在一端进行插入或删除操作的线性表。
-
栈顶(Top):线性表允许进行插入和删除的那一端。
-
栈底(Bottom):固定的,不允许进行插入和删除的另一端
-
特点:
1.栈是受限的线性表,所以自然具有线性关系。
2.栈中元素后进去的必然先出来,即后进先出LIFO(Last In First Out)
- 栈中元素后进去的必然先出来,即后进先出LIFO(Last In First Out)
-
顺序栈
- 栈是线性表的特例,那栈的顺序存储也是线性表顺序存储的简化。栈的顺序存储结构也叫作顺序栈。
- Top值不能超过
MaxSize
- 空战的判定条件通常定为
top == -1
,满栈的判定条件通常为top = MaxSize-1
- Top值不能超过
#define MaxSize 50 //定义栈中元素的最大个数 typedef struct{ Elemtype data[MaxSize]; //存放栈中元素 int top; //栈顶指针 }SqStack; // 顺序栈的简写
-
顺序栈的操作
- 1.判空:
bool StackEmpty(SqStack S){ if(S.top == -1) return true; else return false; }
- 2.进栈:
bool Push(SqStack &S , ElemType x){ if(S.top == MaxSize-1) return false; S.data[++S.top] = x; // 重点++顺序 return true; }
- 3.出栈:
bool Pop(SqStack &S , ElemType x){ if(S.top == -1) return false; x = S.data[S.top--]; // 重点++顺序 return true; }
- 4.读取栈顶元素:
bool GetTop(SqStack &S , ElemType x){ if(S.top==-1) return false; x=S.data[S.top] return true; }
- 栈是线性表的特例,那栈的顺序存储也是线性表顺序存储的简化。栈的顺序存储结构也叫作顺序栈。
-
共享栈
- 顺序栈的存储空间大小需要事先开辟好,很多时候对每个栈各自单独开辟存储空间的利用率不如将各个栈的存储空间共享
- 示意图
- 共享栈的结构
#define MaxSize 100 //定义栈中元素的最大个数 typedef strcut{ Elemtype data[MaxSize]; // 存放栈中元素 int top1; //栈1栈顶指针 int top2; //栈2栈顶指针 }SqDoubleStack;
- 共享栈的操作:(进栈)
bool Push(SqDoubleStack &S,ElemType x,int stackNum){ if(S.top1+1 == S.top2) return false; if(stackNum == 1) S.data[++S.top1]=x; else if(stackNum == 2) S.data[--S.top1]=x; return true; }
-
链式栈
- 栈是线性表的特例,线性表的存储结构还有链式存储结构,所以也可以用链表的方式来实现栈。栈的链式存储结构也叫作链栈。
- 特点
1.链栈一般不存在栈满的情况。
2.空栈的判定条件通常定为top==NULL; - 结构
typedef struct SNode{ Elemtype data; //结点数据域 struct SNode *next; //结点指针域 }SNode,*SLink //链栈的结点 //链栈的数据结构 typedef struct LinkStack{ SLink top; //栈顶结点 int count;//元素个数 }LinkStack
-
链式栈的操作
- 1.进栈
bool Push(LinkStack *S,ElemType &x){ SLink p=(SLink)malloc(sizeof(SNode)); //申请一个新的结点空间 p->data = x; //新元素的值 p->next=S->top; //p的后继指针指向栈顶元素 S->top = p //栈顶指针指向新的元素 S->count++; //栈中元素个数加1 return true; }
- 2.出栈
bool Pop(LinkStack *S,ElemType &x){ if(S->top ==NULL) return flase; x = S->top->data; //栈顶元素值 Slink p = S->top; //辅助指针 S->top = S->top->next; //栈顶指针后移 free(p); //释放被删除数据的存储空间 S->count--; //栈中元素个数减一 return true; }
队列
-
队列是只允许在一端进行插入,而在另一端进行删除的线性表
-
队头(Front):允许删除的一端,又称为队首。
-
队尾(Rear): 允许插入的一端。
-
先进入队列的元素必然先离开队列,即先进先出(First In First Out)简称FIFO
-
顺序队列
#define MaxSize 50 //定义队列中的元素的最大个数 typedef struct{ ElemType data[MaxSize];//存放队列元素 int front,rear;//队头指针和队尾指针 }SqQueue;
- 用数组来实现队列,可以将队首放在数组下标为0的位置。
-
循环队列
-
把数组“掰弯”,形成一个环。
Rear
指针到了下标为4的位置还能继续指回到下标为0的地方。这样首尾相连的顺序存储的队列就叫循环队列 -
入队:
rear=(rear+1)%MaxSize
-
出队:
front=(front+1)%MaxSize
-
循环队列的操作
- 1.入队:
bool EnQueue(SqQueue &Q,ElemType x){ if((Q.rear+1)%MaxSize==Q.front) return false; Q.data[Q.rear]=x; Q.rear=(Q.rear+1)%MaxSize; return true; }
- 2.出队:
bool DeQueue(SqQueue &Q,ElemType &x){ if(Q.rear==Q.front) return false; x=Q.data[Q.front]; Q.front=(Q.front+1)%MaxSize; return true; }
-
概要: 那如何分辨队列是空还是满呢?
- 方法一:设置标志位
flag
,当flag=0
且rear
等于front
时为队列空,当flag=1
且rear
等于front
时为队列满。 - 方法二:我们把
front=rear
仅作为队空的判定条件。当队列满的时候,令数组中仍然保留一个空余单元。我们认为这种情况就是队列满了。
- 方法一:设置标志位
-
-
链式队列
- 队列的链式存储结构,其实就是线性表的单链表,只不过需要加点限制,只能表尾插入元素,表头删除元素。
- 为了方便操作,我们分别设置队头指针和队尾指针,队头指针指向头结点,队尾指针指向尾结点。
- 链式队列的操作
-
1.入队:我们知道队列只能从队尾插入元素,队头删除元素。于是入队就是在队尾指针进行插入结点操作。链队的插入操作和单链表的插入操作是一致的。
-
2.出队:出队就是头结点的后继结点出队,然后将头结点的后继改为它后面的结点。
-
-
双端队列
- 双端队列是指允许两端都可以进行入队和出队操作的队列
面试小算法(Python版)
1、用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail
和 deleteHead
,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead
操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
代码如下:
class CQueue:
def __init__(self):
self.x = []
self.y = []
def appendTail(self, value: int) -> None:
self.x.append(value)
def deleteHead(self) -> int:
if self.y: return self.y.pop()
if not self.x: return -1
while self.x:
self.y.append(self.x.pop())
return self.y.pop()
# Your CQueue object will be instantiated and called as such:
# obj = CQueue()
# obj.appendTail(value)
# param_2 = obj.deleteHead()
2、最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。
示例:
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
提示:
pop
、top
和getMin
操作总是在 非空栈 上调用。
代码如下
class MinStack:
def __init__(self):
"""
initialize your data structure here.
"""
self.stack = []
self.min_stack = []
def push(self, x: int) -> None:
self.stack.append(x)
if self.min_stack ==[] or self.min_stack[-1] >= x:
self.min_stack.append(x)
def pop(self) -> None:
if self.stack.pop() == self.min_stack[-1]:
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
3、栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
提示:
0 <= pushed.length == popped.length <= 1000
0 <= pushed[i], popped[i] < 1000
pushed
是popped
的排列。
代码如下
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
stack,i = [],0
for num in pushed:
stack.append(num)
while stack and stack[-1] == popped[i]:
stack.pop()
i += 1
return not stack