赞
踩
目录
概述: 栈(Stack)是一种具有特定存储结构的数据结构,它遵循先进后出的原则,即最后进入的元素首先被访问和删除。栈的基本操作包括进栈,出栈,销毁栈。栈分为数组栈和链式栈。
概述: 数组栈用数组来存数据
- typedef int STDtype;
- typedef struct stack
- {
- STDtype* data; // 指向栈数组的指针 定义为指针方便动态扩展内存
- int top; // 记录栈的顶点
- int capacity; // 记录栈的容量
- } ST;
初始化栈就是把容量数据顶点都置零
- // 初始化栈
- void InitStack(ST* ps)
- {
- ps->capacity = 0;
- ps->data = NULL;
- // top 可以给0或者1 给0意味着top 指向栈顶数据的下一个,所以在用时先给数据再加一
- // top 等于 -1时 要先加1 再给数据
- ps->top = 0; // ps->top = -1;
- }
- // 插入栈数据
- // 1. 先判断是否满容量 满容量就扩容
- // 2. 扩容完检测是否扩容成功,不成功打印错误信息并退出程序
- // 3. 扩容成功指向新的空间 更新新的容量大小
- // 4. 插入数据
- void StackPush(ST* ps, STDtype e)
- {
- assert(ps);
- if (ps->capacity == ps->top) // 当栈顶等于容量时说明 没有容量了
- {
- // 没有容量有两种情况 1. 没有一个容量,四个容量 2. 容量达到设定容量了
- // 申请新的容量 为原来容量的2 倍。
- int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
- STDtype* temp = realloc(ps->data, sizeof(STDtype) * newCapacity); // // 给ps->data 扩容,如果ps->data 为空则申请空间
- if (temp == NULL)
- {
- printf("realloc faill\n");
- exit(-1);
- }
- ps->data = temp; // 扩容成功 指向新的空间
- ps->capacity = newCapacity; // 更新新的容量
- }
-
- ps->data[ps->top] = e;
- ps->top++;
- }

- // 判断栈是否为空
- bool StackEmpty(ST* ps)
- {
- return ps->top == 0;
- }
注意: 上面的函数返回值为bool C语言中要使用bool 需要包含头文件 #include <stdbool.h>
1.5出栈操作
- // 出栈
- // 1. 判断栈是否为空,为空则返回0
- // 2. 不为空用变量记录栈顶数据
- // 3. 栈顶指针减1
- // 4. 返回开始记录的栈顶数据
- STDtype StackPop(ST* ps)
- {
- if (StackEmpty(ps)) // 判断是否为空
- {
- return 0; // 为空返回0
- }
- else
- {
- STDtype temp = ps->data[ps->top-1]; // 记录栈顶数据
- ps->top--; // 栈顶指针减减
- return temp; // 返回记录的栈顶
- }
- }

- // 销毁栈
- // 1.释放指针
- // 2.将指针和容量指向0
- void StackDestory(ST* ps)
- {
- // 释放指针维护的数据
- free(ps->data);
- // 数据指针和容量指向0
- // 数据指针将其设置为0 意味着将其指向空地址。
- ps->data = NULL;
- ps->capacity = 0;
- }
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdbool.h>
- #include <assert.h>
-
- typedef int STDtype;
- typedef struct stack
- {
- STDtype* data;
- int top;
- int capacity;
- } ST;
-
- // 初始化栈
- void InitStack(ST* ps)
- {
- ps->capacity = 0;
- ps->data = NULL;
- ps->top = 0; // 这里初始化为0 指向栈
- }
-
- // 插入栈数据
- // 1. 先判断是否满容量 满容量就扩容
- // 2. 扩容完检测是否扩容成功,不成功打印错误信息并退出程序
- // 3. 扩容成功指向新的空间 更新新的容量大小
- // 4. 插入数据
- void StackPush(ST* ps, STDtype e)
- {
- assert(ps);
- if (ps->capacity == ps->top) // 当栈顶等于 容量时说明 没有容量了
- {
- // 没有容量有两种情况 1. 没有一个容量,四个容量 2. 容量达到设定容量了
- // 申请新的容量 为原来容量的2 倍。
- int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
- STDtype* temp = realloc(ps->data, sizeof(STDtype) * newCapacity); // // 给ps->data 扩容,如果ps->data 为空则申请空间
- if (temp == NULL)
- {
- printf("realloc faill\n");
- exit(-1);
- }
- ps->data = temp; // 扩容成功 指向新的空间
- ps->capacity = newCapacity; // 更新新的容量
- }
-
- ps->data[ps->top] = e;
- ps->top++;
- }
-
- // 判断栈是否为空
- bool StackEmpty(ST* ps)
- {
- return ps->top == 0;
- }
-
- // 出栈
- // 1. 判断栈是否为空,为空则返回0
- // 2. 不为空用变量记录栈顶数据
- // 3. 栈顶指针减1
- // 4. 返回开始记录的栈顶数据
- STDtype StackPop(ST* ps)
- {
- if (StackEmpty(ps)) // 判断是否为空
- {
- return 0; // 为空返回0
- }
- else
- {
- STDtype temp = ps->data[ps->top-1]; // 记录栈顶数据
- ps->top--; // 栈顶指针减减
- return temp; // 返回记录的栈顶
- }
- }
-
- // 销毁栈
- // 1.释放指针
- // 2.将指针和容量指向0
- void StackDestory(ST* ps)
- {
- // 释放指针维护的数据
- free(ps->data);
- // 数据指针和容量指向0
- // 数据指针将其设置为0 意味着将其指向空地址。
- ps->data = NULL;
- ps->capacity = 0;
- }
-
- void test01(void)
- {
- ST ps;
- InitStack(&ps); // 初始化栈
-
- // 栈插入数据
- StackPush(&ps, 1);
- StackPush(&ps, 2);
- StackPush(&ps, 3);
- StackPush(&ps, 4);
-
- // 若栈不为空则出栈并打印
- while (!StackEmpty(&ps))
- {
- printf("%d ", StackPop(&ps));
- }
-
- StackDestory(&ps); // 释放栈空间
- }
-
- int main(void)
- {
- test01();
- return 0;
- }

概述: 当两个栈的空间需求相反时,如一个增加另一个减少,适合使用两栈共享空间
让一个栈的栈底为数组的始端即下标为0 处,另外一个为数组的末端即下标为n-1处。如果有元素就增加就像两端靠拢。
- // 定义两栈的数据结构
- #define MAXSIZE 20
- typedef int STDtype;
- typedef struct Stack
- {
- STDtype data[MAXSIZE];
- int top1; // 指向栈1的顶指针
- int top2; // 指向栈2的顶指针
- }ST;
初始top1 可以使它指向-1 也可以使它指向0 指向-1 后再后面的运算时要先移动再运算,top2 也样的。
- // 初始化栈
- // 1. 把栈顶指针top1 指向-1 所以在进栈的时候要先移动指针再 进行赋值操作
- // 栈顶指针top2 指向的是MAXSIZE 进栈时也要先移动指针再赋值
- void StackInit(ST* ps)
- {
- ps->top1 = -1;
- ps->top2 = MAXSIZE;
- }
-
- // 置空栈
- // 置空栈就是让他们的顶指针复位
- void StackDestory(ST* ps)
- {
- ps->top1 = -1;
- ps->top2 = MAXSIZE;
- }

当top1 和 top2 都指向初始值时 此时栈为空
- // 判断栈是否为空
- // 为空返回1
- bool StackEmpty(ST* ps)
- {
- return (ps->top1 == -1 && ps->top2 == MAXSIZE);
- }
- // 插入元素到栈中
- // 参数1 栈的指针
- // 参数2 插入的数据
- // 参数3 给第几个栈插入
- // 方法
- // 1. 先判断容量是否满了 满了就退出
- // 2. 判断是插入到哪个栈
- // 3. 插入数据
-
- void StackPush(ST* ps, STDtype e, int stackNumber)
- {
- if (ps->top1 + 1 == ps->top2)
- {
- printf("栈满不能进栈\n");
- exit(-1);
- }
-
- if (stackNumber == 1)
- {
- ps->data[++ps->top1] = e; // 栈顶指针指向的是前一个 每次进栈时要先移动栈顶指针再赋值
- }
- else if (stackNumber == 2)
- {
- ps->data[--ps->top2] = e; // 栈顶指针指向的是后一个 每次进栈时要先移动栈顶指针再赋值
- }
- }

- // 出栈
- // 参数1 栈指针
- // 参数2 第几个栈
- // 方法
- // 1. 先判断栈是否为空
- // 2. 选择哪个栈出栈
- //
- STDtype StackPop(ST* ps, int stackNumber)
- {
- if (StackEmpty(ps))
- {
- printf("栈为空不能出栈\n");
- return -1;
- }
-
- if (1 == stackNumber)
- {
- return ps->data[ps->top1 --]; // 进完栈 栈顶指针指向的是当前的值 所以出完栈后再进行减减
- }
- else if (2 == stackNumber)
- {
- return ps->data[ps->top2++]; // 进完栈 栈顶指针指向的是当前的值 所以出完栈后再进行加加
- }
- }

注意:出栈时的顶指针移动顺序和初始化时的值有关。
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <stdio.h>
- #include <stdbool.h>
-
- // 定义两栈的数据结构
- #define MAXSIZE 20
- typedef int STDtype;
- typedef struct Stack
- {
- STDtype data[MAXSIZE];
- int top1; // 指向栈1的顶指针
- int top2; // 指向栈2的顶指针
- }ST;
-
- // 初始化栈
- // 1. 把栈顶指针1 指向-1 所以在进栈的时候要先移动指针再 进行赋值操作 栈顶指针2 指向的是MAXSIZE 进栈时也要先移动指针再赋值
- void StackInit(ST* ps)
- {
- ps->top1 = -1;
- ps->top2 = MAXSIZE;
- }
-
- // 把栈置空
- void StackDestory(ST* ps)
- {
- ps->top1 = -1;
- ps->top2 = MAXSIZE;
- }
-
- // 判断栈是否为空
- // 为空返回1
- bool StackEmpty(ST* ps)
- {
- return (ps->top1 == -1 && ps->top2 == MAXSIZE);
-
- }
-
- // 插入元素到栈中
- // 参数1 栈的指针
- // 参数2 插入的数据
- // 参数3 给第几个栈插入
- // 方法
- // 1. 先判断容量是否满了 满了就退出
- // 2. 判断是插入到哪个栈
- // 3. 插入数据
-
- void StackPush(ST* ps, STDtype e, int stackNumber)
- {
- if (ps->top1 + 1 == ps->top2)
- {
- printf("栈满不能进栈\n");
- exit(-1);
- }
-
- if (stackNumber == 1)
- {
- ps->data[++ps->top1] = e; // 栈顶指针指向的是前一个 每次进栈时要先移动栈顶指针再赋值
- }
- else if (stackNumber == 2)
- {
- ps->data[--ps->top2] = e; // 栈顶指针指向的是后一个 每次进栈时要先移动栈顶指针再赋值
- }
- }
-
- // 出栈
- // 参数1 栈指针
- // 参数2 第几个栈
- // 方法
- // 1. 先判断栈是否为空
- // 2. 选择哪个栈出栈
- //
- STDtype StackPop(ST* ps, int stackNumber)
- {
- if (StackEmpty(ps))
- {
- printf("栈为空不能出栈\n");
- return -1;
- }
-
- if (1 == stackNumber)
- {
- return ps->data[ps->top1 --]; // 进完栈 栈顶指针指向的是当前的值 所以出完栈后再进行减减
- }
- else if (2 == stackNumber)
- {
- return ps->data[ps->top2++]; // 进完栈 栈顶指针指向的是当前的值 所以出完栈后再进行加加
- }
- }
-
- void test01(void)
- {
- ST ps;
- // 初始化栈
- StackInit(&ps);
-
- // 给栈插入数据
- StackPush(&ps, 1, 1);
- StackPush(&ps, 2, 1);
- StackPush(&ps, 3, 1);
-
- StackPush(&ps, 3, 2);
- StackPush(&ps, 2, 2);
- StackPush(&ps, 1, 2);
-
- // 出栈
- printf("%d ", StackPop(&ps, 1));
- printf("%d ", StackPop(&ps, 1));
- printf("%d ", StackPop(&ps, 1));
-
- printf("%d ", StackPop(&ps, 2));
- printf("%d ", StackPop(&ps, 2));
- printf("%d ", StackPop(&ps, 2));
-
- // 清除栈
- StackDestory(&ps);
- }
-
- int main(void)
- {
- /*test01();*/
- ST ps;
- test01();
- // 清除栈
- StackDestory(&ps);
- return 0;
- }

概述:只允许在一端插入数据 在另一端删除数据的线性表,队列具有先进先出
入队列:进行插入操作的一端称为队尾
出队列:进行删除一端为队头
队列可以用数组来实现也可以用链来实现,但链实现的队列更加节省空间。如下是用链表实现的队列。
- // 链队列的数据结构
- typedef int QDataType;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QDataType data;
- }QueueNode;
通常为了操作队列会定义一个头指针和尾指针,队列在此头结点创建,一般会用结构体封装一下。如下
- // 定义头指针和尾指针
- typedef struct Queue
- {
- QueueNode* head;
- QueueNode* tail;
- }Queue;
初始化队列就是初始化好头结点和尾结点。如下
- // 初始化
- void QueueInit(Queue* pq)
- {
- assert(pq);
- pq->head = NULL;
- pq->tail = NULL;
- }
依次释放队列的每个元素,和单链表的释放类似
- // 释放结点信息
- void QueueDestory(Queue* pq)
- {
- assert(pq);
- QueueNode* cur = pq->head; // 定义工作指针从头开始释放结点
- while (cur != NULL) // 目前结点不为空就一直执行
- {
- QueueNode* next = cur->next; // 临时存储下一个结点
- free(cur); // 释放当前结点
- cur = next; // 指向存储的结点
- }
- pq->head = pq->tail = NULL; // 释放完给头尾结点指针置空
- }
入队列就是从队尾插入数据
- // 插入数据到队列
- // 创建新结点 给结点数据赋值
- // 链接新结点到队尾
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode)); // 创建新的结点
- newnode->data = x; // 给结点数据赋值
- newnode->next = NULL; // 结点的尾置空
- if (pq->head == NULL) // 当插入的是第一个结点时
- {
- pq->head = pq->tail = newnode; // 头结点和尾结点 都指向这个节点
- }
- else
- {
- pq->tail->next = newnode; // 尾结点的下一个指向新结点
- pq->tail = newnode; // 更新尾结点为刚刚插入的新结点
- }
- }

注意: 当第一次插入数据时要判断
判断头是否为空就能得出队列是否为空
- // 判断是否为空
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
- return pq->head == NULL;
- }
删除的是头结点或头的后继结点,看队列定义的时候头结点是否也存储信息,如存储就从头开始删除,如不存储就从头的下一结点删除。
如下头结点也是有效结点,所以出队列就是删除现有头结点再更新头结点。
- // 删除结点
- void QueuePop(Queue* pq)
- {
- assert(pq);
- // 不等于空 等于就保错 提示
- assert(!QueueEmpty(pq));
- QueueNode* next = pq->head->next; // 临时保存要删结点的下一结点
- free(pq->head); // 删除结点
- pq->head = next; // 更新头结点到下一结点
- if (pq->head == NULL)
- {
- pq->tail = NULL; // 当删除完结点head 为空时 tail 也应该为空 不然tail 是一个野指针
- }
- }
前一个结点相当于队头的数据
- // 返回队列前一个数
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->head->data;
- }
注意:出队列就是先返回队头一个结点,然后再删除队头一个结点。即上面两步的结合组成出队列操作也可以把上面两步合为一步。
- // 返回队列后一个数
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->tail->data;
- }
队列的大小可以在定义的时候多定义一个变量来记录,也可以现用现求。如下是现用现求。
- int QueueSize(Queue* pq)
- {
- assert(pq);
- int size = 0; // 定义记录变量
- QueueNode* cur = pq->head; // 定义工作结点
- while (cur) // 当当前结点为空时 退出
- {
- ++size; // 记录大小
- cur = cur->next; // 更新节点
- }
- return size; // 返回大小
- }
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include <stdio.h>
- #include <stdbool.h>
- #include <assert.h>
-
- // 队列的数据结构
- typedef int QDataType;
- typedef struct QueueNode
- {
- struct QueueNode* next;
- QDataType data;
- }QueueNode;
-
- // 定义头指针和尾指针
- typedef struct Queue
- {
- QueueNode* head;
- QueueNode* tail;
- }Queue;
-
- // 初始化
- void QueueInit(Queue* pq)
- {
- assert(pq);
- pq->head = NULL;
- pq->tail = NULL;
- }
- // 释放结点信息
- void QueueDestory(Queue* pq)
- {
- assert(pq);
- QueueNode* cur = pq->head;
- while (cur != NULL)
- {
- QueueNode* next = cur->next;
- free(cur);
- cur = next;
- }
- pq->head = pq->tail = NULL;
- }
-
- // 插入数据到队列
- // 创建新结点 给结点数据赋值
- //
- void QueuePush(Queue* pq, QDataType x)
- {
- assert(pq);
- QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
- newnode->data = x;
- newnode->next = NULL;
- if (pq->head == NULL)
- {
- pq->head = pq->tail = newnode;
- }
- else
- {
- pq->tail->next = newnode;
- pq->tail = newnode;
- }
-
- }
- // 判断是否为空
- bool QueueEmpty(Queue* pq)
- {
- assert(pq);
- return pq->head == NULL;
- }
-
- // 删除结点前一个节点 从头结点开始删
- void QueuePop(Queue* pq)
- {
- assert(pq);
- // 不等于空 等于就保错 提示
- assert(!QueueEmpty(pq));
- QueueNode* next = pq->head->next;
- free(pq->head);
- pq->head = next;
- if (pq->head == NULL)
- {
- pq->tail = NULL; // d当head 出完为空时 tail 也应该为空 不然tail 是一个野指针
- }
- }
-
- // 返回队列前一个数
- QDataType QueueFront(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->head->data;
- }
-
- // 返回队列后一个数
- QDataType QueueBack(Queue* pq)
- {
- assert(pq);
- assert(!QueueEmpty(pq));
-
- return pq->tail->data;
- }
- int QueueSize(Queue* pq)
- {
- assert(pq);
- int n = 0;
- QueueNode* cur = pq->head;
- while (cur)
- {
- ++n;
- cur = cur->next;
- }
- return n;
- }
-
- void test01()
- {
- Queue q; // 创建头指针
- QueueInit(&q);
-
- QueuePush(&q, 1);
- QueuePush(&q, 2);
- QueuePush(&q, 3);
- QueuePush(&q, 4);
- QueuePush(&q, 5);
-
- QueuePop(&q); // 删除一个节点 从前面开始删
- printf("%d \n", QueueSize(&q));
-
- while (!QueueEmpty(&q)) // 遍历队列 遍历一个删除一个
- {
- QDataType front = QueueFront(&q);
- printf("%d ", front);
- QueuePop(&q); // 删除一个队列
- }
-
- QueueDestory(&q); // 销毁队列
- }
-
- int main(void)
- {
- test01();
- return 0;
- }

运行结果
- 4
- 2 3 4 5
上一篇:C语言数据结构单链表详解
下一篇:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。