【背景】
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
【源码运行环境】
操作系统:MAC OS X 10.13.4
编译环境:CLion (C++ 14) cmake
【源码实现】
S_Q.h源码部分:
// // Created by WellLee on 2018/10/29. // #ifndef STACK_Queue_S_Q_H #define STACK_Queue_S_Q_H #include "stdio.h" #include "stdlib.h" #include "memory.h" /* * 说明:define 定义一些常用的符号特性 * 作用:增加代码可读性 * Author: WellLee * Last Modified: 2018年10月31日23:16:07 */ #define TRUE 1 #define FALSE 0 #define OVERFLOW -1 #define MAXSIZE 20 typedef int Status; typedef int Elem; /* * 结构:顺序栈 * 作用:定义了顺序栈的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月31日23:17:31 */ typedef struct Stack{ Elem data[MAXSIZE]; int top; }Stack; Status InitStack(Stack *S); Status pop(Stack *S, Elem *e); Status push(Stack *S, Elem e); Status EmptyStack(Stack S); Status ClearStack(Stack *S); Status StackLength(Stack *S, int *length); Status DestroyStack(Stack *S); void showStackElements(Stack S); /* * 结构:链栈 * 作用:定义了链栈的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月31日23:17:31 */ typedef struct Stacknode{ Elem elem; struct Stacknode *next; }stacknode,*StackNode; typedef struct LinkStack{ StackNode top; int length; }*LStack; Status InitLinkStack(LStack *LS); Status LS_push(LStack *LS, Elem e); Status LS_pop(LStack *LS, Elem *e); Status LS_Clear(LStack *LS); Status LS_Destroy(LStack *LS); Status LS_Empty(LStack LS); void LS_show(LStack LS); /* * 结构:顺序队列 * 作用:定义了顺序队列的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月31日23:17:31 */ typedef struct SequenceQueue{ Elem data[MAXSIZE]; int front; int rear; }SqQueue; Status InitSqQueue(SqQueue *Q); Status EnSqQueue(SqQueue *Q, Elem e); Status DeSqQueue(SqQueue *Q, Elem *e); Status ClearSqQueue(SqQueue *Q); Status DestroySqQueue(SqQueue *Q); Status QueueLength(SqQueue Q, int *length); void showQueueElements(SqQueue Q); /* * 结构:链队列 * 作用:定义了链队列的一些结构特性,封装了常用的线性表操作 * Author: WellLee * Last modified: 2018年10月31日23:17:31 */ typedef struct QueueNode{ Elem data; struct QueueNode *next; }QNode,*QNodeptr; typedef struct LinkQueue{ QNodeptr rear; QNodeptr front; }LinkQueue; Status InitLkQueue(LinkQueue *Q); Status EnLkQueue(LinkQueue *Q, Elem e); Status DeLkQueue(LinkQueue *Q, Elem *e); Status ClearLkQueue(LinkQueue *Q); Status DestroyLkQueue(LinkQueue *Q); Status LkQueueLength(LinkQueue Q, int *length); void showLkQueue(LinkQueue Q); #endif //STACK_Queue_S_Q_H
Stack.c源码部分:
// // Created by WellLee on 2018/10/29. // #include "S_Q.h" #include "time.h" /* * 方法名:InitStack * 作用:初始化顺序栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status InitStack(Stack *S) { S->top = -1; int StackLength = 0; Elem elem; srand(time(0)); printf("请输入栈的元素个数"); scanf("%d", &StackLength); for (int i = 0; i < StackLength; i++) { elem = rand() % 100 - 1; push(&*S, elem); } return TRUE; } /* * 方法名:push * 作用:顺序栈压栈操作 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status push(Stack *S, Elem e) { if(S->top >= MAXSIZE) { printf("栈满\n"); return OVERFLOW; } S->data[++S->top] = e; return TRUE; } /* * 方法名:StackEmpty * 作用:判断顺序栈是否为空 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status StackEmpty(Stack S) { if(S.top == -1) { return TRUE; } return FALSE; } /* * 方法名:pop * 作用:顺序栈出栈操作 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status pop(Stack *S, Elem *e) { if(StackEmpty(*S)) { printf("栈空\n"); return FALSE; } int top = S->top; *e = S->data[S->top--]; S->data[top] = 0; return TRUE; } /* * 方法名:showStackElements * 作用:显示顺序栈元素 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ void showStackElements(Stack S) { if(StackEmpty(S)) { printf("栈空\n"); return; } printf("栈元素如下:"); printf("栈顶指向 -> %d \n",S.top); for(int i = 0; i <= S.top; i++){ printf("%d ",S.data[i]); } printf("\n"); } /* * 方法名:DestroyStack * 作用:销毁顺序栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status DestroyStack(Stack *S) { free(S); return TRUE; } /* * 方法名:ClearStack * 作用:清空顺序栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status ClearStack(Stack *S) { memset(S->data,0, sizeof(S->data)); S->top = -1; return TRUE; } /* * 方法名:StackLength * 作用:获取顺序栈长度 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status StackLength(Stack *S, int *length) { *length = S->top + 1; return TRUE; } /* * 方法名:InitLinkStack * 作用:初始化链栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status InitLinkStack(LStack *LS) { srand(time(0)); StackNode head = (StackNode)malloc(sizeof(stacknode)); if(!head) { printf("内存不足\n"); return OVERFLOW; } head->next = NULL; *LS = (LStack)malloc(sizeof(LStack)); if(!*LS) { printf("内存不足\n"); return OVERFLOW; } LStack ls = *LS; ls->top= head; ls->length = 0; printf("请输入栈的元素个数:"); int length; scanf("%d", &length); for(int i = 0; i < length; i++) { Elem e = rand()%100 -1; LS_push(&ls, e); } printf("初始化成功\n"); return TRUE; } /* * 方法名:LS_push * 作用:链栈压栈操作 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status LS_push(LStack *LS, Elem e) { LStack ls = *LS; if(!ls) { printf("线性表参数有误\n"); return OVERFLOW; } StackNode head = ls->top; StackNode elem = (StackNode)malloc(sizeof(stacknode)); if(!elem) { printf("内存不足\n"); return OVERFLOW; } elem->elem = e; elem->next = ls->top->next; head->next = elem; ls->length++; printf("%d 入栈\n", e); return TRUE; } /* * 方法名:LS_pop * 作用:链栈出栈操作 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status LS_pop(LStack *LS, Elem *e) { if(LS_Empty(*LS)) { printf("空栈,无元素出栈\n"); return OVERFLOW; } LStack ls = *LS; StackNode node = ls->top; StackNode freenode = node->next; *e = freenode->elem; node->next = freenode->next; free(freenode); ls->length--; printf("%d 出栈\n", *e); return TRUE; } /* * 方法名:LS_Clear * 作用:清空链栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status LS_Clear(LStack *LS) { LStack ls = *LS; if(!ls){ printf("栈参数有误\n"); return FALSE; } while(ls->length != 0) { Elem temp; LS_pop(&ls, &temp); } printf("栈已清空\n"); return TRUE; } /* * 方法名:LS_Destroy * 作用:销毁链栈 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status LS_Destroy(LStack *LS) { free(*LS); printf("栈已销毁\n"); return TRUE; } /* * 方法名:LS_Empty * 作用:判断链栈是否为空 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ Status LS_Empty(LStack LS) { if(LS->length == 0) { return TRUE; } return FALSE; } /* * 方法名:LS_show * 作用:显示链栈元素 * Author: WellLee * Last Modified:2018年10月31日23:21:09 */ void LS_show(LStack LS) { if(LS_Empty(LS)) { printf("空栈\n"); return; } StackNode head = LS->top->next; printf("当前栈有%d个元素,如下所示:\n",LS->length); while(head!=NULL) { printf("%d ",head->elem); head = head->next; } printf("\n"); }
Queue.c 源码如下
// // Created by WellLee on 2018/10/31. // #include "S_Q.h" /* * 方法名:InitSqQueue * 作用:初始化顺序队列 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status InitSqQueue(SqQueue *Q) { Q->front = 0; Q->rear = 0; return TRUE; } /* * 方法名:EnSqQueue * 作用:顺序队列入队操作 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status EnSqQueue(SqQueue *Q, Elem e) { if((Q->rear + 1) % MAXSIZE == Q->front) { printf("队列满\n"); return OVERFLOW; } Q->data[Q->rear] = e; Q->rear = (Q->rear+1) % MAXSIZE; return TRUE; } /* * 方法名:DeSqQueue * 作用:顺序队列出队操作 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status DeSqQueue(SqQueue *Q, Elem *e) { if(Q->rear == Q->front) { printf("队空\n"); return FALSE; } *e = Q->data[Q->front]; Q->front = (Q->front + 1) % MAXSIZE; } /* * 方法名:ClearSqQueue * 作用:清空顺序队列 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status ClearSqQueue(SqQueue *Q) { memset(Q->data,0, sizeof(Q->data)); InitSqQueue(Q); return TRUE; } /* * 方法名:DestroySqQueue * 作用:销毁顺序队列 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status DestroySqQueue(SqQueue *Q) { ClearSqQueue(Q); printf("队列已经销毁\n"); return TRUE; } /* * 方法名:QueueLength * 作用:获取顺序队列长度 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status QueueLength(SqQueue Q, int *length) { *length = (Q.rear - Q.front + MAXSIZE) % MAXSIZE; return TRUE; } /* * 方法名:showQueueElements * 作用:输出顺序队列中的元素 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ void showQueueElements(SqQueue Q) { int length = 0; QueueLength(Q, &length); if(length == 0) { printf("队空\n"); return; } printf("当前队列元素如下\n"); for(int i = 0; i < length; i++) { printf("%d ", Q.data[Q.front + i]); } printf("\n"); } /* * 方法名:InitLkQueue * 作用:初始化链队 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status InitLkQueue(LinkQueue *Q) { QNodeptr head = (QNodeptr )malloc(sizeof(QNode)); if(!head) { printf("内存不足\n"); return OVERFLOW; } head->next = NULL; Q->front = head; Q->rear = head; return TRUE; } /* * 方法名:EnLkQueue * 作用:链队压栈操作 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status EnLkQueue(LinkQueue *Q, Elem e) { QNodeptr node = (QNodeptr )malloc(sizeof(QNode)); if(!node) { printf("内存不足\n"); return OVERFLOW; } node->data = e; node->next = NULL; Q->rear->next = node; Q->rear = node; return TRUE; } /* * 方法名:DeLkQueue * 作用:链队出栈操作 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status DeLkQueue(LinkQueue *Q, Elem *e) { if(Q->rear == Q->front) { printf("队空\n"); return FALSE; } QNodeptr temp = Q->front->next; Q->front->next = temp->next; *e = temp->data; if(Q->front->next == NULL) { Q->rear = Q->front; } free(temp); return TRUE; } /* * 方法名:ClearLkQueue * 作用:清除链队所有数据 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status ClearLkQueue(LinkQueue *Q) { while(Q->front != Q->rear) { Elem temp; DeLkQueue(Q, &temp); } return TRUE; } /* * 方法名:DestroyLkQueue * 作用:销毁链队 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status DestroyLkQueue(LinkQueue *Q) { free(Q->front); return TRUE; } /* * 方法名:LkQueueLength * 作用:获取链队元素长度 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ Status LkQueueLength(LinkQueue Q, int *length) { QNodeptr temp = Q.front->next; int i = 0; while(temp != NULL) { i++; temp = temp->next; } *length = i; } /* * 方法名:showLkQueue * 作用:显示链队所有元素 * Author: WellLee * Last Modified:2018年10月31日23:42:16 */ void showLkQueue(LinkQueue Q) { QNodeptr temp = Q.front; temp = temp->next; if(temp == NULL) { printf("队空\n"); return; } printf("队列元素如下\n"); while(temp != NULL) { printf("%d ",temp->data); temp = temp->next; } }
【分析】
-
栈和队列都是特殊的线性表,只不过是对插入和删除做了限制。
-
栈(Stack)是限定仅在表尾进行插入和删除操作的线性表。根据这样的特性又可以称它为:后进先出表(LIFO)。
-
队列(Queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。根据这样的特性又可以称它为:先进先出表(FIFO)。
-
它们均可以用线性表顺序存储结构来实现,但都存在顺序存储的一些弊端,因此它们各自有各自的技巧来解决这个问题。
-
对于栈来说,如果是两个相同数据类型的栈,可以用数组的两端作为栈底的方法来让两个栈共享数据,这就可以最大化利用数组的空间。
-
对于队列来说,为了避免数组插入和删除时需要移动数据,于是引进了循环队列,使得队头和队尾可以在数组中循环变化。解决了移动数据的时间损耗,使得原来的删除和插入式O(n)的时间复杂度编程了O(1)。
-
它们也都可以用链式存储来实现,实现原则上与线性表基本相同。
【总结】
本文通过学习栈和队列两种数据结构的定义,性质和实现方式,给出了栈/队列的C语言程序实现。加深了对栈/队列的认识,也接触了一些栈在计算机中的应用(递归,中缀->后缀表达式等)。也在从线性表表示栈/队列过程中学习到了一些前人解决问题的思想(共享栈、循环队列的引入)。而这两种数据结构的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更更加聚焦于我们要解决问题的核心。
【参考文献】
《大话数据结构》.程杰.清华大学出版社