目录
- 1. 栈
- 1.1 栈的概念及结构
- 1.2 栈的实现
- 2. 队列
- 2.1 队列的概念及结构
- 2.2 队列的实现
正文开始
1. 栈
1.1 栈的概念及结构
栈是线性表的一种,这种数据结构只允许在固定的一端进行插入和删除元素的操作,进行数据插入和删除的一端称为栈顶,另一端称为栈底。
这种数据结构的操作方式可以类比为弹匣,存取子弹只能在固定的一端(图源网络,侵删)
栈对应的两个操作:
- 压栈:栈的插入操作,又叫进栈/压栈/入栈,插入的数据在栈顶。
- 出栈:栈的删除操作,删除的数据同样在栈顶。
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则
1.2 栈的实现
下面我们通过c语言来实现栈的结构以及相关操作。
实现栈的结构,我们可以通过顺序表或链表来实现,相对而言通过顺序表实现更好一些,因为顺序表对于尾部的操作效率更高一些。
对于栈我们提供出栈、入栈、判空等接口,下面来看具体代码:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int STDataType;//通过顺序表来实现栈
typedef struct Stack {STDataType* _a;int _top; //栈顶int _capacity; //容量
}Stack;//初始化栈
void StackInit(Stack* ps) {assert(ps);ps->_a = NULL;ps->_capacity = ps->_top = 0;
}//检查空间是否足够
void CheckCapacity(Stack* ps)
{if (ps->_top == ps->_capacity){int NewCapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;STDataType* NewA = (STDataType*)realloc(ps->_a, sizeof(STDataType) * NewCapacity);if (NewA == NULL){perror("realloc fail");}ps->_capacity = NewCapacity;ps->_a = NewA;}
}//入栈
void StackPush(Stack* ps, STDataType data) {assert(ps);CheckCapacity(ps);*(ps->_a + ps->_top) = data;ps->_top++;
}//出栈
void StackPop(Stack* ps)
{assert(ps && ps->_top > 0);ps->_top--;
}//获取栈顶元素
STDataType StackTop(Stack* ps)
{assert(ps && ps->_top > 0);return *(ps->_a + ps->_top - 1);
}//获取栈中有效元素个数
int StatckSize(Stack* ps) {assert(ps);return ps->_top;
}//检测栈是否为空,若为空则返回非零结果
//若不为空则返回0
int StackEmpty(Stack* ps) {return ps->_top == 0;
}//输出栈
void PrintStack(Stack* ps)
{for (int i = 0; i < ps->_top; i++){printf("%d ", *(ps->_a + i));}printf("\n");
}//销毁栈
void StackDestroy(Stack* ps) {assert(ps);free(ps->_a);ps->_a = NULL;ps->_capacity = ps->_top = 0;
}
值得注意的是,关于_top的指向问题,有两种情况:
- _top指向栈顶元素:初始化时应将_top指向 -1 的位置,因为它若指向 0 的位置,无法判断它是否为空。
- _top指向栈顶元素的下一个位置:初始化时应将_top指向 0 的位置,这样就代表栈为空。上述代码就是按照这种方式的实现的。
2. 队列
2.1 队列的概念及结构
队列:一种只允许在一端进行插入数据操作,在另一端进行删除数据操作的线性表,队列符合先进先出FIFO(First In First Out)
这种数据结构可以类比为排队出门,先进队伍的先出门。
队列对应的两个操作:
- 入队列:进行插入操作的一端称为队尾。
- 出队列:进行删除操作的一端称为队头。
2.2 队列的实现
队列也可以通过顺序表或链表来实现,相对而言使用链表更优一些,因为使用顺序表在队头出队列时效率较低。
下面我们通过c语言来实现对应的结构和相关操作:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int QDataType;// 链式结构:表示队列的节点
typedef struct QListNode
{struct QListNode* _next;// 指针域QDataType _data; // 数据
}QNode;// 在处理队列时,需要用到队列的队头指针和队尾指针
// 所以直接将描述一个队列的信息封装到一个结构体中
// 这样在处理队列时,只需要一个结构体变量就足够了
// 队列的结构
typedef struct Queue
{QNode* _front; // 队头QNode* _rear; // 队尾int size; // 节点个数
}Queue;// 初始化队列
void QueueInit(Queue* q) {assert(q);q->_front = q->_rear = NULL;q->size = 0;
}// 申请节点
QNode* QListBuyNode(QDataType x)
{QNode* newNode = (QNode*)malloc(sizeof(QNode));if (newNode == NULL){perror("malloc fail");exit(1);}newNode->_data = x;newNode->_next = NULL;return newNode;
}// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{assert(q);QNode* newNode = QListBuyNode(data);if (q->size == 0){q->_front = q->_rear = newNode;}else {q->_rear->_next = newNode;q->_rear = q->_rear->_next;}q->size++;
}// 队头出队列
void QueuePop(Queue* q)
{assert(q && q->size);if (q->size == 1){free(q->_front);q->_front = q->_rear = NULL;}else {QNode* PopNode = q->_front;q->_front = q->_front->_next;free(PopNode);PopNode = NULL;}q->size--;
}// 获取队列头部元素
QDataType QueueFront(Queue* q) {assert(q && q->size);return q->_front->_data;
}// 获取队列队尾元素
QDataType QueueBack(Queue* q) {assert(q && q->size);return q->_rear->_data;
}// 获取队列中有效元素个数
int QueueSize(Queue* q) {assert(q);return q->size;
}// 检测队列是否为空
// 如果为空返回非零结果
// 如果非空返回0
int QueueEmpty(Queue* q) {assert(q);return q->size == 0;
}// 销毁队列
void QueueDestroy(Queue* q)
{assert(q);while (q->_front) {QueuePop(q);}q->_front = NULL;q->_rear = NULL;
}
完