數(shù)據(jù)結(jié)構(gòu)與算法--棧和隊列

棧和隊列是兩種重要的數(shù)據(jù)結(jié)構(gòu)减拭,棧和隊列是特殊的線性表蔽豺。

1、棧(stack)是限定僅在表的一端進行插入或刪除操作的線性表拧粪。通常稱插入修陡、刪除的這一端為棧頂(top),另一端為棧底(bottom)可霎。棧操作的特點是先進后出(后進先出)魄鸦。

  • 棧(順序棧)的數(shù)據(jù)結(jié)構(gòu)定義:
 //棧的順序存儲表示(順序棧)
#define STACK_INIT_SIZE 10 // 存儲空間初始分配量
#define STACK_INCREMENT 2 // 存儲空間分配增量
struct SqStack
 {
     SElemType *base; // 在棧構(gòu)造之前和銷毀之后,base的值為NULL
     SElemType *top; // 棧頂指針
     int stacksize; // 當前已分配的存儲空間癣朗,以元素為單位
 };

下圖展示了順序棧中的數(shù)據(jù)元素和top指針之間的對應關(guān)系:

image

top為棧頂指針拾因,其初始值指向棧底,即top=base。每當插入棧頂元素時绢记,元素數(shù)據(jù)儲存在當前top指針所指位置扁达,然后top指針增加1個位置;刪除棧頂元素時蠢熄,返回數(shù)據(jù)等于*--top跪解,即指針top減1。因此签孔,非空棧的top指針始終在棧頂元素的下一個位置上惠遏。

  • 棧(順序棧)的基本操作

構(gòu)造一個空棧

void InitStack(SqStack &S)
 {
   if(!(S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType))))
     exit(OVERFLOW); // 存儲分配失敗
   S.top=S.base;
   S.stacksize=STACK_INIT_SIZE;
 }

入棧

// 插入元素e為新的棧頂元素
void Push(SqStack &S,SElemType e)
 {
     if(S.top-S.base>=S.stacksize) // 棧滿,追加存儲空間
     {
         S.base=(SElemType *)realloc(S.base,(S.stacksize+STACK_INCREMENT)*sizeof(SElemType));
         if(!S.base)
             exit(OVERFLOW); // 存儲分配失敗
         S.top=S.base+S.stacksize;
         S.stacksize+=STACK_INCREMENT;
     }
     *(S.top)++=e;
 }

出棧

// 若棧不空骏啰,則刪除S的棧頂元素,用e返回其值抽高,并返回OK判耕;否則返回ERROR
Status Pop(SqStack &S,SElemType &e)
{
  if(S.top==S.base)
      return ERROR;
  e=*--S.top;
  return OK;
}

2、隊列(queue)是限定在一端(隊尾)進行插入翘骂,另一端(隊頭)進行刪除的線性表壁熄。隊列操作的特點是先進先出(后進后出)。

  • 鏈隊列 用鏈表表示的隊列簡稱鏈隊列碳竟。其含有指向頭結(jié)點的頭指針和指向尾元素的尾指針草丧。

鏈隊列結(jié)構(gòu)圖如下:

image

結(jié)構(gòu)定義如下:

// 單鏈隊列--隊列的鏈式存儲結(jié)構(gòu)
 typedef struct QNode
 {
   QElemType data;
   QNode *next;
 }*QueuePtr;

 struct LinkQueue
 {
     QueuePtr front,rear; // 隊頭、隊尾指針
 };

主要操作算法:
構(gòu)造一個空隊列Q

void InitQueue(LinkQueue &Q)
{
   if(!(Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode))))
     exit(OVERFLOW);
   Q.front->next=NULL;
}

入隊

// 入隊莹桅,插入元素e為Q的新的隊尾元素
void EnQueue(LinkQueue &Q,QElemType e)
{
    QueuePtr p;
    if(!(p=(QueuePtr)malloc(sizeof(QNode)))) // 存儲分配失敗
     exit(OVERFLOW);
    p->data=e;
    p->next=NULL;
    Q.rear->next=p;
    Q.rear=p;
}

出隊

// 出隊昌执,若隊列不空,刪除Q的隊頭元素诈泼,用e返回其值懂拾,并返回OK,否則返回ERROR
Status DeQueue(LinkQueue &Q,QElemType &e)
{
   QueuePtr p;
   if(Q.front==Q.rear)
      return ERROR;
   p=Q.front->next;
   e=p->data;
   Q.front->next=p->next;
   if(Q.rear==p)
       Q.rear=Q.front;
   free(p);
   return OK;
 }
  • 循環(huán)隊列

在介紹循環(huán)隊列前铐达,先了解下順序隊列岖赋。順序隊列的結(jié)構(gòu)如下圖:

image

用一組地址連續(xù)的存儲單元依次存放從隊頭到隊尾的元素。Q.front為指向隊頭的序號瓮孙,Q.rear指向隊尾唐断。當初始化隊列時Q.front=Q.rear=整型0。每當插入新的隊尾元素時杭抠,“隊尾指針”(Q.rear)加 1 脸甘;每當從隊頭刪除元素時,“隊頭指針” (Q.front)減 1 祈争。因此在非空隊列中斤程,隊頭元素等于 Q[Q.front],隊尾元素的下一個位置為 Q.rear 。

假設忿墅,當前隊列存儲最大空間為6扁藕。當隊列處于上圖的(d)狀態(tài)是不能再存儲新的元素,而此時動態(tài)增加存儲空間是不合適的疚脐,因為隊列的實際可用空間并未用滿(之前刪除隊列元素時挪出了新的空間)亿柑。一個巧妙的辦法是將順序隊列想象成一個環(huán)狀的空間。

image

在隊尾增加新的元素時有Q.rear = (Q.rear+1)%maxsize棍弄。另外為了區(qū)分空隊列和滿隊列的判斷條件(空隊列為Q.front=Q.rear)望薄,我們約定當隊列“頭指針”在“尾指針”下一個位置時,認為隊列已滿呼畸。即當(Q.rear+1)%maxsize等于Q.front時認為隊列已滿(最后一個存儲空間不能存元素)痕支。這樣,有一個存儲節(jié)點是不能存數(shù)據(jù)的蛮原,隊列的最大存儲元素的個數(shù)為 maxsize - 1卧须。

下面是循環(huán)隊列的存儲結(jié)構(gòu):

// c3-4.h 隊列的順序存儲結(jié)構(gòu)(元素出隊時不移動元素,只改變隊頭元素的位置)
#define QUEUE_INIT_SIZE 10 // 隊列存儲空間的初始分配量
#define QUEUE_INCREMENT 2 // 隊列存儲空間的分配增量
struct SqQueue2
 {
     QElemType *base; // 初始化的動態(tài)分配存儲空間
     int front; // 頭指針儒陨,若隊列不空,指向隊列頭元素
   int rear; // 尾指針花嘶,若隊列不空,指向隊列尾元素的下一個位置
   int queuesize; // 當前分配的存儲容量(以sizeof(QElemType)為單位)
 };

主要操作算法:
構(gòu)造一個空隊列Q

void InitQueue(SqQueue2 &Q)
 {
   if(!(Q.base=(QElemType *)malloc(QUEUE_INIT_SIZE*sizeof(QElemType)))) // 存儲分配失敗
     exit(ERROR);
   Q.front=Q.rear=0;
   Q.queuesize=QUEUE_INIT_SIZE;
 }

入隊

// 插入元素e為Q的新的隊尾元素
void EnQueue(SqQueue2 &Q,QElemType e)
{
  int i;
  if((Q.rear+1)%Q.queuesize==Q.front)
  { // 隊列滿,增加存儲單元
    Q.base=(QElemType *)realloc(Q.base,(Q.queuesize+QUEUE_INCREMENT)*sizeof(QElemType));
    if(!Q.base) // 增加單元失敗
        exit(ERROR);
    if(Q.front>Q.rear) // “頭指針”大于“尾指針”蹦漠,比如front=3 rear=2
    {
        // 移動高端元素到新的高端
        //比如   [102,103,null,4,5,6,7,8,9,10,101,null,null],
        //移完后為[102,103,null,null,null,4,5,6,7,8,9,10,101],
        for(i=Q.queuesize-1;i>=Q.front;i--)
           Q.base[i+QUEUE_INCREMENT]=Q.base[i];
        Q.front+=QUEUE_INCREMENT; // 移動隊頭指針
    }
    Q.queuesize+=QUEUE_INCREMENT; // 增加隊列長度
  }
  Q.base[Q.rear]=e; // 將e插入隊尾
  Q.rear=++Q.rear%Q.queuesize; // 移動隊尾指針
}

出隊

// 若隊列不空椭员,則刪除Q的隊頭元素,用e返回其值笛园,并返回OK隘击;否則返回ERROR
Status DeQueue(SqQueue2 &Q,QElemType &e)
{
  if(Q.front==Q.rear) // 隊列空
      return ERROR;
  e=Q.base[Q.front]; // 用e返回隊頭元素
  Q.front=++Q.front%Q.queuesize; // 移動隊頭指針
  return OK;
}
  • 鏈隊列和循環(huán)隊列對比:循環(huán)隊列適合能預估隊列長度的情況,這種情況時間效率較高研铆。鏈隊列比較靈活闸度,但每次出隊或者入隊都需要釋放或者申請節(jié)點空間,效率較低蚜印。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末莺禁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子窄赋,更是在濱河造成了極大的恐慌哟冬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件忆绰,死亡現(xiàn)場離奇詭異浩峡,居然都是意外死亡,警方通過查閱死者的電腦和手機错敢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門翰灾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缕粹,“玉大人,你說我怎么就攤上這事纸淮∑秸叮” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵咽块,是天一觀的道長绘面。 經(jīng)常有香客問我,道長侈沪,這世上最難降的妖魔是什么揭璃? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮亭罪,結(jié)果婚禮上瘦馍,老公的妹妹穿的比我還像新娘。我一直安慰自己应役,他們只是感情好扣墩,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扛吞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荆责。 梳的紋絲不亂的頭發(fā)上滥比,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天,我揣著相機與錄音做院,去河邊找鬼盲泛。 笑死,一個胖子當著我的面吹牛键耕,可吹牛的內(nèi)容都是我干的寺滚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼屈雄,長吁一口氣:“原來是場噩夢啊……” “哼村视!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酒奶,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蚁孔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后惋嚎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杠氢,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年另伍,在試婚紗的時候發(fā)現(xiàn)自己被綠了鼻百。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖温艇,靈堂內(nèi)的尸體忽然破棺而出因悲,到底是詐尸還是另有隱情,我是刑警寧澤中贝,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布囤捻,位于F島的核電站,受9級特大地震影響邻寿,放射性物質(zhì)發(fā)生泄漏蝎土。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一绣否、第九天 我趴在偏房一處隱蔽的房頂上張望誊涯。 院中可真熱鬧,春花似錦蒜撮、人聲如沸暴构。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽取逾。三九已至,卻和暖如春苹支,著一層夾襖步出監(jiān)牢的瞬間砾隅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工债蜜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晴埂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓寻定,卻偏偏與公主長得像儒洛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子狼速,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容