赞
踩
目录
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
需要用结构体创建一个栈,这个结构体需要包括栈的基本内容(栈,栈顶,栈的容量)。
- typedef int STDataType;//栈中存储的元素类型(这里用整型举例)
-
- typedef struct Stack
- {
- STDataType* a;//栈
- int top;//栈顶
- int capacity;//容量,方便增容
- }Stack;
然后需要一个初始化函数,对刚创建的栈进行初始化。
- //初始化栈
- void StackInit(Stack* pst)
- {
- // 使用断言检查输入指针是否有效,确保不为空
- assert(pst);
-
- // 动态分配内存,为栈分配空间,初始可存储4个STDataType类型的元素
- pst->a = (STDataType*)malloc(sizeof(STDataType)* 4);
-
- // 初始化栈顶指针为0,表示栈当前为空,没有元素
- pst->top = 0;
-
- // 设置栈的初始容量为4,即栈目前最多可以容纳4个元素
- pst->capacity = 4;
- }
因为栈的内存空间是动态开辟出来的,当我们使用完后必须释放其内存空间,避免内存泄漏。
- void StackDestroy(Stack* pst)
- {
- // 断言检查传入的栈结构体指针是否有效,确保它是指向一个已初始化的栈
- assert(pst != NULL);
-
- // 使用 free 函数释放栈中存储元素的数组所占用的内存防止内存泄漏
- free(pst->a);
-
- // 将栈的元素数组指针置为 NULL,这是一种安全措施,避免栈被释放后仍被当作有效指针使用
- pst->a = NULL;
-
- // 将栈顶指针 top 置为 0,这表示栈内已无任何元素,栈处于空状态
- pst->top = 0;
-
- // 将栈的容量 capacity 置为 0,代表栈当前没有存储能力,明确表示栈已经被销毁
- pst->capacity = 0;
- }
进行入栈操作前,我们需要检测栈的当前状态,若已满,则需要先对其进行增容,然后才能进行入栈操作。
- void StackPush(Stack* pst, STDataType x)
- {
-
- // 断言检查栈结构体指针是否有效
- assert(pst);
-
- // 判断栈是否已满(栈顶索引等于栈的当前容量)
- if (pst->top == pst->capacity)
- {
- // 如果栈满,则尝试重新分配内存,使栈容量翻倍
- STDataType* tmp =(STDataType*)realloc(pst->a,sizeof(STDataType)*pst->capacity * 2);
-
- // 检查重新分配内存是否成功
- if (tmp == NULL)
- {
- // 若失败,输出错误信息,并调用 exit 函数结束程序运行
- printf("realloc fail\n");
- exit(-1);
- }
-
- // 若重新分配内存成功,更新栈的元素数组指针和容量
- pst->a = tmp;
- pst->capacity *= 2;
- }
-
- // 将新元素存入栈顶位置
- pst->a[pst->top] = x;
-
- // 更新栈顶索引,表示栈顶已移动至新的元素处
- pst->top++;
- }
出栈操作比较简单,即让栈顶的位置向下移动一位即可。但需检测栈是否为空,若为空,则不能进行出栈操作。
- void StackPop(Stack* pst)
- {
- // 断言检查栈结构体指针是否有效
- assert(pst);
-
- // 断言检查栈是否为空,若为空则无法弹出元素
- assert(!StackEmpty(pst));
-
- // 栈顶指针向下移一位,相当于删除栈顶元素
- // 注意:这里假设移除元素后不需要返回其值,且不涉及内存释放
- pst->top--;
-
- // 注:在实际应用中,若栈顶元素包含动态分配的内存,此处还需要额外处理释放该内存
- }
获取栈顶元素,即获取栈的最上方的元素。若栈为空,则不能获取。
- STDataType StackTop(Stack* pst)
- {
- // 断言检查栈结构体指针是否有效
- assert(pst);
-
- // 断言检查栈是否为空,若为空则无法获取栈顶元素
- assert(!StackEmpty(pst));
-
- // 返回栈顶元素的值,栈顶元素的位置是栈顶指针减1
- // 注意:此处只读取栈顶元素而不改变栈的状态
- return pst->a[pst->top - 1];
- }
检测栈是否为空,即判断栈顶的位置是否是0即可。若栈顶是0,则栈为空。
- bool StackEmpty(Stack* pst)
- {
- // 断言检查栈结构体指针是否有效
- assert(pst);
-
- // 根据栈顶指针 top 的值判断栈是否为空
- // 当栈顶指针为 0 时,表示栈中没有元素,即栈为空
- return pst->top == 0;
- }
因为top记录的是栈顶,使用top的值便代表栈中有效元素的个数。
- int StackSize(Stack* pst)
- {
- // 断言检查栈结构体指针是否有效
- assert(pst);
-
- // 栈顶指针 top 的值直接表示栈中有效元素的个数
- // 因为每当有元素入栈时,top加1;元素出栈时,top减1
- // 所以,top的值就反映了当前栈内实际存储了多少个元素
- return pst->top;
- }
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列遵守先进先出 FIFO(First In First Out)的原则。
入队列:队列的插入操作叫做入队列,进行插入操作的一端称为队尾。
出队列:队列的删除操作叫做出队列,进行删除操作的一端称为队头。
队列在生活中的运用非常广泛。很大一部分医院、营业厅以及餐厅等场所都存在队列的应用。
例如,医院的采血环节的流程就运用了队列。在医院,如果我们要去抽血,我们首先要在旁边的抽号机抽取自己的编号,当某一个抽血窗口叫到你时,你便可以去抽血了。
在这个过程中,当你在抽号机抽取到某一个编号,那么这个编号这时就从队尾入队列,当某一窗口抽血结束后,会在该队列中拿走一个编号并叫该编号的人到这个窗口接受抽血,那么这时这个编号就从队头出队列。
首先我们需要创建一个结点类型,类型包含了该结点的数据和指向下一结点的指针。
- typedef int QDataType;
-
- typedef struct QListNode
- {
- // 指向下一个节点的指针
- struct QListNode* next;
-
- // 节点所存储的数据
- QDataType data;
- } QListNode;
队列与普通链表又有所不同,普通链表只需要知道链表的头指针,而队列的信息包括了队头和队尾,所以我们需要再创建一个结构体用于存放队列的队头和队尾。
- typedef struct Queue
- {
- // 队列头指针
- // 指向队列的第一个节点,当队列为空时,head 和 tail 都指向 NULL
- QListNode* head;
-
- // 队列尾指针
- // 指向队列的最后一个节点,每次有新元素入队时,都会更新 tail 指针
- QListNode* tail;
- } Queue;
然后需要一个初始化函数,对刚创建的队列进行初始化。
- void QueueInit(Queue* pq)
- {
- // 断言检查传入的队列结构体指针是否有效
- assert(pq);
-
- // 将队列的头指针 head 初始化为 NULL,表示队列当前为空,无元素
- pq->head = NULL;
-
- // 将队列的尾指针 tail 也初始化为 NULL,与 head 一致,表示队列为空
- // 这样设计的原因是,在队列为空时,头尾指针均指向 NULL;而在有元素入队时,tail 指针将指向最后一个元素
- pq->tail = NULL;
- }
队列中的每一个结点所占用的内存空间都是动态开辟的,当我们使用完队列后需要及时释放队列中的每一个结点。
- void QueueDestroy(Queue* pq)
- {
- // 断言检查传入的队列结构体指针是否有效
- assert(pq);
-
- // 创建一个临时指针 cur,初始化为队列的头节点
- QListNode* cur = pq->head;
-
- // 循环遍历队列中的每一个节点
- while (cur)
- {
- // 将 cur 的下一个节点赋值给临时指针 next,保存下一个节点的地址
- QListNode* next = cur->next;
-
- // 释放当前节点 cur 的内存
- free(cur);
-
- // 移动 cur 指针至下一个节点
- cur = next;
- }
-
- // 清空队列的头指针和尾指针,将它们都设置为 NULL
- pq->head = NULL;
- pq->tail = NULL;
- }
入队列,即申请一个新结点并将其链接到队尾,然后改变队尾的指针指向即可。需要注意的是:若队列中原本无数据,那么我们只需让队头和队尾均指向这个新申请的结点即可。
- void QueuePush(Queue* pq, QDataType x)
- {
- // 断言检查队列结构体指针是否有效
- assert(pq);
-
- // 分配内存创建一个新的节点
- QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
-
- // 检查内存分配是否成功
- if (newnode == NULL)
- {
- // 若内存分配失败,输出错误信息并退出程序
- printf("malloc fail\n");
- exit(-1);
- }
-
- // 将新节点的数据域设置为要插入的元素值 x
- newnode->data = x;
-
- // 新节点的下一节点指针初始化为 NULL
- newnode->next = NULL;
-
- // 判断队列是否为空
- if (pq->head == NULL)
- {
- // 若队列为空,则新节点既是头节点也是尾节点
- pq->head = pq->tail = newnode;
- }
- else
- {
- // 若队列非空,将现有尾节点的下一节点指向新节点
- pq->tail->next = newnode;
-
- // 更新队列尾指针,使之指向新插入的节点
- pq->tail = newnode;
- }
- }
出队列,即释放队头指针指向的结点并改变队头指针的指向即可。若队列中只有一个结点,那么直接将该结点释放,然后将队头和队尾置空即可。
- void QueuePop(Queue* pq)
- {
- // 断言检查队列结构体指针是否有效
- assert(pq);
-
- // 断言检查队列是否为空,若为空则不能进行出队操作
- assert(!QueueEmpty(pq));
-
- // 判断队列中剩余元素数量
- if (pq->head->next == NULL)
- {
- // 如果队列只剩一个元素,则释放该元素,并将头尾指针都设为 NULL
- free(pq->head);
- pq->head = NULL;
- pq->tail = NULL;
- }
- else
- {
- // 如果队列中还有多个元素,则释放头节点,然后将头指针指向下一个节点
- QListNode* next = pq->head->next;
- free(pq->head);
- pq->head = next;
- }
- }
获取队列头部元素,即返回队头指针指向的数据即可。
- 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;
- }
检测队列是否为空,即判断队头指针指向的内容是否为空。
- bool QueueEmpty(Queue* pq)
- {
- // 断言检查队列结构体指针是否有效
- assert(pq);
-
- // 判断队列是否为空的条件是:头指针(pq->head)是否为 NULL
- // 如果头指针为 NULL,则队列为空,返回 true
- // 否则,队列非空,返回 false
- return pq->head == NULL;
- }
队列中有效元素个数,即队列中的结点个数。我们只需遍历队列,统计队列中的结点数并返回即可。
- int QueueSize(Queue* pq)
- {
- // 断言检查队列结构体指针是否有效
- assert(pq);
-
- // 初始化计数器 count 为 0
- int count = 0;
-
- // 创建一个临时指针 cur,初始化为队列的头节点
- QListNode* cur = pq->head;
-
- // 遍历队列,直到 cur 为空(即遍历完队列)
- while (cur)
- {
- // 计数器加一,表示找到了一个有效节点
- count++;
-
- // 将 cur 指针移向下一个节点
- cur = cur->next;
- }
-
- // 返回计数器 count 的值,即队列中有效元素的个数
- return count;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。