赞
踩
可以先了解线性表的无头单向非循环链表(单链表)---数据结构——单链表的增加、删除、查找、修改,详细解析_昵称就是昵称吧的博客-CSDN博客,和线性表的带头双向循环链表---数据结构:带头双向循环链表——增加、删除、查找、修改,详细解析_昵称就是昵称吧的博客-CSDN博客
目录
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除。
操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据也在栈顶。
可以将栈理解为弹夹,压栈就是压入子弹,出栈就是打出子弹。
结构如下所示:
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
- typedef int StackDataType;//重定义类型名
-
- typedef struct Stack
- {
- StackDataType* a;//创建一个指针,用来接收数组首元素地址,数组里面的类型是StackDataType
- //用数组来描述栈,因为出栈和入栈都是一个地方
- int top;//相当于数组的下标,可以是0或-1,这里我们选择0
- int capacity;//数组的容量
- }ST;
结构体里用一个指针接收动态开辟的内存空间的地址。
- //初始化结构体,将结构体变量的地址传递过来,是地址传递,这样就能改变实参的内容了
- void StackInit(ST* ps)
- {
- assert(ps);//地址传递,断言好习惯
-
- //动态开辟一个数组的空间,数组的元素类型是StackDataType,将首元素地址赋给指针a
- ps->a = (StackDataType*)malloc(sizeof(StackDataType) * 4);
- if (ps->a == NULL)//判断是否成功开辟动态空间
- {
- perror("the mistake is");
- }
-
- ps->top = 0; //相当于数组下标,可以初始化0或-1,这里用0
- //用0数组下标从0开始,用-1数组下标从-1开始
-
- ps->capacity = 4; //从开辟的动态空间可以看出数组的容量是4
- }

通过地址传递,初始化结构体里的内容。
- //销毁结构体里的内容,地址传递,才能改变实参的内容
- void StackDestroy(ST* ps)
- {
- assert(ps);
-
- free(ps->a);//释放动态开辟的内存
- ps->a = NULL;
-
- ps->top = 0; //将结构体里的 相当于数组下标top 和 容量capacity 置为0
- ps->capacity = 0;
- }
通过释放函数free释放动态开辟的内存,达到销毁栈空间的目的。
- //入栈,相当于往数组填充数据x
- void StackPush(ST* ps,StackDataType x)
- {
- assert(ps);
-
- if (ps->top == ps->capacity)//申请的动态空间满了,数组的元素和容量相等,需要增容
- {
- //用relloc函数申请动态空间,创建临时指针变量是怕动态开辟空间失败,增容一般是上一次的2倍
- //注意realloc和malloc函数里的参数,realloc有两个,malloc只有一个
- StackDataType* tmp = (StackDataType*)realloc(ps->a, sizeof(StackDataType) * ps->capacity * 2);
-
- if (tmp == NULL)//判断是否成功开辟动态空间
- {
- perror("the mistake is");
- }
- else //增容成功
- {
- //printf("增容成功\n");
- ps->a = tmp;//将新开辟的动态空间地址赋给指针a
- ps->capacity *= 2;
- }
- }
-
- //不需要增容或增容之后
- ps->a[ps->top] = x;//想数组填充数据
- ps->top++; //数组下标加1,方便后面下一次入栈;
- //同时top从0开始,填充一个数据top就自增一次,所以数据个数和top相等
- }

需要注意的是,如果栈存放的数据,达到了栈空间动态开辟的容量,便需要增容的操作。
- //打印栈里的数据
- void StackPrint(ST* ps)
- {
- int i = 0;
- for (i = 0; i < ps->top; i++)//因为top初始化的时候是0,存一个数据,top就自增1一次
- {
- printf("%d ", ps->a[i]);
- }
- printf("\n");
- }
类似打印数组里的数据,遍历一次。
- //出栈,相当于数组最后一个数据除去,一般除数组最后一个元素的方法就是下标减1,效果上相当于除去了
- void StackPop(ST* ps)
- {
- assert(ps);
-
- assert(ps->top > 0);//防止数据里面没有数据,就不需要出栈
- //之前入栈的时候,因为top从0开始,填充一个数据top就自增一次,所以数据个数和top相等
-
- ps->top--;//一般除数组最后一个元素的方法就是下标减1,效果上相当于除去了
- }
解释请看注解,非常详细。
- //返回栈顶的元素,相当于数组的最后一个元素,元素的类型是StackDataType
- StackDataType StackTop(ST* ps)
- {
- assert(ps);
-
- assert(ps->top>0);//防止数据里面没有数据,就不用返回
- //因为top从0开始,填充一个数据top就自增1一次,删除一个数据top就自减1一次,所以数据个数和top相等
-
- return ps->a[ps->top - 1];//因为下标top是初始化从0开始的,所以数组最后一个元素的下标是top-1
- }
解释请看注解,非常详细。
- //返回栈里的元素的个数,相当于数组里的元素的个数,就是我们前面说的top,个数的类型就是整型int
- int StackSize(ST* ps)
- {
- assert(ps);
-
- return ps->top;//栈里的元素个数和top相等
- }
解释请看注解,非常详细。
- //判断栈里是否有元素,用布尔型bool,真就为1,假就为0
- bool StackEmpty(ST* ps)
- {
- assert(ps);
-
- return ps->top == 0;//ps->top==0 为真,则返回值是1;为假,返回值是0
- }
解释请看注解,非常详细。
这是总代码的地址所在,需要的可以自取:放代码: 代码 - Gitee.com
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一端称为队头。
结构如下所示:
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
而链表中,使用单链表的结构实现会更好。因为队列结构的入队列相当于单链表的尾插,出队列相当于单链表的头删。
- typedef int QDataType;//类型重命名
-
- typedef struct QueueNode//节点用结构体表示
- {
- struct QueueNode* next;//指向下一个节点
- QDataType data;//队列中存储的数据
- }QNode;
为了方便使用单链表的尾插(入队列),创建一个指针tail,指向链表的尾节点;
为了方便使用单链表的头删(出队列),创建一个指针head,指向链表的第一个节点。
用结构体放入这两个变量,方便使用。
- typedef struct Queue//定义一个结构体,用来存放链表的第一个节点和尾节点
- //第一个节点用于对头出(头删),尾节点用于队尾入(尾插)
- {
- QNode* head;
- QNode* tail;
- }Queue;
- //初始化第一个节点和尾节点
- void QueueInit(Queue* pq)
- {
- assert(pq);
- pq->head = pq->tail = NULL;//初始化两个节点都用空指针NULL
- }
我们会使用这两个指针,所以要先初始化指针head和tail,不然会变成野指针。
- // 队尾入(尾插)
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QNode* newnode = (QNode*)malloc(sizeof(QNode));//开辟新节点
- if (newnode == NULL)//防止动态空间开辟失败
- {
- printf("malloc fail\n");
- exit(-1);//结束函数
- }
-
- //开辟空间成功
- newnode->data = x;//将想放的数据放入节点里
- newnode->next = NULL;//单链表节点里的指针要先赋空指针NULL
-
- if (pq->tail == NULL)//判断链表里是否有节点
- {
- pq->head = pq->tail = newnode;//没有节点,将新节点赋给第一个节点head和尾节点tail
- }
- else
- {
- pq->tail->next = newnode;//有节点,直接单链表尾插
- pq->tail = newnode;//尾节点向后走一位,方便写一次队尾入(尾插)
- }
- }

节点是通过动态内存开辟的,这样可以使避免内存空间的浪费,队尾入首先要判断链表里是否有节点,若没有,动态开辟的第一个节点的地址,要先赋给指针 head 和 tail,因为我们想让指针head指向第一个节点,指针tail指向尾节点。
- // 队头出
- void QueuePop(Queue* pq)
- {
- assert(pq);
-
- assert(pq->head);//防止链表里没有节点,就不需要队头出(头删)
-
- // 1、一个
- // 2、多个
- if (pq->head->next == NULL)//如果链表里只有一个节点的时候
- {
- free(pq->head);//直接free点这节点的空间
- pq->head = pq->tail = NULL;//将指针head和tail置空
- }
- else//链表有两个及以上的节点
- {
- QNode* next = pq->head->next;//先找到第一个节点head的下一个节点next
- free(pq->head);//释放第一个节点的空间
- pq->head = next;//将节点next作为新的第一个节点,方便下一次的队列出(头删)
- }
- }

对头出(头删)的时候,我们要注意一种情况:如果链表只有 一个节点 或者 一直对头出(头删),直到最后一个节点,这个时候如果没有上面的 if 语句判断链表是否只有一个节点,那么当通过free(pq->head)最后一个节点的空间释放的时候,指针tail就会变成野指针,因为它所指向的那块空间被释放了,因为只有一个节点的时候,指针head和tail指向的都是这一个节点,所以要考虑这种情况。
- //访问对头的数据(链表里的第一个节点的数据)
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
-
- assert(pq->head);//防止链表没有节点,没有节点就访问不了结构体里的数据,不然就会报错
-
- return pq->head->data;//访问对头(第一个节点)里的数据
- }
解释请看注解,非常详细。
- //访问对尾的数据(链表里的尾节点的数据)
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
-
- assert(pq->head);//防止链表没有节点,没有节点就访问不了结构体里的数据,不然就会报错
-
- return pq->tail->data;//访问对尾(尾节点)里的数据
- }
解释请看注解,非常详细。
- //队列(链表)里的元素个数
- int QueueSize(Queue* pq)
- {
- assert(pq);
- int size = 0;
- QNode* cur = pq->head;
- while (cur)//遍历一次,直到链表最后一个节点里的指针(NULL)
- {
- ++size;
- cur = cur->next;
- }
-
- return size;
- }
解释请看注解,非常详细。
- //判断链表里是否有元素
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
- return pq->head == NULL;//看第一个节点是否为空指针,
- //为空就是没有元素,不为空就是有元素
- }
解释请看注解,非常详细。
- //销毁空间,即返回每个节点开辟的空间
- void QueueDestory(Queue* pq)
- {
- assert(pq);
-
- QNode* cur = pq->head;
- while (cur)//遍历一次,直到链表最后一个节点里的指针(NULL)
- {
- QNode* next = cur->next;
- free(cur);//依次释放每个节点的空间
- cur = next;
- }
-
- pq->head = pq->tail = NULL;//释放完,指针head和 tail置空
- }
因为节点是动态开辟的,所以通过函数free释放动态内存空间。
这是总代码的地址所在,需要的可以自取:放代码: 代码 - Gitee.com
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。