1. 程式人生 > >棧和佇列 C語言實現

棧和佇列 C語言實現

1、棧的概念

棧又稱堆疊,它是一種運算受限的線性表,其限制是僅允許在表的一端進行插入和刪除運算。

2、棧的順序儲存結構和操作實現

棧的順序儲存結構示意圖:


下面通過一個例項展示棧的順序儲存結構的操作實現,其中包含了6種操作:

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;  //定義元素型別
struct StackSq         //定義棧結構型別
{
    ElemType* stack;   //存棧元素的陣列指標
    int top;           //存棧頂元素的下標位置
    int MaxSize;       //存stack陣列長度
};

//棧滿需要重新分配更大空間 的情況
void againMalloc(struct StackSq* S)
{
    ElemType *p = realloc(S->stack, 2*S->MaxSize*sizeof(ElemType));//此處重新分配的空間為原來的2倍
    if (!p)  //重新分配失敗
    {
        printf("儲存空間用完!\n");
        exit(1);
    }
    S->stack = p;             //使list指向新棧空間
    S->MaxSize = 2 * S->MaxSize;
    printf("儲存空間已擴大為當前的2倍!\n");//輸出提示已擴充空間
}

//1、初始化棧S為空
void InitStack(struct StackSq* S, int ms)
{
    if (ms < 0)
    {
        printf("ms的值非法!\n");
        exit(1);
    }
    S->MaxSize = ms;
    S->stack = malloc(ms*sizeof(ElemType));
    if (!S->stack)
    {
        printf("動態儲存分配失敗!\n");
        exit(1);
    }
    S->top = -1;   //值為-1,表示棧空
}

//2、新元素進棧,即把它插入到棧頂
void Push(struct StackSq* S, ElemType x)
{
    if (S->top == S->MaxSize - 1)
        againMalloc(S);
    S->top++;
    S->stack[S->top] = x;
}

//3、刪除棧頂元素並返回值
ElemType Pop(struct StackSq* S)
{
    if (S->top == -1)
    {
        printf("棧空,無元素出棧!\n");
        exit(1);
    }
    S->top--;
    return S->stack[S->top + 1];
}

//4、讀取棧頂元素的值(並不改變棧)
ElemType Peek(struct StackSq* S)
{
    if (S->top == -1)
    {
        printf("棧空,無任何元素!\n");
        exit(1);
    }
    return S->stack[S->top];
}

//5、判斷S是否為空。若是空返回1,否則返回0
int EmptyStack(struct StackSq* S)
{
    if (S->top == -1)
        return 1;
    else
        return 0;
}

//6、清除棧S中的所有元素,釋放動態儲存空間
void ClearStack(struct StackSq* S)
{
    if (S->stack)
    {
        free(S->stack);   //釋放儲存空間
        S->stack = 0;
        S->top == -1;
        S->MaxSize = 0;
    }
}

//主函式
void main()
{void againMalloc(struct List *L)
{
    ElemType *p = realloc(L->list, 2*L->MaxSize*sizeof(ElemType));//此處重新分配的空間為原來的2倍
    if (!p)  //重新分配失敗
    {
        printf("儲存空間用完!\n");
        exit(1);
    }
    L->list = p;             //使list指向新線性表空間
    L->MaxSize = 2 * L->MaxSize;
    printf("儲存空間已擴大為當前的2倍!\n");//輸出提示已擴充空間
}
    struct StackSq s;
    int a[8] = {3, 8, 5, 17, 9, 30, 15, 22};
    int i;
    InitStack(&s, 5);
    for (i = 0; i < 8; i++)
        Push(&s, a[i]);
    printf("%d ", Pop(&s));
    printf("%d \n", Pop(&s));
    Push(&s, 68);
    printf("%d ", Peek(&s));
    printf("%d \n", Pop(&s));
    while (!EmptyStack(&s))
        printf("%d ", Pop(&s));
    printf("\n");
    ClearStack(&s);
}

執行結果:

3、棧的連結儲存結構和操作實現

棧的連結儲存結構與線性表的連結儲存結構相同。

棧的連結儲存結構及操作過程示意圖:


下面通過一個例項展示棧的連結儲存結構的操作,其中包括6種操作

#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;  //定義元素型別
struct sNode           //定義鏈棧結點型別
{
    ElemType data;
    struct sNode* next;
};

//1、初始化鏈棧為空
void InitStack(struct sNode** HS)
{
    *HS = NULL;   //HS表示棧頂指標
}

//2、向鏈棧中插入一個元素
void Push(struct sNode** HS, ElemType x)
{
    struct sNode *newp;
    newp = malloc(sizeof(struct sNode));
    if (newp == NULL)
    {
        printf("記憶體動態空間用完,退出執行!\n");
        exit(1);
    }
    newp->data = x;
    newp->next = *HS;
    *HS = newp;
}

//3、從鏈棧中刪除棧頂元素並返回它
ElemType Pop(struct sNode** HS)
{
    struct sNode* p;
    ElemType temp;
    if (*HS == NULL)
    {
        printf("棧空無法刪除!\n");
        exit(1);
    }
    p = *HS;
    *HS = p->next;
    temp = p->data;
    free(p);
    return temp;
}

//4、讀取棧頂元素
ElemType Peek(struct sNode** HS)
{
    if (*HS == NULL)
    {
        printf("棧空,無棧頂結點!\n");
        exit(1);
    }
    return (*HS)->data;
}

//5、檢查鏈棧是否為空
int EmptyStack(struct sNode** HS)
{
    if (*HS == NULL)
        return 1;
    else
        return 0;
}

//6、清除鏈棧為空
void ClearStack(struct sNode** HS)
{
    struct sNode *cp, *np;
    cp = *HS;
    while (cp != NULL)
    {
        np = cp->next;
        free(cp);
        cp = np;
    }
    *HS = NULL;
}

//主函式
void main()
{
    struct sNode *a;
    int x = 0;
    int m[8] = {3, 8, 5, 17, 9, 30, 15, 22};
    printf("當前序列:3,8,5,17,9,30,15,22\n");
    InitStack(&a);
    while (x != 8)
    {
        Push(&a, m[x]);
        x++;
    }
    printf("逆序列為:");
    while (!EmptyStack(&a))
        printf("%d,", Pop(&a));
    printf("\n");
    ClearStack(&a);
}

執行結果:

4、佇列概念

佇列也是一種運算受限的線性表,其限制是僅允許在表的一端進行插入(隊尾),在表的另一端進行刪除(隊首)。

5、佇列的順序儲存結構和操作實現(迴圈列隊)

如上圖,當佇列處於圖(d)的狀態時不可再插入元素,不然就越界了,但實際佇列空間並未佔滿,為了解決這個問題,就有了迴圈佇列,如下圖:

圖片展示的很詳細,為了區分隊空和隊滿,我們採取少用一個元素空間,約定以“對頭指標在隊尾指標的下一位置(指環狀的下一位置)上”作為佇列滿狀態的標誌。

                                  空佇列時:Q.front = Q.rear = 0;

                                   一般情況時:入隊一個元素,尾指標後移一次,出隊一個元素,頭指標後移一次。

                                   佇列滿時:( Q.rear + 1 ) % MaxSize == Q.front (如圖中的情況為:(5+1)% 6 == 0)

注意,當情況如下時:


插入a1元素時,Q.rear後移一個位置,但不是直接加1,比如上圖 : 5+1=6,但實際位置應為0,所以必須寫成:

Q.rear = ( Q.rear + 1) % MaxSize ;

刪除元素時的情況同理。

注意:關於記憶體不足時擴大記憶體的情況,若Q.rear = Q.MaxSize - 1 時,即 Q.rear 為 5 時,直接擴大記憶體即可,但當Q.rear != Q.MaxSize - 1 時,如下圖:(Q.rear,2)


此時,原來位置為0、1位置的元素現在變成了位置為6、7的元素了,而且隊尾指標由原來的2變成了8,所以記憶體擴大後應該進行操作(後面有詳細講解):

        for (i = 0; i < Q->rear; i++)
            Q->queue[i + Q->MaxSize] = Q->queue[i];
        Q->rear += Q->MaxSize;

具體情況見下面的程式程式碼。

下面通過一個例項展示佇列順序儲存的操作,包含6種操作

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
struct QueueSq
{
    ElemType *queue;   //指向儲存佇列的陣列空間
    int front, rear;   //隊首指標、隊尾指標
    int MaxSize;       //queue陣列長度
};

//擴充套件儲存空間函式
void againMalloc(struct QueueSq* Q)
{
    ElemType *p;
    p = realloc(Q->queue, 2*Q->MaxSize*sizeof(ElemType));
    if (!p)
    {
        printf("儲存空間用完!\n");
        exit(1);
    }
    Q->queue = p;     //使queue指向新的佇列空間
    if (Q->rear != Q->MaxSize - 1)
    {
        int i;
        for (i = 0; i < Q->rear; i++)                                
            Q->queue[i + Q->MaxSize] = Q->queue[i];   //原佇列的尾部內容後移MaxSize個位置
        Q->rear += Q->MaxSize;                                    //隊尾指標後移MaxSize個位置
    }
    Q->MaxSize = 2*Q->MaxSize;                                //佇列空間修改為原來的2倍
    printf("儲存空間已為當前2倍!\n");
}
//1、初始化佇列
void InitQueue(struct QueueSq* Q, int ms)
{
    if (ms <= 0)
    {
        printf("ms值非法!\n");
        exit(1);
    }
    Q->MaxSize = ms;
    Q->queue = malloc(ms*sizeof(ElemType));
    if (!Q->queue)
    {
        printf("記憶體空間用完!\n");
        exit(1);
    }
    Q->front = Q->rear = 0;    //置佇列為空
}

//2、向佇列插入元素
void EnQueue(struct QueueSq* Q, ElemType x)
{
    if ((Q->rear + 1) % Q->MaxSize == Q->front)  //隊空間滿
        againMalloc(Q);
    Q->queue[Q->rear] = x;                       //插入值
    Q->rear = (Q->rear + 1) % Q->MaxSize;        //隊尾指標後移一個位置
}

//3、從佇列中刪除元素並返回
ElemType OutQueue(struct QueueSq* Q)
{
    ElemType temp;
    if (Q->front == Q->rear)
    {
        printf("佇列已空,無法刪除!\n");
        exit(1);
    }
    temp = Q->queue[Q->front];                  //儲存隊首元素值
    Q->front = (Q->front + 1) % Q->MaxSize;     //使隊首後移一個位置
    return temp;
}

//4、讀取隊首元素,不改變佇列狀態
ElemType PeekQueue(struct QueueSq* Q)
{
    if (Q->front == Q->rear)
    {
        printf("佇列已空,無法讀取!\n");
        exit(1);
    }
    return Q->queue[Q->front];
}

//5、檢查一個佇列是否為空,若是則返回1,否則返回0
int EmptyQueue(struct QueueSq* Q)
{
    if (Q->front == Q->rear)
        return 1;
    else
        return 0;
}

//6、清除一個佇列為空,釋放動態儲存空間
void ClearQueue(struct QueueSq* Q)
{
    if (Q->queue != NULL)
    {
        free(Q->queue);
        Q->queue = 0;
        Q->front = Q->rear = 0;
        Q->MaxSize = 0;
    }
}

//主函式
void main()
{
    struct QueueSq q;
    int a[8] = {3,8,5,17,9,30,15,22};
    int i;
    InitQueue(&q, 5);
    for (i = 0; i < 8; i++)
        EnQueue(&q, a[i]);
    printf("%d ", OutQueue(&q));
    printf("%d \n", OutQueue(&q));
    EnQueue(&q, 68);
    printf("%d ", PeekQueue(&q));
    printf("%d \n", OutQueue(&q));
    while (!EmptyQueue(&q))
        printf("%d ", OutQueue(&q));
    printf("\n");
    ClearQueue(&q);
}

執行結果:

為驗證和詳解上面提到的記憶體擴大問題,我們把上面的主函式換為如下:

void main()
{
    struct QueueSq q;
    struct QueueSq* p = &q;
    int a[5] = {3,8,5,17,9};
    int i;
    InitQueue(&q, 6);
    for (i = 0; i < 5; i++)
        EnQueue(&q, a[i]);
    printf("%d ", OutQueue(&q));
    printf("%d ", OutQueue(&q));
    printf("%d \n", OutQueue(&q));
    EnQueue(&q, 67);
    printf("插入了67!\n");
    EnQueue(&q, 68);
    printf("插入了68!\n");
    EnQueue(&q, 69);
    printf("插入了69!\n");
    EnQueue(&q, 100);
    printf("插入了100!\n");
    printf("%d ", PeekQueue(&q));          //顯示隊頭元素
    printf("\n");
    for (i = 0; i < 12; i++)
        printf("%d ,", p->queue[i]);     //顯示所有元素
    printf("\n");
    while (!EmptyQueue(&q))
        printf("%d ", OutQueue(&q));
    printf("\n");
    ClearQueue(&q);
}

執行結果:

分析:先是依次入隊 3,8,5,17,9 五個元素,然後出隊3,8,5三個元素,再然後入隊67,68,69三個元素,此時狀態如下圖的左圖,

注意:出隊的元素雖然不在隊裡,但在記憶體裡仍存在,如圖中的暗綠色的元素。

下面當要將100入隊時,記憶體空間不夠,擴大為原來的2倍,此時隊中的尾端位置將發生變化如下右圖,(暗綠色元素並不在隊裡)

此時的隊頭位置元素仍為 17,一次輸出下標從0到11位置的記憶體內容:如上面的執行結果所示,68,69,5仍存在,下標6,7,8三個位置為別是68,69,100,後面的空間無值。

將隊的所有元素出隊,結果即為上圖的執行結果中的:17,9,67,68,69,100。

至此迴圈順序佇列的相關內容已經非常清晰了。

6、佇列的連結儲存結構和操作實現

鏈隊的示意圖如下:

下面通過例項展示鏈隊的6種操作

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
struct sNode             //結點型別
{
    ElemType data;       //值域
    struct sNode* next;  //連結指標域
};
struct QueueLk           //佇列連結儲存結構型別
{
    struct sNode* front; //隊首指標
    struct sNode* rear;  //隊尾指標
};

//1、初始化鏈隊
void InitQueue(struct QueueLk* HQ)
{
    HQ->front = HQ->rear = NULL;
}

//2、向鏈隊中插入元素
void EnQueue(struct QueueLk* HQ, ElemType x)
{
    struct sNode* newp;
    newp = malloc(sizeof(struct sNode));
    if (newp == NULL)
    {
        printf("記憶體動態空間用完,退出執行!\n");
        exit(1);
    }
    newp->data = x;
    newp->next = NULL;
    if (HQ->front == NULL)  //若鏈隊為空,則新節點既是隊首結點又是隊尾結點
        HQ->front = HQ->rear = newp;
    else       //若鏈隊非空,則依次修改隊尾結點的指標域和隊尾指標,使之都指向新的隊尾結點
        HQ->rear = HQ->rear->next = newp;
}

//3、從鏈隊中刪除元素並返回
ElemType OutQueue(struct QueueLk* HQ)
{
    struct sNode* p;
    ElemType temp;
    if (HQ->front == NULL)
    {
        printf("佇列為空,無法刪除!\n");
        exit(1);
    }
    temp = HQ->front->data;
    p = HQ->front;
    HQ->front = p->next; //使隊首指標指向下一個結點
    if (HQ->front == NULL) //若刪除後隊空,則使隊尾指標也變為空
        HQ->rear = NULL;
    free(p);
    return temp;
}

//4、讀取隊首元素,不改變佇列狀態
ElemType PeekQueue(struct QueueLk* HQ)
{
    if (HQ->front == NULL)
    {
        printf("佇列已空,無法讀取!\n");
        exit(1);
    }
    return HQ->front->data;
}

//5、檢查一個鏈隊是否為空,若是則返回1,否則返回0
int EmptyQueue(struct QueueLk* HQ)
{
    if (HQ->front == NULL)
        return 1;
    else
        return 0;
}

//6、清除鏈隊為空,釋放動態儲存空間
void ClearQueue(struct QueueLk* HQ)
{
   struct sNode* p = HQ->front;
   while (p != NULL)  //依次刪除佇列中的每一個結點,迴圈結束後隊首指標已變為空
   {
        HQ->front = HQ->front->next;
        free(p);
        p = HQ->front;
   }
   HQ->rear = NULL;  //置隊尾指標為空
}

//主函式
void main()
{
    struct QueueLk q;
    int a[8] = {3,8,5,17,9,30,15,22};
    int i;
    InitQueue(&q);
    for (i = 0; i < 8; i++)
        EnQueue(&q, a[i]);
    printf("%d ", OutQueue(&q));
    printf("%d \n", OutQueue(&q));
    EnQueue(&q, 68);
    printf("%d ", PeekQueue(&q));
    printf("%d \n", OutQueue(&q));
    while (!EmptyQueue(&q))
        printf("%d ", OutQueue(&q));
    printf("\n");
    ClearQueue(&q);
}


執行結果同迴圈佇列相同,只是鏈隊無需考慮記憶體空間的大小。


相關推薦

佇列 C語言實現

1、棧的概念 棧又稱堆疊,它是一種運算受限的線性表,其限制是僅允許在表的一端進行插入和刪除運算。 2、棧的順序儲存結構和操作實現 棧的順序儲存結構示意圖: 下面通過一個例項展示棧的順序儲存結構的操作實現,其中包含了6種操作: #include<stdio.h>

順序的基本操作(入)及C語言實現詳解

棧,可以理解為遵循“後入先出”原則的線性表,因此棧結構可以採用順序表或連結串列實現。 順序棧的實現採用的是順序表,也就是陣列。 順序棧的實現思想是:在陣列中設定一個隨時指向棧頂元素的變數(一般命名為 top ),當 top 的值為 -1 時,說明陣列中沒有資料,即棧中沒有資料元素,為“空棧”;只要資料元素

的基本操作(入)及C語言實現完全攻略

鏈棧,即用線性表的連結串列結構實現棧的功能。實現過程中,鏈棧不需要建立頭結點,增加頭結點反而會增加程式的複雜性,因此連結串列中只需要建立一個頭指標就可以了。 鏈棧的實現思想是:用連結串列頭結點的一端作為棧的棧頂端,這樣做的好處是當資料元素壓棧或者彈棧時,直接使用頭指標就可以完成,不需要增設額外的指標。 例

利用實現佇列(C語言實現)

在上一篇 中,雖然我們對佇列的時間複雜度進行了優化,但是卻讓程式碼的可讀性變差了,程式碼顯得略微臃腫(當然,這些話你看看就好,主要是為了奉承這篇博文的)。 這裡主要實現的是:利用棧來實現佇列 基本思路: 1,建立兩個棧 2,兩個棧合併起來組裝成一個佇列,分別取名為insta

向量、連結串列、佇列的基本實現

目錄 一、一點感想 二、程式碼 1、Vector 2、 List 3、Stack 4、Queue 三、不足之處 一、一點感想 自學資料結構和演算法到現在時間也不短了,一直猶豫著要不要寫一寫向量等幾個最最基本的資料結構,因為總覺得是最基本的,

軟考:資料結構基礎——迴圈佇列C語言實現

  迴圈佇列得實現: 1.     在入隊和出隊時,我們通過      q->rear = (q->rear +1)%MAX_LENTH 來實現迴圈入隊     q

佇列的結構實現

棧 棧(stack),有些地方稱為堆疊,是一種容器,可存入資料元素、訪問元素、刪除元素,它的特點在於只能允許在容器的一端(稱為棧頂端指標,英語:top)進行加入資料(英語:push)和輸出資料(英語:pop)的運算。沒有了位置概念,保證任何時候可以訪問、刪除的元素都是此前最後存入的那個元素,確定

1. 佇列的陣列實現

用陣列實現一個棧 棧頂元素下標index與其對應的陣列元素下標f(index)之間的對映關係是怎樣的?隨著棧的增長,棧頂元素下標依次為0,1,2...n-1,其中n表示棧的最大高度。我們需要制定一種對映規則f,方便我們通過index計算出f(index)。而最簡單的規則就是f(index) = inde

資料結構之帶優先順序的佇列(C語言實現)

#include"PQueue.h" int main() {PNode head;InitPQueue(&head);Item item = { 3, 6 };Push(&head, item);item.data = 5;item.prio = 3;Push(&head, item

順序表的建立翻轉 C語言實現

 要把一個順序表就地逆置(輔助空間為o(1)),可以將表中的開始結點與終端結點互換,第二個結點和倒數第二個結點互換,如此反覆,就可以將整個表逆置了。 #include<stdio.h> #define ListSize 20 typedef int DataTy

資料結構---佇列C語言實現

#include <stdio.h> #include <stdlib.h> //佇列大小 #define SIZE 1024 static int queue[SIZ

表示式計算器(逆波蘭法)操作(C語言實現

可能很多的同學在學資料結構的時候。說到棧,都會有一道很經典的題目,那就是用棧來實現計算器。我們都知道普通的計算寫起來是很簡單的,但是如果涉及到左右括號以及加減乘除組成的運算式的時候則寫起程式時便不那麼容易了。 比如:(1+(2*(1+3)/2)+10)

資料結構之佇列C語言實現

C語言實現迴圈佇列:實現佇列需要理解先進先出的思想,可以先看一下資料機構的書籍,不做過多累述定義為順序表形式。typedef struct{ Elemtype data[MaxSize]; int front,rear; }Queue;MaxSize表示佇列最大值,其中f

C語言實現佇列(佇列的基本操作)

棧: 棧:棧(stack)又名堆疊,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。 特點:先進後出 stack.h #pragma once #include <stdio.h> #include <

佇列:2.佇列(Queue)及其C語言實現

佇列是線性表的一種,在操作資料元素時,和棧一樣,有自己的規則:使用佇列存取資料元素時,資料元素只能從表的一端進入佇列,另一端出佇列,如圖1。 圖1 佇列示意圖 稱進入佇列的一端為“隊尾”;出佇列的一端為“隊頭”。資料元素全部由隊尾陸續進佇列,由隊頭陸續出佇列。 佇

數據結構11: (Stack)的概念應用及C語言實現

next ret 額外 轉換 lib 順序存儲 順序棧 就是 函數 棧,線性表的一種特殊的存儲結構。與學習過的線性表的不同之處在於棧只能從表的固定一端對數據進行插入和刪除操作,另一端是封死的。 圖1 棧結構示意圖 由於棧只有一邊開口存取數據,稱開口的那一端

順序c語言實現OOP實現

目錄 C語言實現順序棧和OOP實現順序棧 1,順序棧 2,C語言實現順序棧 3,用OOP實現一個順序棧 C語言實現順序棧和OOP實現順序棧 1,順序棧 (1)定義:棧(stack)又名堆疊,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除

資料結構(C語言版 嚴蔚敏著)——佇列

棧的定義: · 書本定義:棧是一個後進先出的線性表,它只要求只在表尾 進行刪除和插入操作。 · 通俗定義:棧就是一個特殊的線性表(順序表,連結串列),操作上有一些特殊性:     -棧的元素必須“後進先出”。     -棧的操作只能在這個線性表的表尾進行。    

C++實現佇列(Linux環境)

Stack.h #pragma once #include<iostream> using namespace std; #include<assert.h> //靜態棧 te

資料結構——入,出佇列相關操作(C語言實現

閱讀過程之中可能會花費比較多的時間:建議直接翻到最後,有完整的程式碼可以使用 程式準備工作 #include <stdio.h> #include <malloc.h> #include <stdlib.h> #include<proc