数据结构——栈和队列

栈和队列其实都是线性表的应用和推广



首先还是先看一下分类情况

       |- 顺序栈
栈-----|- 链栈
       |- 共享栈

         |- 循环队列
队列-----|- 链式队列
         |- 双端队列
         
         |- 一维数组
数组-----|
         |- 多维数组:压缩矩阵、稀疏矩阵

一、栈

1. 栈的定义

在这里插入图片描述

  • 栈:一端进行插入和删除
    • 栈顶:允许插入和删除的一端;
    • 栈底:固定的,不能插入和删除。
  • 先进后出
  • 栈的基本操作:
函数名称实现操作
InitStack(&S)初始化一个空栈
StackEmpty(S)判断栈是否为空
Push(&S, x)进栈
Pop(&S, &x)出栈
GetTop(S, &x)读栈顶元素
ClearStack(&S)销毁栈

2. 栈的顺序存储结构

对于n个不同元素进栈,出栈序列的个数为:在这里插入图片描述

(1)栈的存储类型描述

一组地址连续的存储单元,top指针指向当前栈顶位置。

#define MaxSize 50
typedef struct{
	ElemType data[MaxSize];
	int top;
}SqStack;
  • 栈顶指针:初始:S.top=-1
  • 栈顶元素:S.data[S.top]
  • 进栈:指针先加1,再送值到栈顶元素
  • 出栈:先取栈顶元素,指针再减1
  • 栈空:S.top == -1
  • 栈满:S.top == MaxSize - 1
  • 栈长:S.top+1

(2)初始化

void InitStack(&S){
	s.top = -1;
}

(3)判栈空

bool StackEmpty(S){
	if(s.top == -1){
		return true;
	}
	else{
		return false;
	}
}

(4)进栈

bool Push(SqStack &S, ElemType x){
	if(s.top == MaxSize-1){
		return false;
	}
	S.data[++S.top] = x;	//指针先加1,再入栈
	return true;
}

(5)出栈

bool Pop(SqStack &S, ElemType &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top--];	//先出栈,指针再减1
	return true;
}

(6)读栈顶元素

bool GetTop(SqStack S, ElemType &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];
	return true;
}

3. 共享栈

在这里插入图片描述

  • 两个栈共享一个一维数据空间,为了更有效的利用空间。存取数据时间复杂度O(1)。
  • 栈底分别设在共享空间栈的两端,栈顶向中间延伸
  • 栈顶指针:都指向栈顶元素。
  • 栈空:0号栈为空:top0 = -1;1号栈为空:top1 = MaxSize。
  • 栈满:top1 - top0 = 1.
  • 进栈:0号栈:top0先加1再赋值;1号栈:top1先减1再赋值。
  • 出栈:0号栈:top0先赋值再减1;1号栈:top1先赋值再加1。

4. 栈的链式存储结构

在这里插入图片描述

  • 链栈优点便于多个栈共享存储空间和提高效率,且不存在栈满上溢
  • 所有操作在表头进行。通常链栈没有头结点,Lhead指向栈顶元素。
  • 带头结点和不带头结点的链栈,实现方面不同。
typedef struct  LinkNode{
	ElemType data;
	struct LinkNode *next;
} *LiStack;
  • 插入结点x
x->next = top;
top = x;
  • Pop操作,并将出栈元素保存在x
x = top->data;
top = top->next;

二、队列

1. 队列的定义

在这里插入图片描述

  • 队列:只允许在一端进行插入,在另一端进行删除
    • 队头:允许删除的一端;
    • 队尾:允许插入的一端。
  • 先进先出
  • 常见的基本操作:
函数名称实现操作
InitQueue(&Q)初始化一个空队
QueueEmpty(Q)判断队列是否为空
EnQueue(&Q, x)入队
DeQueue(&Q, &x)出队
GetHead(Q, &x)读队头元素

2.队列的顺序存储结构

(1)队列的顺序存储类型

  • 一块连续的存储单元,两个指针 head(队头) 和 rear(队尾) 。
  • 队头指针:指向队头元素;
  • 队尾指针:指向队尾元素的下一个位置。
    (也可以让队尾指针指向队尾元素,队头指针指向队头元素的前一个元素。)
#define MaxSize 50
typedef struct{
	ElemType data[MaxSize];
	int front,rear;
}SqQueue;
  • 初始状态(队空条件):Q.front = Q.rear = 0
  • 进队:先送值到队尾元素,再将队尾指针加1。
  • 出队:先取队头元素值,再将队头指针加1。

3. 循环队列

  • 当队首指针 Q.front = MaxSize-1 后,再前进一个位置就自动到0。
  • 初始: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.front == Q.rear
      • 队列中元素个数:(Q.rear - Q.front + MaxSize) % MaxSize
    • 增加一个表示元素个数的变量。
      • 这两种情况都有Q.front == Q.rear
      • 队满:Q.size == MaxSize
      • 队空:Q.size == 0
    • 增加 tag 数据成员
      • 队满:tag=1, 因插入导致 Q.front == Q.rear
      • 队空:tag=0, 因删除导致 Q.front == Q.rear

(1)初始化

void InitQueue(&Q){
	Q.rear = Q.front = 0;
}

(2)判队空

bool isEmpty(Q){
	if(Q.rear == Q.front){
		return true;
	}
	else
		return false;
}

(3)入队

bool EnQueue(SqQueue &Q, ElemType x){
	if((Q.rear+1) % MaxSize == Q.front){	//判满
		return false;
	}
	Q.data[Q.rear] = x;
	Q.rear = (Q.rear + 1) % MaxSize;
	return true; 
}

(4)出队

bool DeQueue(SqQueue &Q, ElemType &x){
	if(Q.rear == Q.front){	//判空
		return false;
	}
	x = Q.data[Q.front];
	Q.front = (Q.front + 1) % MaxSize;
	return true;
}

4. 队列的链式存储结构

在这里插入图片描述
在这里插入图片描述

(1)队列的链式存储类型

  • 链队列:同时带有队头指针和队尾指针的单链表。队头指针指向队头结点,队尾指针指向队尾结点。
typedef struct{		//链式队列结点
	ElemType data;
	struct LinkNode *next;
}LinkNode;
typedef struct{		//链式队列
	LinkNode *front,*rear;
}LinkQueue;
  • 当Q.front == NULL 且 Q.rear == NULL 时,链式队列空
    不设头结点的链式队列操作比较麻烦。

(2)初始化

void InitQueue(LinkQueue &Q){
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));	//建立头结点
	Q.front->next = NULL;	//初始为空
}

(3)判队空

bool IsEmpty(LinkQueue Q){
	if(Q.front == Q.rear)
		return true;
	else
		return false;
}

(4)入队

void EnQueue(LinkQueue &Q, ElemType x){
	s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;		//创建新结点
	s->next = NULL;		//插入到链尾
	Q.rear->next = s;
	Q.rear = s;
}

(5)出队

bool DeQueue(LinkQueue &Q, ElemType &x){
	if(Q.front == Q.rear)	//空队
		return false;
	p = Q.front->next;
	x = p->data;
	Q.front->next = p->next;
	if(Q.rear==p)		//若原队列只有一个结点,删除后变空
		Q.rear = Q.front;
	free(p);
	return true;
}

5. 双端队列

在这里插入图片描述

  • 两端都可以进行入队和出队的操作,逻辑结构仍是线性结构。
    • 进队:前端进的元素排在后端进的元素前面。
    • 出队:无论前后端,先出的排在后出的元素前面。

(1)输出受限的双端队列

  • 一端进行插入、删除;另一端只能插入
    在这里插入图片描述

(2)输入受限的双端队列

  • 一端进行插入、删除;另一端只能删除
    在这里插入图片描述

三、栈和队列的应用

1. 栈在括号匹配中的应用

算法思想
(1)初始设置空栈,顺序读入括号;
(2)若是右括号,①让其置于栈顶的最急迫期待得以消解;②不合法的情况则退出程序(括号序列不匹配);
(3)若是左括号:作为一个新的更急迫的期待压入栈中;
算法结束时,栈为空,否则括号序列不匹配

2. 栈在表达式求值中的应用

(1)通过后缀表达式计算表达式的值的过程:

(1)顺序扫描表达式的每一项;
(2)①如果该项是操作数,将其压入栈中;
②如果该项是操作符,则连续从栈中退出两个操作数Y和X,形成 “X<操作符>Y”,并将计算结果重新压入栈中;
(3)当表达式的所有项都扫描并处理完后,栈顶存放的是最终的计算结果。
在这里插入图片描述

(2)中缀转后缀的过程:

3. 栈在递归中的应用

通过栈可以实现递归向非递归的转换。(也可以不用栈,用迭代方式消除)

4. 队列在层次遍历中的应用

过程描述
(1)根结点入队;
(2)若队空(所有结点处理完毕),则遍历结束;否则重复③
(3)队列中第一个结点出队,并访问。若其有左孩子,则将左孩子入队;若其有右孩子,则将右孩子入队,返回②。
在这里插入图片描述

5. 队列在计算机系统中的应用

(1)解决主机与外部设备之间速度不匹配的问题。(缓冲区)
(2)解决由多用户引起的资源竞争问题。
(3)页面替换算法。

6.栈其他应用

进制转换、迷宫求解

四、特殊矩阵的压缩矩阵

1. 数组的定义

数组是线性表的推广。

2. 数组的存储结构

(1)一维数组 A[0,1,…,n-1] ,其存储结构关系式为:

LOC(ai) = LOC(a0) + (i) * L

其中L是每个元素所占的存储单元。
(2)二维数组 A行下标和列下标分别为 [l1, h1]、[l2, h2]
行优先
在这里插入图片描述

LOC(a i,j) = LOC(a 0,0) + [(i - l1) * (h2 - l2 + 1) + (j - l2)] * L

列优先
在这里插入图片描述

LOC(a i,j) = LOC(a l1,l2) + [(j - l2) * (h1 - l1 + 1) + (i - l1)] * L

3. 矩阵的压缩存储

压缩矩阵:为多个相同的元素值分配一个存储空间,对零元素不分配存储空间,目的是为了节省存储空间。
特殊矩阵:具有许多相同矩阵元素或零元素,,并分布有规律。

(1)对称矩阵

关于主对角线对称,可以将矩阵 A[1…n][1…n] 存放在一维数组 B[n*(n+1)/2] 中。
aij 在 B中的下标k:

k = i * (i-1) / 2 + j -1; ( i>=j )
k = j * (j-1) / 2 + i -1; ( i<j )

(2)三角矩阵

在这里插入图片描述

下三角矩阵
可以将矩阵 A[1…n][1…n] 存放在一维数组 B[n*(n+1)/2 + 1] 中。
在这里插入图片描述

k = i * (i-1) / 2 + j -1; ( i>=j )
k = n * (n-1) / 2 ; ( i<j )

上三角矩阵
可以将矩阵 A[1…n][1…n] 存放在一维数组 B[n*(n+1)/2 + 1] 中。
在这里插入图片描述

k = (i - 1)* (2 * n - + 2) / 2 + (j - i); ( i<=j )
k = n * (n-1) / 2 ; ( i>j )

(3)三对角矩阵

在这里插入图片描述
在这里插入图片描述

也成为带状矩阵,对于任一 aij,当 | i - j | > 1 时,aij = 0.
用压缩存储时,可以按行优先存放。
aij 在数组B中存放的下标 k:

k = 2 * i + j - 3

反之,B中第k个元素,可得:

i = [ (k+1)/3 +1 ] ;(向下取整)
j = k - 2*i + 3:

4. 稀疏矩阵

在这里插入图片描述

  • 矩阵元素个数 s 相对于矩阵中非零元素个数 t 非常多,即 s >> t。
  • 存储方式
    • ①将非零元素及其相应的行和列构成一个三元组(行标,列标,值)。
    • ②也可以用十字链表来存储。
  • 稀疏矩阵压缩存储之后便失去了随机存取的特性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值