本文已参与「新人创作礼」活动,一起开启掘金创作之路。
数据结构
第三章 栈
堆栈
具有一定操作约束的线性表
- 只在一端(栈顶,Top)做 插入、删除
- 插入数据:入栈(Push)
- 删除数据:出栈(Pop)
- 后入先出:Last In First Out(LIFO)
堆栈的顺序存储实现
栈的顺序存储结构通常由一个一维数组和一个记录 栈顶 元素位置的变量组成。
主要操作的实现:
#include <iostream>
using namespace std;
/*
* 暂时没有写扩充容量的功能
* 后期完善时需注意
*/
// 扩充容量的步伐
#define sizestep 10
// 开始的容量大小
#define startsize 100
typedef int ElemType;
struct SeqStack {
ElemType* data; // 数据
int top; // 用于栈顶指针
int size; // 初始容量
};
// 顺序栈初始化,成功返回栈对象指针,失败返回空指针nullptr
void InitSeqStack(SeqStack& s)
{
s.size = startsize;
s.data = new ElemType[s.size];
s.top = -1;
if (!s.data) {
cout << "空间不足" << endl;
exit(-1);
}
}
// 判断栈是否为空
bool isEmptySeqStack(SeqStack& s)
{
return (s.top == -1) ? true : false;
}
// 入栈
bool pushSeqStack(SeqStack& s, ElemType e)
{
if (s.top == s.size - 1) {
return false;
}
s.data[++s.top] = e;
return true;
}
// 出栈
bool popSeqStack(SeqStack& s, ElemType& e)
{
if (s.top == -1) // if (isEmptySeqStack(s))
return false;
else {
e = s.data[s.top--];
return true;
}
}
// 取栈顶元素
bool topSeqStack(SeqStack s, ElemType& e)
{
if (s.top == -1)
return false; // 栈空
else {
e = s.data[s.top];
return true;
}
}
// 清空栈
void clearSeqStack(SeqStack& s)
{
s.top = -1;
delete[] s.data;
s.data = nullptr;
// 重新分配堆内存
s.data = new ElemType[s.size];
}
// 销毁栈
void destroySeqStack(SeqStack& s)
{
// 归零
s.size = 0;
s.top = -1;
delete[] s.data;
s.data = nullptr;
}
// 实现两栈共享空间
struct DoubleSeqStack {
ElemType* data;
int top1;
int top2;
};
void initDoubleSeqStack(DoubleSeqStack& s)
{
s.data = new ElemType[startsize];
s.top1 = -1;
s.top2 = startsize;
}
// 入栈,根据flag选择 栈1 还是 栈2
bool pushDoubleSeqStack(DoubleSeqStack& s, ElemType e, int flag)
{
// 栈满
if (s.top1 + 1 == s.top2)
return false;
if (flag == 1) {
s.data[++s.top1] = e;
}
else if (flag == 2) {
s.data[--s.top2] = e;
}
else {
cout << "flag有误" << endl;
return false;
}
return true;
}
// 出栈,根据flag选择 栈1 还是 栈2
bool popDoubleSeqStack(DoubleSeqStack& s, ElemType& e, int flag)
{
if (flag == 1) {
if (s.top1 == -1)
return false;
e = s.data[s.top1--];
}
else if (flag == 2) {
if (s.top2 == startsize)
return false;
e = s.data[s.top2++];
}
else {
cout << "flag有误" << endl;
return false;
}
return true;
}
例:用一个数组实现两个堆栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功。
#define startsize 100
typedef int ElemType;
// 实现两栈共享空间
struct DoubleSeqStack {
ElemType* data;
int top1;
int top2;
};
void initDoubleSeqStack(DoubleSeqStack& s)
{
s.data = new ElemType[startsize];
s.top1 = -1;
s.top2 = startsize;
}
// 入栈,根据flag选择 栈1 还是 栈2
bool pushDoubleSeqStack(DoubleSeqStack& s, ElemType e, int flag)
{
// 栈满
if (s.top1 + 1 == s.top2)
return false;
if (flag == 1) {
s.data[++s.top1] = e;
}
else if (flag == 2) {
s.data[--s.top2] = e;
}
else {
cout << "flag有误" << endl;
return false;
}
return true;
}
// 出栈,根据flag选择 栈1 还是 栈2
bool popDoubleSeqStack(DoubleSeqStack& s, ElemType& e, int flag)
{
if (flag == 1) {
if (s.top1 == -1)
return false;
e = s.data[s.top1--];
}
else if (flag == 2) {
if (s.top2 == startsize)
return false;
e = s.data[s.top2++];
}
else {
cout << "flag有误" << endl;
return false;
}
return true;
}
堆栈的链式存储实现
栈的链式存储结构实际上就是一个单链表,叫做 链栈。插入和删除操作只能在链栈的栈顶进行。
栈顶指针Top应该在链表的哪一头?
以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部
主要操作的实现:
#include <iostream>
using namespace std;
typedef int ElemType;
// 链栈结点
struct StackNode {
ElemType value;
StackNode* next;
};
// 链栈结构
struct LinkStack {
StackNode* top;
int length;
};
// 初始化
void initStack(LinkStack& s)
{
s.top = nullptr;
s.length = 0;
}
// 判断栈是否为空
bool isEmptyStack(LinkStack s)
{
return (!s.top) ? true : false;
}
// 入栈
bool pushStack(LinkStack s, ElemType e)
{
StackNode* temp = new StackNode;
// 空间申请失败
if (!temp) {
return false;
}
temp->value = e;
temp->next = s.top;
// 将新元素作为栈顶指针
s.top = temp;
++s.length;
return true;
}
// 出栈
bool popStack(LinkStack& s, ElemType& e)
{
// 空栈
if (!s.top)
return false;
e = s.top->value;
StackNode* temp = s.top;
s.top = s.top->next;
--s.length;
delete temp;
return true;
}
// 获取栈顶元素
bool GetTop(LinkStack s, ElemType& e)
{
if (!s.top)
return false;
e = s.top->value;
return true;
}
例:表达式求值
中缀表达式如何转换为后缀表达式:
从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
- 运算数:直接输出
- 左括号:压入堆栈
- 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出)
- 运算符:
- 若优先级大于栈顶运算符时,则把它压栈
- 若优先级小于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
- 若各对象处理完毕,则把堆栈中存留的运算符一并输出
堆栈的其他引用
- 函数调用及递归实现
- 深度优先搜索
- 回溯算法
第四章 队列
什么是队列?
具有一定操作约束的线性表
- 插入和删除操作:只能在一端插入,而在另一端删除
- 数据插入:入队
- 数据删除:出队
- 先来先服务
- 先进先出:FIFO
队列的顺序存储实现
队列的顺序存储结构通常由一个 一维数组 和一个记录队列头元素位置的变量 front以及一个记录队尾元素位置的变量 rear组成。
循环队列
堆栈空和满的判别条件是什么?
解决方案:
- 设置一个标志变量flag,当
front == rear
,且flag = 0
时为队列空;当front == rear
,且flag = 1
时为队列满。 - 当队列为空时,条件是
front == rear
;当队列满时,修改条件,保留一个元素空间front=(rear+1)%MAXSIZE
。
主要操作的实现:
#include <iostream>
using namespace std;
#define MAXSIZE 100
typedef int ElemType;
struct SeqQueue {
ElemType* data;
int front;
int rear;
};
// 初始化
void initSeqQueue(SeqQueue& Q)
{
Q.data = new ElemType[MAXSIZE];
if (!Q.data) {
exit(-1);
}
Q.front = 0;
Q.rear = 0;
}
// 入队
bool enqueue(SeqQueue& Q, ElemType e)
{
if ((Q.rear + 1) % MAXSIZE == Q.front) {
return false;
}
Q.data[Q.rear] = e; // 将e赋值给队尾
Q.rear = (Q.rear + 1) % MAXSIZE;
return true;
}
// 出队
bool dequeue(SeqQueue& Q, ElemType& e)
{
if (Q.front == Q.rear) {
return false;
}
e = Q.data[Q.front];
Q.front = (Q.front + 1) % MAXSIZE;
return true;
}
// 求队长
int lengthSeqQueue(SeqQueue Q)
{
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
// 取队头元素
bool GetHead(SeqQueue Q, ElemType& e)
{
if (Q.front == Q.rear) {
return false;
}
else {
e = Q.data[Q.front];
return true;
}
}
队列的链式存储实现
队列的链式存储结构也可以用一个 单链表 实现。插入和删除操作分别在链表的两头进行;队列指针front和rear应该分别指向链表的哪一头?
front 指向链表的头,rear指向链表的尾
注意:下面的队列中有一个头结点
主要操作的实现:
#include <iostream>
using namespace std;
typedef int ElemType;
struct QNode {
ElemType data;
QNode* next;
};
struct LinkQueue {
QNode* front; // 队头指针
QNode* rear; // 队尾指针
};
// 初始化
void initQueue(LinkQueue& Q)
{
// front和rear指向头结点,头结点不存储元素
Q.front = Q.rear = new QNode;
if (!Q.front) {
exit(-1);
}
Q.front->next = nullptr;
}
// 判断队是否为空
bool isEmptyQueue(LinkQueue& Q)
{
return (!Q.rear->next) ? true : false;
}
// 取队头元素
bool GetHeadQueue(LinkQueue& Q)
{
if (Q.front == Q.rear) {
return false;
}
else {
return Q.front->next->data;
}
}
// 入队
bool enqueue(LinkQueue& Q, ElemType e)
{
QNode* temp = new QNode;
if (!temp) {
return false;
}
temp->data = e;
temp->next = nullptr;
Q.rear->next = temp;
Q.rear = temp;
return true;
}
// 出队
bool dequeue(LinkQueue& Q, ElemType& e)
{
if (Q.front == Q.rear) {
return false;
}
QNode* temp = Q.front->next; // 临时指针指向首元结点
e = temp->data;
Q.front->next = temp->next;
if (Q.rear == temp)
Q.rear = Q.front;
delete temp;
}
// 销毁队列
void destroyQueue(LinkQueue& Q)
{
while (Q.front) {
Q.rear = Q.front->next;
delete Q.front;
Q.front = Q.rear;
}
Q.rear = Q.front = nullptr;
}
例:多项式的加减法运算实现