线性结构的C实现之队列和栈(面向新手,大佬勿喷)

这篇文章讲解一下什么叫队列什么叫栈。
这两种结构往往并不新鲜,其实就是简化版的数组或者链表,但是确实这两种结构应用的范围应该比数组和链表的应用要广一点,至少对于我来说,实现一些复杂操作离不开这两种结构。

这个里面的操作很多都是前两个部分的继承,对数组和链表还不熟的请查看上面两篇文章。

队列结构

队列是所谓先进后出的一种特殊结构,回想当年Walker插队的时候,多么风光,这下没办法了,明令禁止不允许插队,Walker只能乖乖的到队尾,慢慢排呗,什么时候排到自己了,什么时候再出来。
计算机中实现队列可不是很容易,因为数组从末尾加进去还好,要是从队首出去,如果每个人都往前迈一步那岂不是很麻烦,这里介绍一种比较高效的方法,就是利用取模运算来实现。

如果你Wow了一下,我想我会非常骄傲,但是实际上,这种操作也比较常规,下面介绍一下我的写法。

#include<stdio.h>
int main()
{
	int Queue[20] = {0};//还是一个数组
	int back = 0;//队尾,永远指向下次应该添加的位置
	int front = 0;//队首,永远指向下次应该删除的位置和最先访问的位置
	//两个数相等,判断为队空
	return 0;
}

队列的增删操作有些情形下也可以直接写成

//还是那句话,自己掂量着是否越界的问题
Queue[back] = 100;//直接在back下标位置添加
back++;//然后back右移一位


front++;//和数组的思想一致,front++,有效范围缩小,即删除操作

现在,我们设想这个队列每次在增删操作的时候,任何元素都不动,这样就导致了一个问题,如果我在不断的从后方入队,从前方出队,然后后方入队到了队尾,而前方却还有空余,从逻辑角度,这种状态并非队满,但是确确实实不能再从数组末尾添加了,怎么办呢?

按照常规逻辑,应该从数组首部进行添加,没有毛病,但是怎么实现呢。聪明的你可能想到用ifelse判断一下如果真的到了数组尾部就从数组首部添加,是个办法,但是你不感觉有点费劲么?

更简单的办法是利用取余这种运算的优势进行实现,设想一下,假设队列最大长度只有二十,那么,如果我每次运算都模除20,会是什么结果,假设back的值为19,然后模除20,结果还是19,也就是说,back的值如果是19即小于20的话,那么一定是真实值19,但是如果back的值是20呢,我不可能再从Queue[20]的位置添加数据了,但是我模除20,我惊异的发现,结果是零,正好就是队首,同理,front也是一个道理,这样,我可以不用管back的取值,只要在每次增删改查的时候都对back和front取模就可以解决这个问题。

Queue[back % 20] = 100;
back++;//仍旧back++,但是在取Queue种元素的时候进行取余的操作

front++;//同样的道理,删除操作也是对front++
Queue[front % 20] = 100;//取队首元素的时候,进行取余操作

总结一下,涉及到实际的数组操作的时候进行取余,中括号中取余,括号外照常加。

你可能会想过用取余来直接改变back和front的值,从而省去了存取时取余的操作,或许你最开始就是这么想的,我也是这么想的,但是到了后面就出现一个问题,当两个数一样大时,我是应该当队空呢,还是当队满呢(自己思考是不是这么个道理,队空的时候也是相等,队满的时候也是相等)?这涉及到一个数论上的知识叫做同余,你每次都将back和front改变成取余后的值,那么两个数实际差20时,因为取余操作,这个20被你算没了,你无法判断他俩是否真的相等,队空那是真相等,但是队满,两个数实际应该相差20。所以,我没有直接改变这两个数,而是选择了在数据存取的时候麻烦一些做了取余操作。

所以按照这个逻辑,这个队空时,两个数是相等的
队满时,两个数正好差了20,为了保险起见,写了大于等于

if(back == front)
	printf("队空!\n");
if(back - front >= 20)
	printf("队满!\n");

这就是队列的顺序存储的基本操作

现在我们讨论链式存储的操作,链表型的

链表操作相较于数组操作,新增的内容并不多,只是多了一个尾指针,别的还是链表的操作。

首先创建一个链表

typedef struct Node
{
	int data;
	struct Node* next;
}node, *pnode;
int main()
{
	pnode LinkList = (pnode)malloc(sizeof(node));
	LinkList->next = NULL;
	LinkList->data = 0;
	pnode toil = LinkList;//多了一个尾指针
	return 0;
}

有了尾指针,增加操作就可以分几步来进行,其本质就是尾插法,插到队尾去。

pnode ptr = (pnode)malloc(sizeof(node));
ptr->data = 100;
ptr->next = NULL;
toil->next = ptr;
toil = toil->next;//尾插法五行

删除也是队首删除的办法

pnode ptr = LinkList->next;
LinkList->next = LinkList->next->next;
free(ptr);

访问自然就是直接写LinkList->next就好了呗。很简单。

栈结构

栈结构是所谓先进先出,就是好比穿衣服,只能从里往外穿,脱也要从外套先脱,如果觉得有点热,我想如果不是脑洞新奇的人都会先给外套脱掉吧。。

栈结构就相比的粗暴得多,既然是先进先出,那岂不正中数组下怀

#include<stdio.h>
int main()
{
	int Stack[20] = {0};
	int top = 0;//这就是一个栈
	
	Stack[top] = 100;
	top++;//这就是入栈
	
	top--;//这就是出栈
	
	Stack[top - 1] = 99;//注意这才是真正的栈顶元素。
	return 0;
}

完了。。数组就这么实现。。简单粗暴

链表呢,链表也有这种轻便的操作,直接上代码

#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
	int data;
	struct Node* next;
}node, *pnode;
int main()
{
	pnode LinkList = (pnode)malloc(sizeof(node));
	LinkList->next = NULL;
	LinkList->data = 0;//这就是一个链表
	
	pnode ptr = (pnode)malloc(sizeof(node));
	ptr->data = 100;
	ptr->next = LinkList->next;
	LinkList->next = ptr;//经典的头插法插入

	ptr = LinkList->next;
	LinkList->next = LinkList->next->next;
	free(ptr);//删除元素也很经典
	
	LinkList->next->data = 100;//这就是访问,也很简单
	
	return 0;
}

当然了,栈和队列的实现相对方便,但是使用他们将是数据结构和算法的重要课题,这两种结构是递归转化为非递归程序的重要辅助工具。熟练使用它们是学算法的基础。

好了,对于线性结构的基础知识我已经介绍完了,欢迎大家批评指正。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值