湖大计学考研系列文章目录
- 22湖南大学计算机学硕上岸经验
- 22湖南大学 866 数据结构真题(回忆版)
- 866数据结构重点内容
- 866 数据结构模拟题(一)及解析
- 866数据结构笔记 - 第一章 绪论
- 866数据结构笔记 - 第二章 线性表
- 866数据结构笔记 - 第三章 栈和队列
- 866数据结构笔记 - 第四章 串
- 866数据结构笔记 - 第五章 树和二叉树
- 866数据结构笔记 - 第六章 图
- 866数据结构笔记 - 第七章 查找
- 866数据结构笔记 - 第八章 排序
目录
重点内容
22年866真题:3个选择+1个简答,共计16分
- 栈和队列能在哪进行插入和删除?
- 栈和队列的特点?
- 顺序栈的两种定义以及操作(入栈、出栈、获取栈顶、判断栈空、栈满)
- 共享栈的栈满条件?
- 循环队列队满和元素个数公式?
- N个元素进栈,出栈个数?
- 栈和队列顺序题目?(必考)
- 如何将队列逆置?
- 括号匹配思想和代码?
- 数组和特殊矩阵(对称、三角、三对角)存储注意事项?(优先带值法)
一、基本概念
- 栈:操作受限的线性表,只能在栈顶进行插入和删除。
- 队列:操作受限的线性表,只能在队尾插入,队头删除。
- 特性:
- 栈:后进先出/先进后出(LIFO)
- 队列:先进先出(FIFO)
二、栈
1.顺序栈
用静态数组实现,并且需要记录栈顶指针。
2.定义
#define MaxSize 10 //定义栈中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //静态数组存放栈中元素
int top; //栈顶元素
}SqStack;
3.两种实现
1. 初始化 top=-1(最常用,top指向栈顶元素)
- 入栈:S.data[++S.top] = x
- 出栈:x = S.data[S.top--]
- 获取栈顶元素:x = S.data[S.top]
- 判断栈空:S.top == -1
- 判断栈满:S.top == MaxSize - 1
2. 初始化 top=0(top指向栈顶后一位置)
- 入栈:S.data[S.top++] = x
- 出栈:x = S.data[--S.top]
- 获取栈顶元素:x = S.data[--S.top]
- 判断栈空:S.top == 0
- 判断栈满:S.top == MaxSize
4.共享栈
利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数组空间,将两个栈的栈底 分别设置在共享空间的两端,两个栈顶向共享空间的中间延伸。
- 0号栈指针初始化:top=-1
- 1号栈指针初始化:top=MaxSize
- 判断栈满:top1-top0=1或者top0+1=top1
5.链栈
采用链式存储的栈称为链栈。链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。
struct Linknode{
int data; //数据域
Linknode *next; //指针域
}Linknode,*LiStack;
typedef Linknode *Node; //结点结构体指针变量
typedef Node List; //结点结构体头指针变量
//1. 初始化
void InitStack(LiStack &L){ //L为头指针
L = new Linknode;
L->next = NULL;
}
//2.判栈空
bool isEmpty(LiStack &L){
if(L->next == NULL){
return true;
}
else
return false;
}
//3. 进栈(链栈基本上不会出现栈满的情况)
void pushStack(LiStack &L, int x){
Linknode s; //创建存储新元素的结点
s = new Linknode;
s->data = x;
//头插法
s->next = L->next;
L->next = s;
}
//4.出栈
bool popStack(LiStack &L, int &x){
Linknode s;
if(L->next == NULL) //栈空不能出栈
return false;
s = L->next;
x = s->data;
L->next = L->next->next;
delete(s);
return true;
}
三、队列
1.顺序队列
# define MaxSize 10; //定义队列中元素的最大个数
typedef struct{
ElemType data[MaxSize]; //用静态数组存放队列元素
//连续的存储空间,大小为——MaxSize*sizeof(ElemType)
int front, rear; //队头指针和队尾指针
}SqQueue;
2.循环队列
将循环队列臆造为一个环状的空间,即把存储队列元素的表从逻辑上视为一个环,称为循环队列。
初始:Q.front = Q.rear = 0;
队首指针进1:Q.front = (Q.front + 1) % MaxSize
队尾指针进1:Q.rear = (Q.rear + 1) % MaxSize —— 队尾指针后移,当移到最后一个后,下次移动会到第一个位置
队列长度:(Q.rear + MaxSize - Q.front) % MaxSize
区分队空还是队满的情况:
- 牺牲一个存储单元:队满:(Q.rear+1)%MaxSize == Q.front,队空:Q.rear==Q.front
- 增加size变量,记录队列长度:队满:Q.size==Maxsize,队空:Q.size==0
- 增加tag=0/1,标记出队入队: 队满:Q.front == Q.rear && tag == 1,队空:Q.front == Q.rear && tag == 0
3.链式队列
typedef struct LinkNode{ //链式队列结点
ElemType data;
struct LinkNode *next;
}
typedef struct{ //链式队列
LinkNode *front, *rear; //队列的队头和队尾指针
}LinkQueue;
void InitQueue(LinkQueue &Q){
//初始化时,front、rear都指向头结点
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front -> next = NULL;
}
//判断队列是否为空
bool IsEmpty(LinkQueue Q){
if(Q.front == Q.rear) //也可用 Q.front -> next == NULL
return true;
else
return false;
}
//新元素入队 (表尾进行)
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode)); //申请一个新结点
s->data = x;
s->next = NULL; //s作为最后一个结点,指针域指向NULL
Q.rear->next = s; //新结点插入到当前的rear之后
Q.rear = s; //表尾指针指向新的表尾
}
//队头元素出队
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q.front == Q.rear)
return false; //空队
LinkNode *p = Q.front->next; //p指针指向即将删除的结点 (头结点所指向的结点)
x = p->data;
Q.front->next = p->next; //修改头结点的next指针
if(Q.rear == p) //此次是最后一个结点出队
Q.rear = Q.front; //修改rear指针
free(p); //释放结点空间
return true;
}
4.双端队列
双端队列是指允许两端都可以进行入队和出队操作的队列
- 双端队列允许从两端插入、两端删除的线性表
- 如果只使用其中一端的插入、删除操作,则等同于栈
- 输入受限的双端队列:允许一端插入,两端删除的线性表
- 输出受限的双端队列:允许两端插入,一端删除的线性表
四、栈和队列应用
1.栈在括号匹配中的应用
- 遇到左括号就入栈
遇到右括号,就“消耗”一个左括号 (出栈)
匹配失败情况:扫描到右括号且栈空,则该右括号单身
扫描完所有括号后,栈非空,则该左括号单身
左右括号不匹配
#define MaxSize 10
typedef struct{
char data[MaxSize];
int top;
} SqStack;
//初始化栈
InitStack(SqStack &S)
//判断栈是否为空
bool StackEmpty(SqStack &S)
//新元素入栈
bool Push(SqStack &S, char x)
//栈顶元素出栈,用x返回
bool Pop(SqStack &S, char &x)
bool bracketCheck(char str[], int length){
SqStack S; //声明
InitStack(S); //初始化栈
for(int i=0; i<length; i++){
if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
Push(S, str[i]); //扫描到左括号,入栈
}else{
if(StackEmpty(S)) //扫描到右括号,且当前栈空
return false; //匹配失败
char topElem; //存储栈顶元素
Pop(S, topElem); //栈顶元素出栈
if(str[i] == ')' && topElem != '(' )
return false;
if(str[i] == ']' && topElem != '[' )
return false;
if(str[i] == '}' && topElem != '{' )
return false;
}
}
StackEmpty(S); //栈空说明匹配成功
}
2.队列在层次遍历中的应用
- 初始化一个辅助队列
- 根节点入队
- 若队列非空,则队头结点出队,访问该结点,依次将其左、右孩子插入队尾(如果有的话)
- 重复以上操作直至队列为空
//二叉树的结点(链式存储)
typedef struct BiTnode{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//链式队列结点
typedef struct LinkNode{
BiTNode * data;
typedef LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front, *rear;
}LinkQueue;
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q;
InitQueue (Q); //初始化辅助队列
BiTree p;
EnQueue(Q,T); //将根节点入队
while(!isEmpty(Q)){ //队列不空则循环
DeQueue(Q,p); //队头结点出队
visit(p); //访问出队结点
if(p->lchild != NULL)
EnQueue(Q,p->lchild); //左孩子入队
if(p->rchild != NULL)
EnQueue(Q,p->rchild); //右孩子入队
}
}
五、特殊矩阵的压缩
1.一维数组
数组元素 a[i]
的存放地址 = LOC + i × sizeof(ElemType)
2.二维数组
行优先存储: LOC + (i×N + j) × sizeof(ElemType)
列优先存储:LOC + (j×M + i) × sizeof(ElemType)
3.特殊矩阵压缩注意点
- 存储上三角还是下三角
- 行优先还是列优先
- 矩阵元素下标从0还是1开始
- 数组下标从0还是1开始
六、栈和队列输出序列题
n个不同元素进栈,出栈不同排列个数:
一个栈的入栈序列是a,b,c,d,e则栈的不可能的输出序列是:()
A edcbd B decba C dceab D abcde
第一次出栈d,说明什么?说明a,b,c一定早已入栈(入栈顺序决定的)。那么在出栈d以后,a,b,c的出栈顺序一定是c,b,a,而不用理会中间穿插着出栈了d后面的字符(因为可以再入栈,再出栈嘛)。所以立即选中C,不用犹豫,理由简单:d出栈了,abc一定已经入栈,那么abc只能以cba的顺序出栈,C不符合
七、特殊矩阵压缩题
有一个n×n的对称矩阵A,将其下三角部分换行存放在一维数组B中,而A[0][0]存放产B0l中,则第i+1行的对角元素A[i][i]存放于B中的()处。
A. (i+3)i/2 B. (i+1)i/2 C.(2n-i+1)/2 D.(2n-i-1)i/2
- 第0行到第i-1行一共:1+2+3+.......+i = i(i+1)/2
- 第i行有i+1个元素
- 总共:i(i+1)/2+i+1-1=i(i+3)/2(-1是因为B从0开始)
- 当然,本题可以使用特殊值法,i=1时,存放在B[2],只有A符合。
参考(具体细节见原文)
参考书目:
- 王道:《数据结构》
湖大本科: 《数据结构与算法分析( C++版)(第三版)》Clifford A. Shaffer 著,张铭、刘晓丹等译。