棧
-
棧(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