第3章_堆棧和隊列

堆棧和隊列都是特殊的線性表。線性表、堆棧和隊列三者的數據元素以及數據元素間的邏輯關係完全相同,差別是線性表的插入和刪除操作不受限制,而堆棧只能在棧頂插入和刪除,隊列只能在隊尾插入在隊頭刪除。堆棧和隊列都可以分別用順序存儲結構和鏈式存儲結構。順序隊列通常採用順序循環隊列方法實現,因爲順序循環隊列可以避免順序隊列的“假溢出”問題。
堆棧和隊列在各種類型的軟件中應用十分廣泛,堆棧可以用來完成數據序列的特定轉換,隊列可以用作 數據元素序列的緩衝存儲。

順序堆棧

SeqStack.h

//順序堆棧結構體定義
typedef struct 
{
    DataType stack[MaxStackSize];
    int top;
}SeqStack;

//1.初始化
void StackInitiate(SeqStack*S)
{
    S->top = 0;
}

//2.判斷是否爲空
int StackNotEmpty(SeqStack S)
{
    //判斷順序堆棧S非空否,非空返回1,否則返回0
    if(S.top <= 0) return 0;
    else return 1;
}

//3.入棧
int StackPush(SeqStack*S,DataType x)
{
    //把數據元素值x壓入順序堆棧S,入棧成功返回1,否則返回0
    if(S->top >= MaxStackSize)
    {
        printf("堆棧已滿無法插入!\n");
        return 0;
    }
    else
    {
        S->stack[S->top] = x;
        S->top++;
        return 1;
    }
}

//4.出棧
int StackPop(SeqStack*S,DataType*d)
{
    //彈出順序堆棧S的棧頂數據元素值到參數d,出棧成功返回1,否則返回0
    if(S->top <= 0)
    {
        printf("堆棧已空無數據元素出棧!\n");
        return 0;
    }
    else
    {
        S->top--;
        *d = S->stack[S->top];
        return 1;
    }
}

//5.取棧頂數據元素
int StackTop(SeqStack S,DataType*d)
{
    if(S.top <= 0)
    {
        printf("堆棧已空!\n");
        return 0;
    }
    else
    {
        *d = S.stack[S.top - 1];
        return 1;
    }
}

main.c

#include<stdio.h>               //該文件包含printf()函數
#include<stdlib.h>              //該文件包含exit()函數
#define MaxStackSize 100        //定義MaxStackSize 爲100
typedef int DataType;           //定義DataType爲int數據類型
#include "SeqStack.h"

void main(void)
{
    SeqStack myStack;
    int i,x;

    StackInitiate(&myStack);
    for(i = 0;i < 10; i++)
    {
        if(StackPush(&myStack,i + 1) == 0)  //入棧10個數據元素
        {
            printf("錯誤!\n");
            return ;
        }
    }

    if(StackTop(myStack,&x) == 0)       //取棧頂數據元素
    {
        printf("錯誤!\n");
        return ;
    }
    else
    {
        printf("當前棧頂數據元素爲:%d\n",x);
    }

    printf("依次出棧的數據元素序列如下:\n");
    while(StackNotEmpty(myStack))
    {
        StackPop(&myStack,&x);
        printf("%d ",x);
    }
    printf("\n");

}
/*
當前棧頂數據元素爲:10
依次出棧的數據元素序列如下:
10 9 8 7 6 5 4 3 2 1
Press any key to continue
*/

這裏寫圖片描述

鏈式堆棧


typedef struct snode
{
    DataType data;
    struct snode *next;
}LSNode;

//1.初始化
void StackInitiate(LSNode** head)
{
    if((*head = (LSNode*)malloc(sizeof(LSNode))) == NULL) exit(1);
    (*head)->next = NULL;
}

//2.判空
int StackNotEmpty(LSNode*head)
{
    //判斷堆棧是否非空,非空返回1;空返回0
    if(head->next == NULL) return 0;
    else return 1;
}

//3.入棧
int StackPush(LSNode*head,DataType x)
{
    //把數據元素x插入鏈式堆棧head的棧頂作爲新的棧頂
    LSNode*p;
    if((p = (LSNode*)malloc(sizeof(LSNode))) == NULL)
    {
        printf("內存空間不中無法插入!\n");
        return 0;
    }

    p->data = x;            
    p->next = head->next;   //新結點鏈入棧頂
    head->next = p;         //新結點成爲新的棧頂
    return 1;
}

//4.出棧
int StackPop(LSNode*head,DataType* d)
{
    LSNode*p = head->next;
    if(p == NULL)
    {
        printf("堆棧已空出錯!\n");
        return 0;
    }

    head->next = p->next;   //刪除原棧頂結點
    *d = p->data;           //原棧頂結點元素賦予d
    free(p);                //釋放原棧頂結點內存空間
    return 1;
}

//5.取棧頂數據元素
int StackTop(LSNode*head,DataType*d)
{
    //取棧頂元素並把棧頂元素由參數d帶回
    LSNode *p = head->next;
    if(p == NULL)
    {
        printf("堆棧已空出錯!");
        return 0;
    }
    *d = p->data;
    return 1;
}

//6.撤消動態申請空間
void Destroy(LSNode*head)
{
    LSNode*p,p1;
    p = head;
    while(p != NULL)
    {
        p1 = p;
        p = p->next;
        free(p1);
    }
}
#include<string.h>
#include<stdio.h>               //該文件包含printf()函數
#include<stdlib.h>              //該文件包含exit()函數
#define MaxStackSize 100
typedef char DataType;
#include "SeqStack.h"

void ExpIsCorrect(char exp[],int n);

void main(void)
{
    char a[] = "(())abc{]()}";  //左右括號配對次序不正確
    char b[] = "(()))abc{[]}";  //右括號多於左括號
    char c[] = "(()()abc{[]}";  //左括號多於右括號
    char d[] = "(())abc{[]}";   //左右括號配匹正確

    int n1 = strlen(a);
    int n2 = strlen(b);
    int n3 = strlen(c);
    int n4 = strlen(d);

    ExpIsCorrect(a,n1);
    ExpIsCorrect(b,n2);
    ExpIsCorrect(c,n3);
    ExpIsCorrect(d,n4);

}

/************************************************************************/
/* 
1.假設一個算術表達式的包含圓 括號、方括號和花括號三種類型的括號,編寫一個判
別表達式中括號是否正確配對的函數,並設遍地開花個測試主函數。
算法思想:
算術表達式中右括號和左括號匹配的次序正好符 合後到的括號要最先被匹配的“後進先出”
堆棧操作特點,因此,可以藉助一個堆棧來進行判斷。
括號匹配共有4種情況:
a.左右括號配對次序不正確;
b.右括號多於左括號
c.左括號多於右括號
d.左右括號匹配正確
*/
/************************************************************************/
void ExpIsCorrect(char exp[],int n)
{
    //判斷有n個字符的字符串exp左右括號是否配對正確
    SeqStack myStack;       //定義鏈式堆棧
    int i;
    char c;

    StackInitiate(&myStack);
    for(i = 0;i < n; i++)
    {
        if((exp[i] == '(')  || (exp[i] == '[') || (exp[i] == '{')) 
        {
            StackPush(&myStack,exp[i]);
        }
        else if(exp[i] == ')' && StackNotEmpty(myStack) 
            && StackTop(myStack,&c) && c == '(')
        {
            StackPop(&myStack,&c);      //出棧
        }
        else if(exp[i] == ')' && StackNotEmpty(myStack) && StackTop(myStack,&c) && c != '(')
        {
            printf("左右括號配對次序不正確!\n");
            return ;
        }
        else if(exp[i] == ']' && StackNotEmpty(myStack) && StackTop(myStack,&c) && c == '[')
        {
            StackPop(&myStack,&c);
        }
        else if(exp[i] == ']' && StackNotEmpty(myStack) && StackTop(myStack,&c) && c != '[')
        {
            printf("左右括號配對次序不正確!\n");
            return ;
        }
        else if(exp[i] == '}' && StackNotEmpty(myStack) && StackTop(myStack,&c) && c == '{')
        {
            StackPop(&myStack,&c);  //出棧
        }
        else if(exp[i] == '}' && StackNotEmpty(myStack) && StackTop(myStack,&c) && c != '{')
        {
            printf("左右括號配對次序不正確!\n");
            return ;
        }
        else if(((exp[i] == ')') || (exp[i] == ']') || exp[i] == '}') && !StackNotEmpty(myStack))
        {
            printf("右括號多於左括號!\n");
            return ;
        }
    }

    if(StackNotEmpty(myStack))
    {
        printf("左括號多於右括號!\n");
    }
    else
    {
        printf("左右括號匹配正確!\n");
    }
}

/*
左右括號配對次序不正確!
右括號多於左括號!
左括號多於右括號!
左右括號匹配正確!
Press any key to continue
*/

這裏寫圖片描述

隊列也是一種特殊的線性表,隊列的數據元素以及數據元素間的邏輯關係與線性表完全相同,其差別是線性表允許在任意位置插入和刪除,而隊列只允許在其一端進行插入操作,在其另一端進行刪除操作。隊列中允許進行插入操作的一端稱爲隊尾,允許進行刪除操作的一端稱爲隊頭。隊頭和隊尾分別由隊頭指示器(或稱隊頭指針)和隊尾指示器(或稱隊尾指針)指示。隊列的插入操作通常稱爲入隊列,隊列的刪除操作通常稱爲出隊列。當隊列中沒有數據元素時稱爲空隊列。

順序隊列


typedef struct 
{
    DataType queue[MaxQueueSize];
    int rear;       //隊尾指針
    int front;      //隊頭指針
    int count;      //計數器
} SeqCQueue;

//1.初始化
void QueueInitiate(SeqCQueue*Q) //初始化順序循環隊列Q
{
    Q->rear = 0;        //定義初始隊尾指針下標值
    Q->front = 0;       //定義初始隊頭指針下標值
    Q->count = 0;       //定義初始計數器值
}

//2.非空否
int QueueNotEmpty(SeqCQueue Q)
{
    //判斷順序循環隊列Q非空否,非空時返回1,否則返回0
    if(Q.count != 0) return 1;
    else return 0;
}

//3.入隊列
int QueueAppend(SeqCQueue*Q,DataType x)
{
    //把數據元素值x插入順序循環隊列Q的隊尾,成功返回1,失敗則返回0
    if(Q->count > 0 && Q->rear == Q->front)
    {
        printf("隊列已滿無法插入!\n");
        return 0;
    }
    else
    {
        Q->queue[Q->rear] = x;
        Q->rear = (Q->rear + 1) % MaxQueueSize;
        Q->count++;
        return 1;
    }
}

//4.出隊列
int QueueDelete(SeqCQueue*Q,DataType *d)
{
    //刪除順序循環隊列Q的隊頭元素並賦給d,成功返回1,失敗返回0
    if(Q->count == 0)
    {
        printf("隊列已空無數據元素出隊列!\n");
        return 0;
    }
    else
    {
        *d = Q->queue[Q->front];
        Q->front = (Q->front + 1) % MaxQueueSize;
        Q->count--;
        return 1;
    }
}

//5.取隊頭元素
int QueueGet(SeqCQueue Q,DataType*d)
{
    //取順序循環隊列Q的當前隊頭元素並賦給d,成功返回1,失敗返回0
    if(Q.count == 0)
    {
        printf("隊列已空無數據元素可取!\n");
        return 0;
    }
    else
    {
        *d = Q.queue[Q.front];
        return 1;
    }
}

鏈式隊列


typedef struct qnode {
    DataType data;
    struct qnode * next;
}LQNode;

typedef struct  {
    LQNode* front;  //隊頭指針
    LQNode* rear;   //隊尾指針
}LQueue;

//1.初始化
void QueueInitiate(LQueue*Q)
{
    Q->rear = NULL;     //定義初始隊尾指針
    Q->front = NULL;    //定義初始隊頭指針
}

//2.非空否
int QueueNotEmpty(LQueue Q)
{
    //判斷鏈式隊列Q非空否,非空返回1,否則返回0
    if(Q.front == NULL) return 0;
    else return 1;
}

//3.入隊列
int QueueAppend(LQueue*Q,DataType x)
{
    //把數據元素值x插入鏈式隊列Q的隊尾,入隊列成功返回1,否則返回0
    LQNode*p;
    if((p = (LQNode*)malloc(sizeof(LQNode))) == NULL)
    {
        printf("內存空間不中!");
        return 0;
    }
    p->data = x;
    p->next = NULL;

    if(Q->rear != NULL) Q->rear->next = p;
    Q->rear = p;
    if(Q->front == NULL) Q->front = p;
    return 1;
}

//4.出隊列
int QueueDelete(LQueue*Q,DataType*d)
{
    //刪除鏈式隊列Q的隊頭數據元素值到d,出隊列成功返回1,否則返回0
    LQNode*p;
    if(Q->front == NULL)
    {
        printf("隊列已空無數據元素出隊列!\n");
        return 0;
    }
    else
    {
        *d = Q->front->data;
        p = Q->front;
        Q->front = Q->front->next;
        if(Q->front == NULL) Q->rear = NULL;
        free(p);
        return 1;
    }
}

//5.取隊頭數據元素
int QueueGet(LQueue Q,DataType*d)
{
    //取鏈式隊列Q的當前隊頭數據元素值到d,成功返回1,否則返回0
    if(Q.front == NULL)
    {
        printf("隊列已空無數據元素出隊列!\n");
        return 0;
    }
    *d = Q.front->data;
    return 1;
}

//6.撤消動態申請空間
void Destroy(LQueue Q)
{
    LQNode*p,*p1;
    p = Q.front;
    while(p != NULL)
    {
        p1 = p;
        p = p->next;
        free(p1);
    }
}

隊列應用

#include <stdio.h>
#include <string.h>
#define MaxQueueSize 100
#define MaxStackSize 100
typedef char DataType;
#include "SeqQueue.h"
#include "SeqStack.h"

void HuiWen(char str[]);

void main(void)
{
    char str1[] = "ABCDEDCBA";
    char str2[] = "ABCDEDCAB";

    HuiWen(str1);
    HuiWen(str2);
}
/*
ABCDEDCBA是迴文!
ABCDEDCAB不是迴文!
Press any key to continue
*/

/************************************************************************/
/* 
1.編程序判 斷一個字符序列是否是迴文。迴文是指一個字符序列以中間字符爲基準
兩邊字符完全相同,如字符序列“ABCDEDCBA”就是迴文,而字符序列“ABCDEDBAC”就不是回
文。
算法思想:
設字符數組str中存放了要判斷的字符串。把字符數組中的字符逐個分別存入隊列和堆棧,
然後逐個出隊列和退棧並比較出隊列的字符和退棧的字符是否相等,若全部相等則該字符
序列是迴文,否則就不是迴文。
*/
/************************************************************************/
void HuiWen(char str[])
{
    SeqCQueue myQueue;
    SeqStack myStack;
    char x,y;
    int i,length;

    length = strlen(str);
    QueueInitiate(&myQueue);
    StackInitiate(&myStack);
    for(i = 0;i < length; i++)
    {
        QueueAppend(&myQueue,str[i]);
        StackPush(&myStack,str[i]);
    }

    while(QueueNotEmpty(myQueue) == 1 && StackNotEmpty(myStack) == 1)
    {
        if(QueueDelete(&myQueue,&x) == 1 && StackPop(&myStack,&y) == 1 && x != y)
        {
            printf("%s不是迴文!\n",str);
            return ;
        }
    }

    if(QueueNotEmpty(myQueue) || StackNotEmpty(myStack))
    {
        printf("%s不是迴文!\n",str);
    }
    else
        printf("%s是迴文!\n",str);
}

這裏寫圖片描述

優先級隊列

優先級隊列是帶有優先級的隊列。隊列是數據元素的先進先出表,即最先進入隊列的元素將最先被刪除。但在有些軟件系統中,有時也要求把進入隊列中的元素分優先級,出隊列時首先選擇優先級最高的元素出隊列(即優先級高的元素被先服務),對優先級相同的元素則按先進先出的原則出隊列。顯示,優先級隊列和一般隊列的主要區別是:優先級隊列的出隊列操作不是把隊頭元素出隊列,而是把隊列中優先級最高的數據元素出隊列。

順序優先級隊列


typedef struct  {
    int priority;
    ElemType elem;
} DataType;

typedef struct  {
    DataType queue[MaxQueueSize];
    int size;
}SeqPQueue;

//1.初始化
void QueueInitiate(SeqPQueue*Q)
{
    Q->size = 0;    //定義初始元素個數
}

//2.空否
int QueueNotEmpty(SeqPQueue Q)
{
    //判斷順序優先隊列Q非空否,非空返回1,否則返回0
    if(Q.size <= 0) return 0;
    else return 1;
}

//3.入隊
int QueueAppend(SeqPQueue*Q,DataType x)
{
    //把數據元素值x插入順序優先隊列Q的隊尾,成功返回1,失敗返回0
    if(Q->size >= MaxQueueSize)
    {
        printf("隊列已滿無法插入!\n");
        return 0;
    }
    else
    {
        Q->queue[Q->size] = x;
        Q->size++;
        return 1;
    }
}

//4.出隊
int QueueDelete(SeqPQueue*Q,DataType*d)
{
    //刪除順序優先隊列Q中優先級最高的元素並賦給d,成功返回1,失敗返回0
    DataType min;
    int minIndex,i;

    if(Q->size <= 0)
    {
        printf("隊列已空無數據元素出隊列!\n");
        return 0;
    }
    else
    {
        min = Q->queue[0];  //初始選queue[0]爲優先級最高的元素
        minIndex = 0;       //minIndex爲優先級最高元素的下標
        for(i = 1;i < Q->size; i++)
        {
            if(Q->queue[i].priority < min.priority)
            {
                min = Q->queue[i];
                minIndex = i;
            }
        }

        *d = Q->queue[minIndex];    //找到優先級最高的元素
        for(i = minIndex + 1; i < Q->size; i++) //數據元素依次前移
            Q->queue[i - 1] = Q->queue[i];
        Q->size--;                  //元素個數減1
        return 1;
    }
}

int QueueGet(SeqPQueue*Q,DataType*d)
{
    //取順序優先隊列Q中優先級最高的元素並賦給d,成功返回1,失敗返回0
    DataType min;
    int minIndex,i;
    if(Q->size <= 0)
    {
        printf("隊列已空無數據元素可取!\n");
        return 0;
    }
    else
    {
        min = Q->queue[0];  //初始選queue[0]爲優先級最高的元素
        minIndex = 0;       //minIndex爲優先級最高元素的下標
        for(i = 1;i < Q->size; i++)
        {
            if(Q->queue[i].priority < min.priority)
            {
                min = Q->queue[i];
                minIndex = i;
            }
        }
        *d = Q->queue[minIndex];
        return 1;
    }
}

優先級隊列應用

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

#define MaxQueueSize 100
typedef int ElemType;       //定義data域的數據類型爲int
#include "SeqPQueue.h"      //包含順序優先級隊列

/************************************************************************/
/* 
1.設計一個程序,模仿操作系統的進程管理問題。進程服務按優先級高的先服務,
優先級相同的先到先服務的原則管理。設文件task.dat中存放了仿真進程服務請求數據,
其中第一列表示進程的任務號,第二列表示進程的優先級。
文件task.dat中的仿真進程服務請求數據如下:
1   30
2   20
3   40
4   20
5   0

設計:文件task.dat中每一個進程服務的請求數據包含任務號和優先級兩項。可直接
用上述結構體DataType中的抽象數據類型ElemType爲int數據類型。
*/
/************************************************************************/
void main(void)
{
    SeqPQueue myPQueue;
    FILE * fp;
    DataType task;
    int i;
    if((fp = fopen("task.dat","r")) == NULL)
    {
        printf("不能打開文件task.dat!");
        exit(0);
    }
    QueueInitiate(&myPQueue);       //初始化順序優先級隊列
    while(!feof(fp))
    {
        fscanf(fp,"%d\t%d",&task.elem,&task.priority);
        QueueAppend(&myPQueue,task);        //把數據入隊列
    }

    i = 1;
    printf("序號\t任務號\t優先級\n");
    while(QueueNotEmpty(myPQueue))  //逐個出隊
    {
        QueueDelete(&myPQueue,&task);   //出隊
        printf("%d\t%d\t%d\n",i,task.elem,task.priority);
        i++;
    }
}
/*
序號    任務號  優先級
1       5       0
2       2       20
3       4       20
4       1       30
5       3       40
Press any key to continue
*/

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章