三、栈和队列

栈和队列

一、栈(stack)

(一)定义:

只允许在一端进行插入和删除的线性表,逻辑结构和普通线性表相同,插入、删除操作有区别。
空栈:没有元素的栈
栈底:不允许插入、删除的一端
栈顶:允许插入删除的一端
在这里插入图片描述

(二)基本操作:

1.创建和销毁:

1.1.栈的创建:

InitStack(&S):初始化一个空栈S,并分配内存空间

1.2.栈的销毁:

DestroyStack(&L):销毁栈,释放栈S所用的内存空间;

2.元素的增删查改:

2.1.进栈(增):

Push(&S,x):若栈未满,则将x加入并使之成为新栈顶

2.2.出栈(删):

Pop(&S,&x):若栈非空,则弹出栈顶元素,并用x返回【需要返回则使用&】

2.3.读栈(查):

GetTop(S,&x):读栈顶元素,若栈S非空,则用x返回栈顶的元素
读栈顶与出栈的区别:出栈删除元素,读栈顶不删除
注意:在栈中,通常只需要访问(查)栈顶元素

3.其他操作:

3.1.判栈空:

StackEmpty(S):若为空,则返回true,否则返回false

(三)常见考试题型:

已知进栈顺序,写出有几种出战顺序:
在这里插入图片描述

(四)顺序栈的实现:

顺序栈:用顺序存储方式实现的栈

1.基本操作:

1.0.顺序栈的定义:
#define MaxSize 10//栈中元素的总个数 
typedef struct{
	int data[MaxSize];//静态数组存放 
	int top;//栈顶指针 
}SqStack; 
1.1.创(初始化):
void InitSqStack(SqStack &S){
	S.top = -1;//初始化栈顶指针 
	//问题:为什么初始化栈顶指针指向-1
	//答:数组下标从0开始,初始时没有元素,栈顶指针指向0不合理,故而指向-1 	
}
1.2.增(进栈):
bool Push(SqStack &S,int x){
	if(S.top == MaxSize-1){
		return false;
	}
	S.top = S.top+1;//栈顶指针加1移向新的栈顶 
	S.data[S.top] = x;//x放入到数据域中,位置为栈顶指针指向的位置 
	return true;
} 
1.3.删(出栈):
bool Pop(SqStack &S,int &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];//将栈顶元素给x 
	S.top = S.top-1;//指针回退 
	return true;
} 
1.4.查(获取栈顶元素):
bool GetTop(SqStack &S,int &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];
	return true;
} 
1.5.判空、判满:
//判栈空
bool StackEmpty(SqStack S){
	if(S.top == -1){
		return true;
	}else{
		return false;
	}
} 

//判栈满
bool Stack(SqStack S){
	if(S.top == MaxSize){
		return true;
	}else{
		return false;
	}
} 

2.代码合集:

#include <stdio.h>
#include <stdlib.h>

//定义一个顺序栈
#define MaxSize 10//栈中元素的总个数 
typedef struct{
	int data[MaxSize];//静态数组存放 
	int top;//栈顶指针 
}SqStack; 

//初始化顺序栈
void InitSqStack(SqStack &S){
	S.top = -1;//初始化栈顶指针 
	//问题:为什么初始化栈顶指针指向-1
	//答:数组下标从0开始,初始时没有元素,栈顶指针指向0不合理,故而指向-1 	
}

//判栈空
bool StackEmpty(SqStack S){
	if(S.top == -1){
		return true;
	}else{
		return false;
	}
} 

//判栈满
bool Stack(SqStack S){
	if(S.top == MaxSize){
		return true;
	}else{
		return false;
	}
} 

//新元素入栈
bool Push(SqStack &S,int x){
	if(S.top == MaxSize-1){
		return false;
	}
	S.top = S.top+1;//栈顶指针加1移向新的栈顶 
	S.data[S.top] = x;//x放入到数据域中,位置为栈顶指针指向的位置 
	return true;
} 

//出栈
bool Pop(SqStack &S,int &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];//将栈顶元素给x 
	S.top = S.top-1;//指针回退 
	return true;
} 

//读取栈顶元素
bool GetTop(SqStack &S,int &x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];
	return true;
} 
int main(){
	SqStack S;
}

2.共享栈:

两个栈共享同一片空间

#include <stdio.h>
#include <stdlib.h>

//定义一个共享栈:有两个栈,0号栈和1号栈 
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int top0; 
	int top1;
}ShStack; 

//初始化共享栈
void InitShStack(ShStack &S){
	S.top0 = -1;//从下向上 
	S.top1 = MaxSize;//从上向下
	 
} 

//判断栈满
bool Stack(ShStack S){
	if(S.top0+1 == S.top1){
		return true;
	}else{
		return false;
	}
} 

(五)链栈的实现:

和单链表相似

二、队列(Queue)

(一)定义:

只允许在==一端插入(入队)、另一端删除(出队)==的线性表(先进先出FIFO)
在这里插入图片描述

(二)基本操作:

在这里插入图片描述

(三)顺序存储实现队列:

1.定义:

//定义一个队列
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int front;//对头指针 
	int rear;//对尾指针 
}SqQueue; 

2.创:

//初始化
void InitSeQueue(SqQueue &Q){
	//队列初始化,没有元素,对头和队尾指向同一个位置
	Q.front = Q.rear = 0; 
} 

3.销:

4.增:

//入队(插入元素) 
bool EnQueue(SqQueue &Q,int x){
	if((Q.rear+1)%MaxSize==Q.front){//判断队列已满 
		return false;
	} 
	Q.data[Q.rear] = x;//只能从队尾插入 
	Q.rear ++;
	//因为队列是对头删除,对尾插入
	//则有这种可能:队尾指针已到最大,而对头元素有删除,上句代码不适用,怎么办? 
	//用Q.rear = (Q.rear+1) % MaxSize; 替换上句代码,从而形成逻辑上的环状 
	return true;
}

在这里插入图片描述

5.删:

//出队(删除元素) 
bool DeleteQueue(SqQueue &Q,int &e){
	if(Q.front == Q.rear){//判断队列空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = (Q.front+1)%MaxSize;
	return true;
} 

6.查:

//获取对头元素的只,并用x返回
bool GetHead(SqQueue &Q,int x){
	if(Q.rear == Q.front){
		return false;
	}
	x = Q.data[Q.front];
	return true;
} 

7.判空:

//队列判空
bool SeQueue(SqQueue Q){
	if(Q.front == Q.rear){
		return true;
	}else{
		return false;
	}
} 

8.判队满:

(Q.rear+1)%MaxSize==Q.front;//循环队列判对满

9.代码合集:

#include <stdio.h>
#include <stdlib.h>

//定义一个队列
#define MaxSize 10
typedef struct{
	int data[MaxSize];
	int front;//对头指针 
	int rear;//对尾指针 
}SqQueue; 

//初始化
void InitSeQueue(SqQueue &Q){
	//队列初始化,没有元素,对头和队尾指向同一个位置
	Q.front = Q.rear = 0; 
} 

//队列判空
bool SeQueue(SqQueue Q){
	if(Q.front == Q.rear){
		return true;
	}else{
		return false;
	}
} 

//入队(插入元素) 
bool EnQueue(SqQueue &Q,int x){
	if((Q.rear+1)%MaxSize==Q.front){//判断队列已满 
		return false;
	} 
	Q.data[Q.rear] = x;//只能从队尾插入 
	Q.rear ++;
	//因为队列是对头删除,对尾插入
	//则有这种可能:队尾指针已到最大,而对头元素有删除,上句代码不适用,怎么办? 
	//用Q.rear = (Q.rear+1) % MaxSize; 替换上句代码,从而形成逻辑上的环状 
	return true;
}

//出队(删除元素) 
bool DeleteQueue(SqQueue &Q,int &e){
	if(Q.front == Q.rear){//判断队列空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = (Q.front+1)%MaxSize; 
	return true;
} 

//获取对头元素的只,并用x返回
bool GetHead(SqQueue &Q,int x){
	if(Q.rear == Q.front){
		return false;
	}
	x = Q.data[Q.front];
	return true;
} 
 int main(){
 	SqQueue Q;
 	return 0;
 }

(四)链式存储实现队列:

#include <stdio.h>
#include <stdlib.h>

//定义链式队列
typedef struct LinkNode{//链式队列结点 
   int data;
   struct LinkNode* next;
}LinkNode; 
typedef struct{
   LinkNode* front;//对头 
   LinkNode* rear;//队尾 
}LinkQueue; 

// 初始化(带头节点) 
void InitQueue0(LinkQueue &Q){
   Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
   Q.front->next = NULL; 
}
//判空
bool IsEmpty0(LinkQueue Q){
   if(Q.front == Q.rear){
   	return true;
   } else{
   	return false;
   }
} 

//初始化(不带头节点) 
void InitQueue1(LinkQueue &Q){
   Q.front = NULL;
   Q.rear = NULL;
}
//判空
bool IsEmpty1(LinkQueue Q){
   if(Q.front == NULL){
   	return true;
   }else{
   	return false;
   }
} 

//入队(带头节点) 
void EnQueue0(LinkQueue &Q,int x){
   LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode)); 
   s->data = x;
   s->next = NULL;
   Q.rear->next = s;//s插入到rear后 
   Q.rear = s;//s作为队尾 
}

//入队(不带头节点) 
void EnQueue1(LinkQueue &Q,int x){
   LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
   s->data = x;
   s->next = NULL;
   if(Q.front == NULL){//对头插入元素 
   	Q.front = s;
   	Q.rear = s; 
   }else{//非第一个元素 
   	Q.rear->next = s;
   	Q.rear = s;
   }
}

//出队(带头节点) 
bool DeQueue0(LinkQueue &Q,int &x){
   if(Q.front == Q.rear){
   	return false;
   }
   LinkNode* p = Q.front->next;//用p指向需要删除的结点 ,因为队列是对头依次删,则是头节点的下一结点 
   x = p->data;
   Q.front->next = p->next;
   if(Q.rear = p){
   	Q.rear = Q.front;
   }
   free(p);
   return true;
    
}
//出队(不带头节点) 
bool DeQueue1(LinkQueue &Q,int &x){
   if(Q.front == NULL){
   	return false;
   }
   LinkNode* p = Q.front;//p指向需要删除的结点
   x = p->data;
   Q.front = p->next;
   if(Q.rear == p){
   	Q.front = NULL;
   	Q.rear = NULL;
   } 
   free(p);
   return true;
}

int main(){
	return 0;
}

(五)双端队列:

1.定义:

在这里插入图片描述只允许在两端插入、删除的线性表,若将双端队列只从一端插入删除,则变成了栈

2.分类:

2.1.输入受限:

在这里插入图片描述

2.2.输出受限:

在这里插入图片描述

(六)判断输出序列的合法性:

1.栈:

在这里插入图片描述

2.输入受限的双端队列:

在这里插入图片描述

2.输出受限:

在这里插入图片描述

三、栈的应用:

(一)括号匹配:

1.基本思路:

在这里插入图片描述遇到做括号则压入栈中,当遇到右括号时,弹出栈顶元素与之相匹配,相同则true

在这里插入图片描述

2.C语言实现:

#include <stdio.h> 
#include <stdlib.h>

//定义一个栈
#define MaxSize 10
typedef struct{
	char data[MaxSize];
	int top;
}SqStack; 

//初始化栈
void InitStack(SqStack &S){
	S.top = -1;
}

//压栈
bool Push(SqStack &S,int x){
	if(S.top == MaxSize-1){
		return false;
	}
	S.top = S.top + 1;
	S.data[S.top] = x;
	return true;
} 

//弹栈
bool Pop(SqStack &S,int x){
	if(S.top == -1){
		return false;
	}
	x = S.data[S.top];
	S.top = S.top-1;
	return true;
} 

//判栈空
bool IsEmpty(SqStack S){
	if(S.top == -1){
		return true;
	}else{
		return false;
	}
} 

//括号匹配 
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(IsEmpty(S)){
				return false;
				
			}
			char topElem;//存储弹出的左括号 
			Pop(S,topElem);
			if(topElem == ')' && str[i] != '('){
				return false;
			}
			if(topElem == ']' && str[i] != '['){
				return false;
			}
			if(topElem == '}' && str[i] != '{'){
				return false;
			}
		}
		return IsEmpty(S);
	} 
	
}

int main(){
	return 0;
}

(二)表达式求值:

1.中缀表达式:

运算符在两个操作数中间:a + b ;a + b - c ; a + b - c * d

2.前缀表达式(波兰表达式):

运算符在两个操作数前面:+ a b ; - + a b c ; - + a b * c d

3.后缀表达式(逆波兰表达式):

运算符在两个操作数后面:a b + ; a b + c - ; a b + c d * -

4.转换方法:

4.1.中缀转后缀:

在这里插入图片描述

在这里插入图片描述从左向右看,碰到符号则将符号放在左边最近的两个操作数之间
在这里插入图片描述

在这里插入图片描述

4.2.中缀转前缀:

在这里插入图片描述
在这里插入图片描述对比:
在这里插入图片描述

(三)栈的递归调用:

1.求阶乘:

//求n个数的阶乘 
int factorial (int n){
	if(n == 0 || n == 1){
		return 1;
	}else{
		return n*factorial(n-1);
	}
}

int main(){
	int input;
	printf("请输入需求的阶乘数:");
	scanf("%d",&input);
	int output = factorial(input);
	printf("%d的阶乘为:%d\n",input,output);
	return 0;
}

2.求斐波那楔数列:

0 1 1 2 3 5 8 13 … 数列从第三项开始,每一项都是前两项数之和

int Fib(int n){
	if(n == 0){
		return 0;
	}else if(n == 1){
		return 1;
	}else{
		return Fib(n-1) + Fib(n-2);
	}
}

int main(){
	int a = Fib(4);
	printf("%d\n",a);
	return 0;
}

四、队列的应用:

例如:进程的先来先服务原则,打印机缓冲区先来后到原则等。

五、矩阵的压缩存储:

(一)数组的存储结构:

1.一维数组:

各数组元素大小相同,物理上连续存放,数组下标从0开始。

2.二维数组:

各数组元素大小相同,物理上连续存放,数组下标从0开始,分为行优先(先存行后存列)和列优先(先存列后存行),视为一维存储。

(二)特殊矩阵:

1.对称矩阵:

aij = aji,即:关于主对角线对称,主对角线 i = j,因为主对角线上下对称,则可以只存储主对角线和下三角区域(或上三角区域)的内容

2.三角矩阵:

分为上三角和下三角,上三角:除了主对角线和下三角区,其他元素均相同,下三角:除了主对角线和上三角区,其他元素均相同

3.三对角矩阵:

带状矩阵:|i - j| > 1时,有aij = 0

4.稀疏矩阵:

非零元素远远小于矩阵元素的个数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值