栈
栈的概念及结构栈的实现 队列
队列的概念及结构队列的实现循环队列
本章学习线性表中的栈和队列。
栈 栈的概念及结构栈是一种特殊的线性表,只允许在固定的一端进行插入和删除元素 *** 作。进行数据插入和删除 *** 作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
栈是线性表的一种,那么栈的存储形式可以选择连续存储或者链式存储。
数组栈:数组尾作为栈顶,尾插、尾删效率高,缓存命中率高,缺点是空间不够需要扩容。
链式栈:头结点作为栈顶,可以选择单链表;如果尾结点作为栈顶,那么选择双向链表,否则删除、插入效率低。
单链表栈
双向链表栈
二者更推荐数组栈。
下面我们来实现动态数组栈
定义数组栈结构体类型
#pragma once #include#include #include #include typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; //栈初始化 void StackInit(ST* ps); //销毁栈 void StackDestroy(ST* ps); //数组容量检查 void CheckCapacity(ST* ps); //入栈 void StackPush(ST* ps, STDataType data); //出栈 void StackPop(ST* ps); //返回栈顶元素 STDataType StackTop(ST* ps); //栈大小 int StackSize(ST* ps); //栈是否为空 bool StackEmpty(ST* ps);
接口实现
#include "stack.h" void StackInit(ST* ps) { assert(ps); ps->a = NULL; ps->top = 0;//top指向栈顶元素的下一个 //ps->top = -1;//top指向栈顶数据 ps->capacity = 0; } void StackDestroy(ST* ps) { assert(ps); free(ps->a); ps->a = NULL; ps->top = 0; ps->capacity = 0; } //判断空间大小 void CheckCapacity(ST* ps) { if (ps->capacity == ps->top) { int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity; //扩容 STDataType* tmp = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType)); if (tmp == NULL) { exit(-1); } ps->a = tmp; ps->capacity = newCapacity; } } //入栈 void StackPush(ST* ps, STDataType data) { assert(ps); CheckCapacity(ps); ps->a[ps->top] = data; ps->top++; } //出栈 void StackPop(ST* ps) { assert(ps); assert(ps->top > 0); ps->top--; } //返回栈顶元素 STDataType StackTop(ST* ps) { assert(ps); assert(ps->top > 0); return ps->a[ps->top - 1]; } //返回栈的大小 int StackSize(ST* ps) { assert(ps); return ps->top; } //栈是否为空 bool StackEmpty(ST* ps) { assert(ps); //if (ps->top == 0) //{ // return false; //} //else //{ // return true; //} return ps->top == 0; }
练习
括号匹配问题
队列是限制在两端进行插入 *** 作和删除 *** 作的线性表,允许进行存入 *** 作的一端称为“队尾”,允许进行删除 *** 作的一端称为“队头”。
队列的特点:
1.队列具有先进先出FIFO(First In First Out) 或者后进后出LILO(Last In Last Out)
2.队列只能在队头和队尾进行数据 *** 作
队列也可以用数组和链表的结构实现,但是使用链表的结构实现更优一些,因为如果使用数组的结构(顺序存储,从头开始存储,中间不能跳跃),那么入队在数组尾入数据,出队列在数组头上出数据,效率会比较低。
队列的链式存储
对于单链表,我们只定义了头指针,没有定义尾指针,因为即使定义尾指针,尾插方便了,但是尾删仍然不方便,所以对于单链表定义尾指针没有意义。
对于队列,一端进一端出,所以我们定义两个指针,并且把链表的尾作为队尾,元素从这端进,链表的头作为队头,数据从这端出。
//队列数据元素的类型 typedef int QDataType; //队列中数据元素结构体 typedef struct QueueNode { struct QueueNode* next; QDataType data; }QueueNode; //队列结构体 typedef struct Queue { QueueNode* head;//队头指针 QueueNode* tail;//队尾指针 //size_t size;//队列元素个数 }Queue;
对于队列的实现,我们使用链式存储,选择没有哨兵位,这里,我们定义了两个指针,所以选择使用结构体(存放多个成员),当然也可以不使用结构,定义两个指针作为参数(需传入二级指针)。
需要实现如下接口:
//队列初始化 void QueueInit(Queue* pq); //销毁队列 void QueueDestroy(Queue* pq); //入队 void QueuePush(Queue* pq,QDataType x); //出队 void QueuePop(Queue* pq); //获取队头元素 QDataType QueueFront(Queue* pq); //获取队尾元素 QDataType QueueBack(Queue* pq); //获取队列中有效元素个数 int QueueSize(Queue* pq); //检测队列是否为空,如果为空返回非零结果,如果非空返回0 bool QueueEmpty(Queue* pq);
代码如下:
//队列初始化 void QueueInit(Queue* pq) { //我们需要改变结构体的内容(需要改变结构成员head,tail),所以传入结构体指针 //对于结构的指针成员,要初始化 assert(pq); pq->head = NULL; pq->tail = NULL; } //销毁队列 void QueueDestroy(Queue* pq) { assert(pq); QueueNode* cur = pq->head; while (cur != NULL) { QueueNode* next = cur->next; free(cur); cur = NULL; } 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) { //1.队列为空 pq->head = pq->tail = newnode; } else { //2.队列不为空,尾部直接插入 pq->tail->next = newnode; pq->tail = newnode; } } //判断队列是否为空 bool QueueEmpty(Queue* pq) { assert(pq); //队列为空时 head == NULL return pq->head == NULL; } //出队 void QueuePop(Queue* pq) { assert(pq);//结构体指针是不可能为空的,结构体成员指针指向NULL,与结构体指针为空没有任何关系 //1.队列空 //assert(pq->head!=NULL); assert(!QueueEmpty(pq)); QueueNode* next = pq->head->next; free(pq->head); pq->head = next; if (pq->head == NULL) { //2.只有一个元素 head = tail 注意处理tail变成野指针的情况 //链表已经删完了 pq->tail = NULL; } } //获取队尾元素 QDataType QueueBack(Queue* pq) { assert(pq); //assert(pq->tail!=NULL); assert(!QueueEmpty(pq)); return pq->tail->data; } //获取队头元素 QDataType QueueFront(Queue* pq) { assert(pq); //assert(pq->head!=NULL); assert(!QueueEmpty(pq)); return pq->head->data; } //获取队列元素个数 int QueueSize(Queue* pq) { assert(pq); QueueNode* cur = pq->head; int sz = 0; //遍历队列 while (cur!= NULL) { sz++; cur = cur->next; } return sz; }
练习
用队列实现栈
用栈实现队列
循环队列特点:
1.先进先出
2.大小固定,首尾相接
3.可以重复利用之前的空间
环形队列可以使用数组实现,也可以使用循环链表实现。
对于循环队列,无论是数组实现还是循环链表实现,都要多开一个空间,也就是说,要是一个存k个数据的循环队列,就要开k+1个空间,否则没办法实现判空和判满。
数组实现
循环队列为空:front == tail
循环队列满了:(tail+1)%(k+1) == front
tail指向队尾元素的下一个位置,是队尾的下标,front是对头的下标,k是循环队列能够存储数据的大小
(1)空队列
(2)入队
(3)出队
(4)队列满了
tail = 0,front = 1,k = 6
(tail+1)%(k+1) = 1%7 = 1 = front,队列满了。
循环链表实现
(1)空队列
(2)入队
(3)队列满了
tail->next == front;
循环队列练习
循环队列
本章完。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)