第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
*/

这里写图片描述

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