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