棧和隊列可看作是特殊的線性表,它們是運算受限的線性表
一、棧
棧:棧是只能在表的一端(表尾)進行 插入和刪除的線性表;允許插入及刪除的一端(表尾)稱為棧頂(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的一個非零元。因此,稀疏矩陣可由表示非零元的三元組及其行列數唯一確定。
稀疏矩陣的三元組順序表表示法——將矩陣中的非零元素化成三元組形式並按行的遞減次序(同行按列的遞增次序)存放在內存中。