这几天总结了C语言的队列,栈的实现方法,在此总结一下:
一、栈
首先从栈开始,诚然,相信学习过数据结构的你,肯定应该知道栈是什么东西了,如果不知道也没事每一句话我就可以帮你总结--数据
只在栈顶进行插入和删除操作,数据进出栈符合先进后出或者后进先出的原则。来贴个图片,你就知道了。
再也没有比上述图片更能贴切的描述栈了,数据结构中的栈和程序运行压栈的栈还略有区别,在此先不说那么多,继续回归正题。栈的应用很多,你最可能会用到的一个就是10进制转换2进制数了,具体怎么应用,请亲爱的你动手试试哦!
接下来我就介绍栈的几种实现方法:
1.固定栈
固定栈是最简单的一种了,要点就2个,一是注意栈满,二是注意要符合先进后出的原则。在这两点的基础上,就可以自己动手写一个栈空间实现
程序,这里我就介绍那么多了,因为这个实在是太简单了,代码中我也都作了详细地说明。
1 # include "stdio.h" 2 # include "stdlib.h" 3 4 # define MAX_STACK_SIZE 100 //定义栈空间的最大存储内存 5 # define ERROR 1 //错误标志 6 # define OK 0 7 8 typedef struct sqstack{ //封装一个结构体,包括定义的栈存储数组,栈顶和栈底标志 9 int stack_array[MAX_STACK_SIZE]; 10 int top; 11 int bottom; 12 }SqStack; 13 14 SqStack SqStack_Init(void) //初始化一个栈内存 15 { 16 static SqStack S; 17 S.top = S.bottom = 0; 18 return S; 19 } 20 21 int Push_Stack(SqStack *S) //压栈操作 22 { 23 int ele; 24 if(S->top == MAX_STACK_SIZE-1) 25 { 26 return ERROR; 27 } 28 scanf("%d",&ele); 29 S->stack_array[S->top] = ele; 30 S->top+=1; 31 return OK; 32 } 33 34 int Pop_Stack(SqStack *S) //出栈操作 35 { 36 if(S->top != 0) 37 S->top--; 38 printf("%d ",S->stack_array[S->top]); 39 } 40 41 int main(void) 42 { 43 int ele; 44 int i = 0; 45 SqStack MyStack = SqStack_Init(); 46 while(1) 47 { 48 system("cls"); 49 printf("***************静态栈空间模拟实现******************* ") ; 50 printf("请输入元素!(元素进栈)"); 51 for(i=0;i<3;i++) //压三次栈 52 Push_Stack(&MyStack); 53 printf("元素出栈 "); 54 for(i=0;i<3;i++) //出三次栈 55 Pop_Stack(&MyStack); 56 system("pause") ; 57 } 58 return 0; 59
2.动态链栈
这是完全采用链表的形式实现一个栈空间,稍微有点麻烦,你可以选择该栈没有上限,只要你一直输就一直开辟栈空间,
并且进行数据存储,不过这显然是不符合实际的,所以我加了个stack_size变量,为该栈设置了上限,具体的代码如下:
1 # include"stdio.h" 2 # include "stdlib.h" 3 4 # define Stack_Size 5 5 6 typedef struct stack{ 7 int ele; 8 int stack_size; 9 struct stack *next; 10 }Stack,* Pstack; 11 12 //动态链栈的实现,每个节点可以存储一个栈点元素, 因此首先需要一个数据区,然后就是一个指针区,首先是一个链表,其次才是一个栈 13 //且应该定义一个top和bottom指针,来指向栈底,和栈顶,这样才能辅助来进行完成链栈的实现。 14 15 Pstack Top; 16 Pstack Bottom; 17 18 void Creat_StackNode(void) //创建一个栈底。并且定义这个栈底不允许释放。 19 { 20 Top = (Pstack)malloc(sizeof(Stack)); 21 Bottom = Top; 22 scanf("%d",&Top->ele); //写入栈底元素 23 Top->stack_size=0; 24 Top->next = NULL; 25 printf("栈底建立成功 "); 26 } 27 28 void PushStack(void) 29 { 30 Pstack pnew = (Pstack)malloc(sizeof(Stack)); 31 pnew->next = Top; //新建一个节点,指针指向前一个节点,其实这就是在连接一个链表,然后再让Top指向新建的节点。 32 scanf("%d",&pnew->ele); 33 pnew->stack_size=Top->stack_size + 1; 34 Top = pnew; 35 } 36 37 void PopStack(void) 38 { 39 Pstack ptemp = Top->next; //这个其实跟链表的删除一样,因为你要释放掉这个节点,所以必须提前保存该节点的下一个指向,然后才能释放带该节点 40 printf("%d ",Top->ele); //将节点元素打印出来。 41 free(Top); //释放节点空间,也就是出栈, 42 Top = ptemp; //再将top指针指向新的栈顶 。 43 } 44 45 int main(void) 46 { 47 int sel; 48 int i = 0; 49 printf("创建链栈的头节点!"); 50 Creat_StackNode(); //这个是链栈的最底部空间,不允许释放。 51 while(1) 52 { 53 printf("请选择是压栈还是出栈!(1.入 2.出)"); 54 scanf("%d",&sel); 55 if(sel == 1) 56 { 57 if(Top->stack_size > Stack_Size-1) 58 printf("栈内存已满了!") ; 59 else 60 PushStack(); //将数据压入链栈 61 } 62 else if(sel == 2) 63 { 64 if(Top->stack_size == 0) 65 { 66 printf("%d ",Top->ele); 67 printf("已经到达栈底,禁止继续弹出数据! ") ; 68 } 69 else 70 PopStack(); //将数据弹出栈 71 } 72 else 73 { 74 printf("选择输入错误,请重新输入:"); 75 fflush(stdin) ; 76 } 77 } 78 return 0; 79 }
二、队列
队列和栈是近似的,也是一种存储空间吧,不过它的存储与栈刚好相反,符合先进先出的原则。队列的首尾成为队首和队尾
元素在队尾实现入队,在队首实现出队,详情请看下图--形象的比喻:水从水管的一端进入,另一端出去。。
队列的实现有以下三种,
1.静态队列
类似上述静态栈的构建,不过这种的入队和出队都是向一个方向进行,在有限的空间内很容易造成空间的浪费因此用途并不广泛,再次我也就不举例了,太简单了。
2.静态循环队列
这就是 一个很好的队列实现方式了,使用循环的方式,能够节省空间,因此,使用较为广泛,实现起来也略微有点麻烦,首先来看下循环队列的结构示意图,或许你会更理解:
具体的代码如下,供参考,代码中也有了十分详细的注释:
1 # include "stdio.h" 2 3 # define QUEUE_SIZE 5 4 5 typedef struct queue{ 6 int queue_array[QUEUE_SIZE]; 7 int front; 8 int rear; 9 }Queue,*Pqueue; 10 //由于队列无论是入队还是出队都是只能像一个方向增长的操作,因此单向静态队列的用途不是很大, 同场有用的是静态循环队列和动态队列。 11 //该部分介绍一个静态循环队列。 12 //静态循环队列,是一个将队头和队尾链接在一起的队列,队列空间能够循环使用,因此空间利用率高,使用比较广泛 13 14 Pqueue Init_Queue(void) //初始化一个队列 15 { 16 static Queue queue; 17 queue.front = 0; 18 queue.rear = 0; 19 return &queue; 20 } 21 22 /*接下来就是插入都列,和出队列了,这两个操作其实并不难,唯一的难点就在于临界状态的判断,有两个临界状态, 第一个就是队首不动,空间插入满,队尾追上了队首 23 另外一个难点就是出队列队首追上了队尾,表示队尾已经空。因此确定了两个判断条件是 (rear+1)%size == front.表示队列满了,如果,(front)%size == front 24 则表示队列已经空了,并且这其中,默认rear所指向向的空间一直为空*/ 25 void InputQueue(Pqueue MyQueue) 26 { 27 if((MyQueue->rear+1)%QUEUE_SIZE == MyQueue->front) //这一步骤的运算可以称得上是神来之笔 28 { 29 printf("队列已满,不允许插入! "); 30 return; 31 } 32 else 33 { 34 scanf("%d",&MyQueue->queue_array[MyQueue->rear]); 35 MyQueue->rear = (MyQueue->rear+1)%QUEUE_SIZE; 36 printf("插入成功! "); 37 } 38 } 39 40 void OutPutQueue(Pqueue MyQueue) 41 { 42 if(MyQueue->front==MyQueue->rear) 43 { 44 printf("队列已空,请勿继续进行删除操作! "); 45 return; 46 } 47 else 48 { 49 printf("%d ",MyQueue->queue_array[MyQueue->front]); 50 MyQueue->front = (MyQueue->front+1)%QUEUE_SIZE; 51 printf("删除成功! "); 52 } 53 } 54 55 int main(void) 56 { 57 int i = 0; 58 int sel; 59 Pqueue MyQueue; MyQueue = Init_Queue(); 60 while(1) 61 { 62 system("cls"); 63 printf("请输入选择: 1.插入 2.删除!!! "); 64 scanf("%d",&sel); 65 66 if(sel ==1) 67 { 68 InputQueue(MyQueue); 69 sel = 0; 70 } 71 else if(sel == 2) 72 { 73 OutPutQueue(MyQueue); 74 sel = 0; 75 } 76 system("pause"); 77 } 78 return 0; 79 }
3.链式队列
队列的实现同样可以使用链的方式来构建完成,一旦牵扯到链,那么队列也就非常灵活了,不多啰嗦,直接看代码。
1 # include "stdio.h" 2 # include "stdlib.h" 3 4 typedef struct queue{ //封装一个队列节点的结构体 5 int ele; 6 struct queue *next; 7 }Dqueue,*Pqueue; 8 9 Pqueue Rear; //定义队首指针和队尾指针。 10 Pqueue Front; 11 12 void Init_Queue(void) //初始化队的开始,该部分空间不允许释放,否则会丢失队列的指针。 13 { 14 Pqueue phead = (Pqueue)malloc(sizeof(Dqueue)); 15 phead->next = NULL; 16 printf("请输入第一个队列元素:"); 17 scanf("%d",&phead->ele); 18 Rear = phead; //开始时队首指针和队尾指针都指向该节点。 19 Front = phead; 20 printf("构建成功! "); 21 } 22 23 void Input_Queue(void) //入队操作。 24 { 25 Pqueue pnew = (Pqueue)malloc(sizeof(Dqueue)); 26 if(pnew == NULL) 27 printf("空间申请失败"); 28 else 29 { 30 scanf("%d",&pnew->ele); 31 pnew->next = NULL; //入队操作时一个在头结点前插入的操作,对于新申请的堆空间必须要设置其指针域和数据域,缺一不可。 32 Rear->next = pnew; /*该步十分关键,!!!为何这么说,它指定了新创建的前一个节点的指向是新建的节点,也就确定一条由下向上的的 33 链式指向,因为这一步,Bottom指针才能一次向上访问节点,进行出队操作。*/ 34 Rear = pnew; //使队尾指针指向最末尾节点。 35 printf("入队成功 "); 36 } 37 } 38 39 void Output(void) //出队操作 40 { 41 Pqueue ptemp; 42 if(Front == Rear) //这就是队空标志,只允许弹出这个节点元素,不允许改变指针指向和释放改空间,否则队列指针将没有指向,还得重新构建 43 { 44 printf("%d",Front->ele); 45 printf("队列已经空,请禁止继续出队 "); 46 } 47 else 48 { 49 ptemp = Front->next; //同时这也是一个释放链式节点的操作,先让暂存指针保存,释放节点的指向, 50 printf("%d",Front->ele); //然后打印出释放节点的节点数值 51 free(Front); //释放该节点 52 Front = ptemp; //然后重新保存暂存指针,这样队首指针就向上移动一个节点。 53 printf("出队成功 "); 54 } 55 } 56 57 int main(void) 58 { 59 int sel; 60 Init_Queue(); 61 62 while(1) 63 { 64 printf("请选择入队还是出队!1.入队 2.出队: "); 65 scanf("%d",&sel); 66 if(sel == 1) 67 { 68 Input_Queue(); 69 sel = 0; 70 } 71 else if(sel == 2) 72 { 73 Output(); 74 sel = 0; 75 } 76 } 77 return 0; 78 }
至此,栈和队列已经总结完毕,其实说难也不难,只是刚学的你,可能会稍微有点绕,希望能帮到诸位!