今天8.12开始第三章栈和队列,最近进度协调不好。
目录
第三章 栈、队列和数组
3.1栈
3.1.1栈的基本概念
栈是线性结构,存储结构既有链式存储的链栈,也有顺序存储的栈。
只允许在栈顶插入或删除的线性表,允许为空栈。
3.1.2栈的基本操作
1.栈的存储结构Stack(顺序存储结构与链式存储结构)
//栈的顺序存储结构
#define maxsize 20//栈中元素最大个数
typedef struct{
Elemtype data[maxsize]; //数据域
int top; //栈顶指针
}SqStack;
//栈的链式存储结构
#define maxsize 20//栈中元素最大个数
typedef struct Linknode{ //定义了Linknode
Elemtype data; //数据域
sturct Linknode *next; //指针域,指向下一个结点
}Linknode,*LinkStackPtr; //LinkStackPtr是一个指向Linknode的指针类型。
typedef struct LinkStack{//定义链栈
LinkStackPtr top; //指向栈顶元素
int count; //栈的大小
}
2.InitStack(&S):初始化一个空栈S
//顺序存储结构
void InitStack(SqStack &S){
S.top=-l;//直接把指针域赋值为-1表示此时为空栈
}
//链式
void InitStack(LinkStack &S){
S->top = NULL;
S->count = 0;
}
3.判断栈是否为空
//顺序栈
bool IsEmpty(SqStack &S){
if(S.top == -1){
return true;
}
else
return false;
}
//链栈
bool IsEmpty(LinkStack &S){
if(S->count == 0){
return true;
}
else
return false;
}
4.进栈
注:
//顺序栈
bool Push(SqStack &S,ElemType x){
if(S.top >= maxsize-1){
return false;}
S.data[++S.top] = x;
return true;
}
//链栈
bool Push(LinkStack &S, ElemType x){
if(S->count >=maxsize-1){//判断是否已满
return false;
}
LinkStackPtr node = (LinkStackPtr)malloc(sizeof(StackNode));//分配一个指针空间
node->data = x;
node->next = S.top;
S.top = node;
S->count++;
}
5.出栈
//顺序栈
bool pop(SqStack &S, Elemtype &x){
if(S.top == -1){//如果栈为空,则返回false
return false;
}
x=S.data[top--];//先出栈,指针再减一
return true;
}
//链栈
bool pop(LinkStack &S, ElemType &x){
if(S.count == 0){
retrun false;}
LinkStackptr p = S->top;
x = p->data;
S->top = p->next;//栈顶指针后移
free(p);
S->count--;
return true;
}
6.读取栈顶元素
//顺序
bool GetTopElem(SqStack S, ElemType x){
if(S.top == -1){//如果是空栈就返回false
return false;
}
x = S.data[S.top];
return true;
}
//链栈
bool GetTopElem(LinkStack S, ElemType x){
if(S->count == 0){//如果是空栈就返回false
return false;
}
x = S->top->data;
return true;
}
5.共享栈
栈底位置相对不变,让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在数组的两端,两个栈顶同时向共享空间延伸。
两个栈的栈顶指针都指向栈顶元素,top1=0时1号栈为空,top2=maxsize时2号栈为空
仅当两个栈的栈顶指针相邻top2-top1=1时栈满;
采用共享栈的好处:节省存储空间,降低发生上溢的可能
上溢(栈顶指针超出了最大范围)
//顺序栈
//定义结构类型
typedef struct {
int data[MAXSIZE];
int top1;//分别代表两端指针
int top2;
} SharedStack;
void InitStack(SharedStack &S){
S.top1 = 0;
S.top2 = maxsize;
}
bool push(SharedStack &S,int x, int StackNum){//要入栈的元素以及入栈的栈号
if(S.top1 == S.top2){ //栈满,返回false
return false;
}
if(StackNum==1){
S.data[++S.top1] = x;
}else if(StackNum==2){
S.data[--S.top2] = x;
}else{return false;}//如果输错了则返回false
return true;
}
3.2 队列
3.2.1队列的基本概念
3.2.2队列的存储结构及基本操作
一、队列的顺序存储结构
1.队列的顺序存储
#define maxsize 20
typedef sturct{
Elemtype data[maxsize];//数据域
int front; //队头指针
int rear; //队尾指针
}SqQueue;
void InitQueue(SqQueue &Q){//初始化队列为空
Q.front = 0;
Q.rear = 0;
}
bool EnterQueue(SqQueue &Q, int x){//入队
if(Q.rear == maxsize-1 && Q.front == 0){ //判断队伍是否已满
return false;}
Q.data[++Q.rear] = x; //如果未满,则队尾指针先+1再入队
return true;
}
bool DeleteQueue(SqQueue &Q, int &x){ //出队
if(Q.front==Q.rear){ //判断队伍是否为空
return false;}
x = Q.data[Q.front]; //先将队头元素的值赋给x,然后队头元素-1
Q.front--;
return true;
}
void GetHead(SqQueue &Q){ //读取队头元素
return Q.data[Q.front];}
顺序队列的缺点:当Q.rear==maxsize时不能作为队满的条件,这时候入队会出现假的上溢出,因为对队列中实际上还有存放空间,所以是假溢出
2.循环队列
基本操作的实现与顺序存储基本相同
二、队列的链式存储结构
1.链队
队列的链式存储是一个同时带有队头指针和队尾指针的单链表,其头指针指向队头结点,尾指针指向队尾结点。当Q.front==NULL且Q.rear==NULL时,链式队列为空。
typedef sturct LinkNode{ //队列结点
ElemType data;
struce LinkNode *next;
}
typedef sturct{ //链队
LinkNode *front,*rear; //队头队尾指针
}*LinkNode;
//队列初始化
void InitQueue(LinkQueue &Q){
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));//建立头结点
Q.front->next = NULL; //初试为空
}
//判断队列是否为空
bool IsEmpty(LinkQueue &Q){
if(Q.rear == Q.front)
return true;
else return false;
}
//入队
bool EnterQueue(LinkQueue &Q, ElemType x){
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));//创建新节点
s->data = x; //x赋值到s的数据域
s->next = NULL; //s的后继结点为空
Q.rear->next = s; //队尾指针的后继结点设置为s
Q.rear = s; //在把队尾指针指向s
return true;
}
//出队
bool DeleteQueue(LinkQueue &Q, ElemType &x){
if(Q.front==Q.rear) return false;//判断队列是否为空
LinkNode *p = Q.front->next; //Q.front是头结点,不存储内容,
//所以队头结点实际上是Q.front->next
x = p->data;
Q.front->next = p->next; //此时p结点指向的是Q.front->next也就是队头结点
//p->next也就是队伍中第二个结点,
//以将Q.fornt->next改为第二个结点的方式把队头结点删除
if(Q.rear == p)
Q.rear = Q.front; //如果队列中只有一个结点,则删除后变为空队列
free(p);
return true;
}
2.双端队列(重点是了解输入输出顺序,操作实现过程可以不那么注重,只需要修改链队部分代码)
8.13日继续以下部分
3.3 栈和队列的应用
一、括号匹配
给定一串括号,要检测这些括号是否互相匹配
算法思想:
//括号匹配
#include<stdio.h>
#define maxsize 20//栈中元素最大个数
typedef struct{
char data[maxsize]; //数据域
int top; //栈顶指针
}SqStack;
void push(SqStack &s, char c) {
if (s.top != maxsize - 1) {
s.data[++s.top] = c;
} else {
printf("栈满\n");
}
}
char pop(SqStack &s) {
if (s.top != -1) {
return s.data[s.top--];
} else {
printf("栈空\n");
}
}
bool check(char arr[]){
SqStack S;
char x = ' ';
S.top = -1;//初始化栈
int i = 0;
while(arr[i]){
if(arr[i]=='('||arr[i]=='{'||arr[i]=='['){//如果是左括号,则入栈
push(S, arr[i]);}
else if(arr[i]==')'||arr[i]=='}'||arr[i]==']'){//如果是右括号
if(S.top == -1){//如果为空,则直接报错
return false;}
x = pop(S);//不为空,则出栈
if((arr[i]=='('&&x == ')' ) || (arr[i]=='{'&&x == '}' ) || (arr[i]=='['&&x == ']' ))
return true;
else{return false;}
}
i++;}
}
int main(){
char arr[maxsize];
printf("输入括号序列:");
scanf("%s", arr);
if(check(arr)){
printf("括号序列不匹配");
return 1;}
else {
printf("括号序列匹配");
return 0;}
return 0;
}
二、栈在表达式求值中的应用
它的实现是栈应用的一个典型范例。
要掌握所有方法的手算以及部分的机算过程
算数表达式:A+B*(C-D)-E/F 操作数:A,B,C,D,E,F 运算符:*,/,+,- ;界限符:()
中缀表达式:A+B*(C-D)-E/F
(波兰表达式)前缀表达式:-+A*B-CD/EF
(逆波兰表达式)后缀表达式:ABCD-*+EF/-
中缀表达式就是正常的表达式,把中缀表达式按照运算顺序做成一个二叉树,前缀表达式就是二叉树的先序遍历序列,后缀表达式就是二叉树的后序遍历序列。
(1)由中缀表达式转为后缀表达式:(遵循左优先原则:只要左边运算符能先算,就先算左边的)
(2)由后缀表达式转中缀表达式:从左向右扫描,将与道德运算符最近(前面)的两个操作数执行对应运算。
(3)由中缀表达式转前缀表达式:遵循右优先原则
用栈实现后缀表达式的计算:①从左到右扫描,数字压入栈中,运算符则执行下一条 ②如果是运算符,则弹出前两个栈顶元素,与扫描到的符号执行对应运算,结果压回栈中。最终栈中只剩下一个元素,就是最终结果。
用栈实现前缀表达式的计算:①从右向左扫描,数字压入栈中,运算符则执行下一条 ②如果是运算符,则弹出前两个栈顶元素,与扫描到的符号执行对应运算,结果压回栈中。最终栈中只剩下一个元素,就是最终结果。(与上一个几乎完全相同,只是遍历方向变了)
用栈实现中缀表达式的计算(机算):①初始化两个栈,分别是操作数栈和运算符栈,②所有的操作数压入操作数栈,③扫描到界限符“(”时,(入栈;扫描到界限符“)”时,依次弹出栈内运算符并加入到后缀表达式中直到弹出)为止 ④扫描到运算符时,依次弹出优先级 大于等于 当前运算符的所有运算符,并输出,遇到遇到优先级小于栈顶元素的优先级或栈空时停止,之后再把当前运算符入栈。
用栈实现中缀表达式转后缀表达式(机算):①遇到操作数时直接后缀表达式中②扫描到界限符“(”时,“(”入栈;扫描到界限符“)”时,依次弹出栈内运算符并输出直到弹出“(”为止 ③扫描到运算符时,依次弹出优先级 大于等于 当前运算符的所有运算符,并加入后缀表达式中,遇到“(”或栈空时停止,之后再把当前运算符入栈,
经过以上处理后再将站内剩余运算符依次弹出并加入后缀式中
#中缀表达式的计算与中缀表达式转后缀表达式
8.14
三、栈在递归中的应用
递归策略通常只需要用较少的代码量完成多次重复计算,但效率不太高
递归模型不能是循环定义的,一定要满足以下两个条件:
递归表达式(递归体);边界条件(递归出口)
四、队列在层次遍历中的应用
应用于树的层次遍历中,将每层元素按顺序存入队列中
五、队列在计算机系统中的应用
3.4 数组和特殊矩阵(思路,简答)
3.4.1 定义
3.4.2数组的存储结构
对于多维数组,有两种映射方法:按行优先和按列优先
按行优先存储的基本思想是:先行后列,先存储行号较小的元素,行号相等先存储列号较小的元素
按列优先存储的基本思想是:先列后行,先存储列号较小的元素,列号相等先存储行号较小的元素
行优先:第i行*列数+j
列优先:第j列*行数+i
3.4.3 特殊矩阵的压缩存储(重点)
一、对称矩阵
若对一个n阶矩阵/中的任意一个元素a i j都有 ai j = aj i 则称其为对称矩阵。
元素下标对应关系!!!
***********************************************************************************
8.15
二、三角矩阵
1.上三角矩阵
上三角矩阵中,下三角区的所有元素均为同一常量。只需存储主对角线、上三角区上的元素和下三角区的常量一次即可。可将其压缩存储在B[n(n+1)/2+1]中
2.下三角矩阵
三、三对角矩阵
矩阵A中3条对角线上的元素ai,j (| i-j |<=1 ,1<=i,j<=n)在一维数组B中存放的下标为k=2i+j-3
反之,一直三对角线矩阵中某元素存放于一维数组B中的第k个位置,则可得i=[(k+1)/3+1]向下取整,j =k-2i+3 (掌握)