數據結構筆記三:棧和隊列


基本概念

棧(Stack)是只允許在一端進行插入或刪除的線性表。

棧頂:線性表允許進行插入刪除的一端。

棧底:固定的,不允許進行插入和刪除的一端。

特點:后進先出(LIFO)

數學性質:n個不同元素進棧,出棧元素不同排列的個數為$\frac{1}{n+1} \mathrm{C}_{2n}^{n} $(卡特蘭樹)​

基本操作

InitStack(&S);			//初始化一個空棧S
DestoryList(&S);		//銷毀棧,並釋放棧S所占用的存儲空間

Push(&S,x);				//進棧,若棧S未滿,則將x加入使之成為新棧頂
Pop(&S,&x);				//出棧,若棧非空,則彈出棧頂元素,並用x返回

GetTop(S,&x);			//讀棧頂元素,若棧S非空,則用x返回棧頂元素

StackEmpty(S);			//判斷一個棧S是否為空。若S為空,則返回true,否則返回false

順序棧

順序棧的實現

采用順序存儲的棧,它利用一組地址連續的存儲單元存放自棧底到棧頂的數據元素,同時設一個指針指示當前棧頂元素的位置。

#define MaxSize 10		//定義棧中元素的最大個數
typedef struct{
    Elemtype data[MaxSize];		//存放棧中元素
    int top;				   //棧頂指針
}SqStack;

棧頂指針:\(S.top\)​,初始設置為\(S.top=-1\);棧頂元素:\(S.data[S.top]\)

順序棧的基本運算

//初始化
void InitStack(SqStack& S)
{
    S.top=-1;
}
//判斷空
bool InitStack(SqStack S)
{
    if(S.top=-1)
        return true;
    return false;
}
//進棧
//棧不滿時,棧頂指針先加1,再送值到棧頂元素
bool Push(SqStack& S,Elemtype x)
{
    if(S.top==MaxSize-1)
        return false;
    S.data[++S.top]=x;
    return true;
}
// 出棧
// 棧非空時,先取棧頂元素值,再將棧頂指針減1
bool Pop(SqStack& S,Elemtype& x)
{
    if(S.top==-1)
        return false;
    e=S.data[S.top--];
    return true;
}
//取棧頂元素
bool GetTop(SqStack S,Elemtype& x)
{
    if(S.top==-1)
        return false;
    e=S.data[S.top];
    return true;
}

共享棧

將兩個順序棧共享一個一維數組空間

image-20210811213129939

鏈棧

鏈棧的實現

采用鏈式存儲;

優點是便於多個棧共享存儲空間和提高其效率,且不存在棧滿上溢的情況。

通常采用單鏈表實現,並規定所有操作都是在單鏈表的表頭進行。(Lhead指向棧頂元素)

typedef struct Linknode{
    Elemtype data;
    struct Linknode *next;
}*LiStack;

具體操作自己實現

棧的應用

括號匹配

算法思想:

  1. 初始設置一個空棧,順序讀入括號
  2. 若是右括號,則使置於棧頂的最急迫期待的左括號進行匹配,或者不合法的i情況(不匹配,退出程序)
  3. 若是左括號,則壓入棧中;算法結束時,棧為空,否則括號序列不匹配

image-20210811231653080

//代碼實現
bool bracketCheck(char str[],int length)
{
    SqStack S;
    InitStack(S);
    for(int i=0;i<length;i++)
    {
        if(str[i]=='('||str[i]=='['||str[i]=='{')
            Push(S,str[i]);
        else
        {
            if(StackEmpty(S))
                return false;
            char topElem;
            Pop(S,topElem);
            if(str[i]==')'&&topElem!='(')
                return false;
            if(str[i]==']'&&topElem!='[')
                return false;
            if(str[i]=='}'&&topElem!='{')
                return false;
        }
    }
    return StackEmpty(S);
}

表達式求值

中,后,前綴表達式

image-20210811232353371

手算

中綴轉后綴手算方法:

  1. 確定中綴表達式中的各個運算的運算順序
  2. 選擇下一個運算符,按照【左操作數,右操作數,運算符】的方式組合成一個新的操作數
  3. 如果還有運算符沒被處理,就繼續②

“左優先”原則:只要左邊的運算符能先計算,就優先算左邊的(保證手算和機算的結果一樣)

后綴表達式的手算方法:從左往右掃描,每遇到一個運算符,就讓運算符前面最近的兩個操作數執行對應運算,合體為一個操作數(注意:兩個操作數的左右順序)

特點:最后出現的操作數先被運算

用棧實現后綴表達式的計算

  1. 從左往右掃描下一個元素,直至處理完所有元素
  2. 若掃描到操作數則壓入棧,並回到①;否則執行③
  3. 若掃描到運算符,則彈出兩個棧頂元素,執行相應運算,運算結果壓回棧頂,回到①

注意:先出棧的是”右操作數“

中綴轉前綴的手算方法:

  1. 確定中綴表達式中各個運算符的運算順序
  2. 選擇下一個運算符,按照【運算符,左操作數,右操作數】的方式組合成一個新的操作數
  3. 如果還有運算符沒被處理,就繼續②

“右優先”原則:只要右邊的運算符能先計算,就優先算右邊的

用棧實現前綴表達式的計算

  1. 從右往左掃描下一個元素,直至處理完所有元素
  2. 若掃描到操作數則壓入棧,並回到①;否則執行③
  3. 若掃描到運算符,則彈出兩個棧頂元素,執行相應運算,運算結果壓回棧頂,回到①

注意:先出棧的是”左操作數“

機算

中綴轉后綴表達式

初始化一個棧,用於保存暫時還不能確定運算順序的運算符

從左到右處理各個元素,直到末尾,可能遇到三種情況:

  1. 遇到操作數,直接加入后綴表達式
  2. 遇到界限符。遇到“(”直接入棧;遇到“)”則依次彈出棧內運算符並加入后綴表達式,直至彈出“(”為止。注意:“(”不加入后綴表達式
  3. 遇到運算符。依次彈出棧中優先級高於或等於當前運算符的所有運算符,並加入后綴表達式,若碰到“(”或棧空則停止。之后再把當前運算符入棧。

按上述方法處理完所有字符后,將棧中剩余運算符依次彈出,並加入后綴表達式。

中綴表達式的計算

用棧實現:初始化兩個棧,操作數棧和運算符棧

  1. 若掃描到操作數,壓入操作數棧
  2. 若掃描到運算符或界限符,則按照“中綴轉后綴”相同的邏輯壓入運算符棧(期間也會彈出運算符,每當彈出一個運算符,就需要再彈出兩個操作數棧的棧頂元素並執行相應運算,運算結果再壓回操作數棧)

遞歸的應用

函數調用背后的過程

函數調用的特點:最后被調用的函數最先執行結束(LIFO)

函數調用時需要一個棧存儲:

  • 調用返回地址
  • 實參
  • 局部變量

遞歸調用時,函數調用棧可稱為“遞歸調用棧”

每進入一層遞歸,就將遞歸調用所需信息壓入棧頂

每退出一層遞歸,就從棧頂彈出相應信息

適合用“遞歸”算法解決:可以把原始問題轉換為屬性相同,但規模較小的問題。如:斐波那契數列

隊列

基本概念

隊列是只允許在一端進行插入,在另一端刪除的線性表。

特點:先進先出(FIFO)

image-20210811213958995

基本操作

InitQueue(&Q);			//初始化隊列,構造一個空隊列Q
DestoryQueue(&Q);		//銷毀隊列。銷毀並釋放隊列Q所占的內存空間

EnQueue(&Q,x);			//入隊,若隊列未滿,將x加入,使之成為新的隊尾
DeQueue(&Q,&x);			//出隊,若隊列Q非空,刪除隊頭元素,並用x返回

GetHead(Q,&x);			//讀隊頭元素,若隊列Q非空,則將隊頭元素賦值給x

QueueEmpty(Q);			//判隊列空,若隊列Q為空返回true,否則返回false

隊列的順序存儲結構

隊列的順序存儲

#define MaxSize 50				//定義隊列中元素的最大個數
typedef struct{
    Elemetype data[Maxsize];	//存放隊列元素
    int front,rear;				//隊頭指針和隊尾指針
}

初始狀態(隊空條件):\(Q.front=Q.rear=0\)

進隊操作:隊不滿時,先送值到隊尾元素,再將隊尾指針加1

出隊操作:隊不空時,先取隊頭元素值,再將隊頭指針加1

“上溢出”(加溢出):\(Q.rear=MaxSize\)

具體操作代碼自己實現

循環隊列

image-20210811220856173

初始時:\(Q.front=Q.rear=0\)

隊首指針進1:\(Q.front=(Q.front+1)\%Maxsize\)

隊尾指針進1:\(Q.rear=(Q.rear+1)\%Maxsize\)

隊列長度:\((Q.rear+Maxsize-Q.front)\% MaxSize\)

判斷判空判滿方法

  • 犧牲一個存儲單元區分隊空和隊滿

    隊滿條件:\((Q.rear+1)\%MaxSize==Q.front\)

    隊空條件仍為:\(Q.rear=Q.front\)

    隊列中元素個數:\((Q.rear-Q.front+MaxSize) \% MaxSize\)

  • 增加一個\(Size\)變量記錄隊列長度

    隊滿條件:\(Q.Size=MaxSize\)

    隊空條件仍為:\(Q.Size=0\)

  • 增加\(tag=0/1\)標記出隊/入隊

    \(tag=0\)​​時,\(Q.front=Q.rear\)​​,則為隊空(出隊操作)

    \(tag=1\)​時,\(Q.front=Q.rear\)​​,則為隊滿(入隊操作)

具體操作代碼自己實現

隊列的鏈式存儲結構

隊列的鏈式存儲結構

image-20210811222426505

typedef struct{    Elemtype data;    struct LinkNode *next;}LinkNode;typedef struct{					//鏈式隊列    LinkNode *front,*rear;		//隊列的隊頭和隊尾指針}LinkQueue;			

鏈式隊列的基本操作

以下代碼都是帶頭結點,若不帶頭結點則需要進行特殊處理(自己思考)

//初始化void InitQueue(LinkQueue &Q){    Q.front=Q,rear=(LinkNode*)malloc(sizeof(LinkNode));    Q->front->next=NULL;}
//判隊空bool Isempty(LinkQueue Q){    if(Q.front==Q.rear)         return true;    return false;}
//入隊Void EnQueue(LinkQueue &Q,Elemtype x){    LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));    s->data=x;s->next=NULL;    Q.rear->next=s;    Q.rear=s;}
//出隊bool DeQueue(LinkQueue &Q,Elemtype &x){    if(Q.front==Q.rear)         return false;    LinkNode* p=Q.front->next;    x=p->data;    Q.front->next=p->next;    if(Q.rear==p)        Q.rear==Q.front;			//若原隊列中只有一個結點,刪除后變空    free(p);    return true;    }

雙端隊列

雙端隊列時指允許兩端都可以進行入隊和出隊操作的隊列。

image-20210811231000563

輸出受限的雙端隊列:允許在一端進行插入和刪除,但在另一端只允許插入的雙端隊列

image-20210811231050883

輸入受限的雙端隊列:允許在一端進行插入和刪除,但在另一端只允許刪除的雙端隊列

image-20210811231108551

考點:輸出序列

隊列的應用

樹的層次遍歷

  1. 根結點入隊
  2. 若隊空(所有結點都已處理完畢),則結束遍歷;否則重復③操作
  3. 隊列中第一個結點,並訪問之。若其有左孩子,則將左孩子入隊;若其有右孩子,則將右孩子入隊,返回②。

操作系統的應用

image-20210812230736074

特殊矩陣的壓縮存儲

一維數組的存儲結構

image-20210812230915595

二維數組的存儲結構

image-20210812231018387

image-20210812231113743

image-20210812231146158

普通矩陣的存儲

image-20210812231231304

對稱矩陣

image-20210812231932513

若n階方陣中任意一個元素\(a_{i,j}\)​都有\(a_{i,j}=a_{j,i}\),則該矩陣為對稱矩陣。

策略:只存儲主對角線+下三角區

按行優先原則將各元素存入一維數組中(長度:\((1/n)*n/2\)

按照行優先原則,元素下標之間的對應關系如下:

\[\begin{equation} k=\left\{ \begin{array}{rcl} \frac {i(i-1)}{2}+j-1 & & i\ge j(下三角區和主對角元素)\\ \frac {j(j-1)}{2}+i-1 & & j\ge i(上三角區和主對角元素) \end{array} \right. \end{equation} \]

三角矩陣

image-20210812232303586

壓縮存儲策略:按行優先原則則將橙色區元素存入一維數組,並在最后一個位置存儲常量c句號

(下三角矩陣)元素下標之間的對應關系如下:

\[\begin{equation} k=\left\{ \begin{array}{rcl} \frac {i(i-1)}{2}+j-1 & & i\ge j(下三角區和主對角元素)\\ \frac {n(n+1)}{2} & & i< j(上三角區) \end{array} \right. \end{equation} \]

(上三角矩陣)元素下標之間的對應關系如下:

\[\begin{equation} k=\left\{ \begin{array}{rcl} \frac {(i-1)(2n-i+2)}{2}+j-1 & & i\le j(上三角區和主對角元素)\\ \frac {n(n+1)}{2} & & i> j(下三角區) \end{array} \right. \end{equation} \]

三對角矩陣

image-20210812232715400

三對角矩陣,又稱帶狀矩陣;

\(|i-j|>1\)時,有\(a_{i,j}=0(i\le i,j \le n)\)

壓縮存儲策略:按行(列)優先原則,只存儲帶狀部分(長度為:\(3n-2\)

按行優先原則,對應關系\(k=2i+j-1\),\(\lceil(k+2)/3 \rceil\)或者\((\lfloor (k+2)/3+1 \rfloor)\)

稀疏矩陣

image-20210812233222010

非零元素遠遠少於矩陣元素的個數

壓縮存儲策略:

  • 順序存儲-三元組<行,列,值>

  • 鏈式存儲-十字鏈表法

    image-20210812233414567


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM