一、栈(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.稀疏矩阵:
非零元素远远小于矩阵元素的个数