堆栈和队列都是特殊的线性表。线性表、堆栈和队列三者的数据元素以及数据元素间的逻辑关系完全相同,差别是线性表的插入和删除操作不受限制,而堆栈只能在栈顶插入和删除,队列只能在队尾插入在队头删除。堆栈和队列都可以分别用顺序存储结构和链式存储结构。顺序队列通常采用顺序循环队列方法实现,因为顺序循环队列可以避免顺序队列的“假溢出”问题。
堆栈和队列在各种类型的软件中应用十分广泛,堆栈可以用来完成数据序列的特定转换,队列可以用作 数据元素序列的缓冲存储。
顺序堆栈
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
*/