數據結構導論之第三章(棧、隊列、數組)


棧和隊列可看作是特殊的線性表,它們是運算受限的線性表

一、棧

棧:棧是只能在表的一端(表尾)進行 插入和刪除的線性表;允許插入及刪除的一端(表尾)稱為棧頂(Top); . 另一端(表頭)稱為棧底(Bottom);當表中沒有元素時稱為空棧

進棧:在棧頂插入一元素; 

出棧:在棧頂刪除一元素;

棧的特點:后進先出,棧中元素按a1,a2,a3,…an的次序進棧,出棧的第一個元素應 為棧頂元素。換句話說,棧的修改是按后進先出的原則進行的。 因此,棧稱為后進先出線性表(LIFO)。

棧的用途:常用於暫時保存有待處理的數據

1、棧的順序實現:順序棧

  • ● 棧容量——棧中可存放的最大元素個數;
  • ● 棧頂指針 top——指示當前棧頂元素在棧中的位置;
  • ● 棧空——棧中無元素時,表示棧空;
  • ● 棧滿——數組空間已被占滿時,稱棧滿;
  • ● 下溢——當棧空時,再要求作出棧運算,則稱“下溢”;
  • ● 上溢——當棧滿時,再要求作進棧運算,則稱“上溢”。

 

 約定棧的第1個元素存在data[1]中, 則: s->top==0 代表順序棧s為空; s->top==maxsize-1 代表順序棧s為滿 ;

//棧大小
const int maxsize=6;
//1、順序棧類型的定義
typedef struct seqstack {
    DataType data[maxsize];
    int top;
}SeqStk;

//2、棧的初始化
int Initstack(SeqStk *stk){
    stk->top=0;
    return 1;
}
//3、判斷空棧(棧空時返回值為1,否則返回值為0)
int EmptyStack(SeqStk *stk){
    if(stk->top= =0){
        return 1;
    }else{
        else return 0;
    } 
}
/*4、進棧 數據元素x進順序棧sq*/
int Push(SeqStk *stk, DataType x){
     /*判是否上溢*/
    if(stk->top==maxsize -1){ 
        error(“棧滿”);return 0;
    } else {
        stk->top++;/*修改棧頂指針,指向新棧頂*/
        stk->data[stk->top]=x; /*元素x插入新棧頂中*/
        return 1;
    }
}
/*5、出棧 順序棧sq的棧頂元素退棧*/
int Pop(SeqStk *stk){
     /*判是否下溢*/
    if(stk->top==0){
         error(“棧空”);
         return 0;
     }else {
        stk->top-- ; /*修改棧頂指針,指向新棧頂*/
        return 1;
    }
}
//6、 取棧頂元素
DataType GetTop(SeqStk *stk){
    if(EmptyStack(stk)){
        return NULLData;
    }else{
        return stk->data[stk->top];
    }
}

雙棧
在某些應用中,為了節省空間,讓兩個數據元素類型一致的棧共享一維數組空間 data[max],成為雙棧,兩個棧的棧底分別設在數組兩端,讓兩個棧彼此迎面“增長”,兩個棧的棧頂變量分別為 top1、top2,僅當兩個棧的棧頂位置在中間相遇時(top1 + 1 =top2)才發生“上溢”,判棧空時,兩個棧不同,當 top1=0 時棧 1 為空棧,top2=max-1 時棧 2 為空桟。雙棧如圖:

2、棧的鏈接實現:鏈棧

棧的鏈式存儲結構稱為鏈棧,它是運算受限的單鏈表, 插入和刪除操作僅限制在表頭位置上進行。棧頂指針就是鏈 表的頭指針

 

 下溢條件:LS->next==NULL;上溢:鏈棧不考慮棧滿現象

//1、鏈棧的定義
typedef struct node{
    DataType data;
    struct node *next
} LkStk;
//2、鏈棧的初始化
void InitStack(LkStk *LS){
    LS=(LkStk *)malloc(sizeof(LkStk));
    LS->next=NULL;
}
//3、判斷棧空
int EmptyStack(LkStk *LS){
    if(LS->next= =NULL){
        return 1;
    } else{
        return 0;
    } 
}
//4、進棧:在棧頂插入一元素x:生成新結點(鏈棧不會有上溢情況發生);將新結點插入鏈棧中並使之成為新的棧頂結點
void Push (LkStk *LS, DataType x){ 
    LkStk *temp;
    temp= (LkStk *) malloc (sizeof (LkStk));
    temp->data=x;
    temp->next=LS->next;
    LS->next=temp;
}
//5、出棧:在棧頂刪除一元素,並返回;考慮下溢問題;不下溢,則取出棧頂元素,從鏈棧中刪除棧頂結點並將結點回歸系統
int Pop (LkStk *LS){ 
    LkStk *temp;
    if (!EmptyStack (LS)){ 
        temp=LS->next;
        LS->next=temp->next;
        free(temp);
        return 1;
    }else{
        return 0;
    }
}
//6、取棧頂元素
DataType GetTop(LkStk *LS){
    if (!EmptyStack(LS)){
        return LS->next->data;
    }else{
        return NULLData;
    }
}

 二、隊列

隊列(Queue)也是一種運算受限的線性表。

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

其中:允許刪除的一端稱為隊頭(front), 允許插入的另一端稱為隊尾(rear)。 隊列 Q=(a1,a2,a3,…an )

 

 隊列特點:先進先出(FIFO);常用於暫時保存有待處理的數據

1、隊列的順序實現

隊列的順序實現:一般用一維數組作為隊列的存儲結構

隊列容量:隊列中可存放的最大元素個數

初始: front=rear=0

進隊: rear增1,元素插入尾指針所指位置

出隊: front增1,取頭指針所指位置元素

隊頭指針front:始終指向實際隊頭元素的前一位置

隊尾指針 rear:始終指向實際隊尾元素

//順序隊列的構造
const int maxsize=20;
typedef struct seqqueue {
    DataType data[maxsize];
    int front, rear ;
}SeqQue;
SeqQue sq;

入隊列操作:sq.rear=sq.rear+1;sq.data[sq.rear]=x;
出隊列操作:sq.front=sq.front+1;
上溢條件:sq.rear = = maxsize-1 ( 隊滿 )
下溢條件:sq.rear = = sq.front (隊列空)
順序隊列的假溢出:sq.rear == maxsize-1,但隊列中實際容量並未達到最大容量的現象;極端現象:隊列中的項不多於1,也導致“上溢”.假溢出浪費空間循環隊列可以解決該問題

2、循環隊列

為隊列分配一塊存儲空間(數組表示),並將 這一塊存儲空間看成頭尾相連接的。

對插入即入隊: 隊尾指針增1,Sq.rear=(sq.rear+1)%maxsize
對刪除即出隊: 隊頭指針增1,Sq.front=(sq.front+1)%maxsize
下溢條件即隊列空:CQ.front==CQ.rear
上溢條件即隊列滿:尾指針從后面追上頭指針,(CQ.rear+1)%maxsize==CQ.front(浪費一個空間,隊滿時實際隊容量=maxsize-1)

設以數組Q[m]存放循環隊列的元素,變量rear和queuelen分別表示循環隊列中隊尾元素的下標位置和元素的個數。則計算該隊列中隊頭元素下標位置的公式是 (rear-queuelen+m)%m

例題:

(rear-front+n)%n=(7-8+100)%100=99

例題:

 

 

//循環隊列的定義
typedef struct Cycqueue{
    DataType data[maxsize];
    int front,reat;
}CycQue;
CycQue CQ;

//循環隊列的初始化
void InitQueue(CycQue CQ){
    CQ.front=0;
    CQ.rear=0;
}
//循環隊列判斷空
int EmptyQueue(CycQue CQ){
    if (CQ.rear==CQ.front){
        return 1;
    } else{
        else return 0;
    }
}
入隊——在隊尾插入一新元素x
● 判上溢否?是,則上溢返回;
● 否則修改隊尾指針(增1),新元素x插入隊尾。
int EnQueue(CycQue CQ,DataType x){
    if ((CQ.rear+1)%maxsize==CQ.front){
        error(“隊列滿”);return 0;
    }else {
        CQ.rear=(CQ.rear+1)%maxsize;
        CQ.data[CQ.rear]=x;
        return 1;
    }
}
出隊——刪除隊頭元素,並返回
● 判下溢否?是,則下溢返回;
● 不下溢,則修改隊頭指針,取隊頭元素。
int OutQueue(CycQue CQ){
    if (EmptyQueue(CQ)){
        error(“隊列空”);
        return 0;
    }else {
        CQ.front=(CQ.front+1)%maxsize;
        return 1;
    }
}
//.取隊列首元素
DataType GetHead(CycQue CQ){
    if (EmptyQueue(CQ)){
        return NULL;
    }else{
        return CQ.data[(CQ.front+1)%maxsize];
    }
}

 3、隊列的鏈接實現

隊列的鏈接實現實際上是使用一個帶有頭結點的單鏈表來表示隊列,稱為鏈隊列。頭指針指向鏈表的頭結點,單鏈表的頭結點的next 域指向隊列首結點,尾指針指向隊列尾結點,即單鏈表的最后一個結點;隊列的鏈式實現是動態申請空間,所以不會存在隊滿的情況。

 

 

 

//類型定義
typedef struct LinkQueueNode{
    DataType data;
    struct LinkQueueNode *next;
} LkQueNode;
typedef struct LkQueue{
    LkQueNode *front, *rear;
}LkQue;
LkQue LQ;
由於鏈接實現需要動態申請空間,故鏈隊列在一定范圍內不會出現隊列滿的情況,當(LQ.front==LQ.rear)成立時,隊列中無數據元素,此時隊列為空
//(1)隊列的初始化
void InitQueue(LkQue *LQ){
    LkQueNode *temp;
    temp= (LkQueNode *)malloc (sizeof (LkQueNode)); //生成隊列的頭結點
    LQ->front=temp; //隊列頭才旨針指向隊列頭結點
    LQ->rear=temp; //隊列尾指針指向隊列尾結點
    (LQ->front) ->next=NULL;
}
//判隊列空
int EmptyQueue(LkQue LQ){
    if (LQ.rear==LQ.front){
        return 1; //隊列為空
    }else{
        return 0;
    }
}
//入隊列
void EnQueue(LkQue *LQ;DataType x){
    LkQueNode *temp;
    temp=(LkQueNode *)malloc(sizeof(LkQueNode));
    temp->data=x;
    temp->next=NULL;
    (LQ->rear)->next=temp; //新結點入隊列
    LQ->rear=temp; //置新的隊列尾結點
}
//出隊列
OutQueue(LkQue *LQ){
    LkQueNode *temp;
    if (EmptyQueue(CQ)){ //判隊列是否為空
        error(“隊空”); //隊列為空
        return 0;
    }else { //隊列非空
        temp=(LQ->front) ->next; //使 temp 指向隊列的首結點
        (LQ->front) ->next=temp->next; //修改頭結點的指針域指向新的首結點
        if (temp->next==NULL){
            LQ->rear=LQ->front; //無首結點時,front 和 rear 都指向頭結點
        }
        free(temp);
        return 1;
    }
}
//取隊列首元素
DataType GetHead (LkQue LQ){
    LkQueNode *temp;
    if (EmptyQueue(CQ)){
        return NULLData; //判隊列為空,返回空數據標志
    }
    else {
        temp=LQ.front->next;
        return temp->data; //隊列非空,返回隊列首結點元素
    }
}

三、數組

數組:是線性表的推廣,其每個元素由一個值和一組下標組成,其中下標個數稱為數組的維數

一維數組:數組可以看成線性表的一種推廣,一維數組又稱向量,它由一組具有相同類型的數據元素組成,並存儲在一組連續的存儲單元中

多維數組:若一維數組中的數據元素又是一維數組結構,則稱為二維數組;依此類推,若一維數組中的元素又是一個二維數組結構,則稱作三維數組,一般地,一個 n 維數組可以看成元素為 (n-1) 維數組的線性表,多維數組是線性表的推廣

二維數組Amn可以看成是由m個行向量組成的向量,也可以看成是n個列向量組成的向量。

數組一旦被定義,它的維數和維界就不再改變。因此,除了結構的初始化和銷毀之外,數組通常只有兩種基本運算:

  • 讀:給定一組下標,返回該位置的元素內容;
  • 寫:給定一組下標,修改該位置的元素內容。

數組的存儲結構
一維數組元素的內存單元地址是連續的,二維數組可有兩種存儲方法:一種是以列序為主序的存儲;另一種是以行序為主序的存儲。數組元素的存儲位置是下標的線性函數

1. 存儲結構:順序存儲結構
  由於計算機的內存結構是一維的,因此用一維內存來表示多維數組,就必須按某種次序將數組元素排成一列序列,然后將這個線性序列存放在存儲器中;又由於對數組一般不做插入和刪除操作,也就是說,數組一旦建立,結構中的元素個數和元素間的關系就不再發生變化。因此,一般都是采用順序存儲的方法來表示數組。

 

 

 2、矩陣的壓縮存儲

為了節省存儲空間, 我們可以對這類矩陣進行壓縮存儲:即為多個相同的非零元素只分配一個存儲空間;對零元素不分配空間。

特殊矩陣:即指非零元素或零元素的分布有一定規律的矩陣。下面我們討論幾種特殊矩陣的壓縮存儲。

 

 

 

三角矩陣
以主對角線划分,三角矩陣有上三角和下三角兩種。上三角矩陣如圖所示,它的下三角(不包括主對角線)中的元素均為常數。下三角矩陣正好相反,它的主對角線上方均為常數,如圖所示。在大多數情況下,三角矩陣常數為零。

 

 三角矩陣中的重復元素c可共享一個存儲空間,其余的元素正好有n(n+1)/2個,因此,三角矩陣可壓縮存儲到向量s[0..n(n+1)/2]中,其中c存放在向量的最后一個分量中。

稀疏矩陣

什么是稀疏矩陣:簡單說,設矩陣A中有s個非零元素,若s遠遠小於矩陣元素的總數,則稱A為稀疏矩陣。
稀疏矩陣的壓縮存儲: 即只存儲稀疏矩陣中的非零元素。由於非零元素的分布一般是沒有規律的,因此在存儲非零元素的同時,還必須同時記下它所在的行和列的位置(i,j)。
反之,一個三元組(i,j,aij )唯一確定了矩陣A的一個非零元。因此,稀疏矩陣可由表示非零元的三元組及其行列數唯一確定。

 

 稀疏矩陣的三元組順序表表示法——將矩陣中的非零元素化成三元組形式並按行的遞減次序(同行按列的遞增次序)存放在內存中。

 


免責聲明!

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



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