1.1 琐碎知识点
栈、队列、数组是线性存储结构,它们都是一段连续的内存空间,其中栈和队列是动态的,而数组是静态的。它们的区别在于:
- 栈:后进先出,只能在栈顶进行插入和删除操作。
- 队列:先进先出,只能在队尾进行插入操作,在队头进行删除操作。
- 数组:可以随机访问,但插入和删除操作比较麻烦。
n个不同元素进栈,出栈元素的排列个数可为
判断输出序列的合法性:按栈的序列可以的,双端队列也可以
栈和队列的应用:
栈:①括号匹配 ②表达式求值 ③递归
队列:①层次遍历 ②计算机系统
1.2 栈在表达式求值中的应用
规则:①运算项直接进栈 ②运算符级别高的直接进,低的就将栈内容清至级别低的地方为止
将中缀表达式转换为后缀表达式的具体过程如下:
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
- 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4--1)与s1中新的栈顶运算符相比较;
- 遇到括号时:
- 如果是左括号“(”,则直接压入s1;
- 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
- 重复步骤2至5,直到表达式的最右边;
- 将s1中剩余的运算符依次弹出并压入s2;
- 依次弹出s2中的元素并输出,结果的逆序即为该中缀表达式对应的后缀表达式。
例如:A+B*(C-D)-E/F转换为后缀表达式为:ABCD-*+EF/-
顺序栈基本运算算法:
//顺序栈基本运算算法
#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int top; //栈指针
} SqStack; //顺序栈类型
void InitStack(SqStack *&s)
{
s=(SqStack *)malloc(sizeof(SqStack));
s->top=-1;
}
void DestroyStack(SqStack *&s)
{
free(s);
}
bool StackEmpty(SqStack *s)
{
return(s->top==-1);
}
bool Push(SqStack *&s,ElemType e)
{
if (s->top==MaxSize-1) //栈满的情况,即栈上溢出
return false;
s->top++;
s->data[s->top]=e;
return true;
}
bool Pop(SqStack *&s,ElemType &e)
{
if (s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top];
s->top--;
return true;
}
bool GetTop(SqStack *s,ElemType &e)
{
if (s->top==-1) //栈为空的情况,即栈下溢出
return false;
e=s->data[s->top];
return true;
}
求简单表达式的值 ----中缀表达式转换为后缀表达式的具体过程实现
//求简单表达式的值
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
//---------------------------------------------------------
//--运算符栈基本运算---------------------------------------
//---------------------------------------------------------
typedef struct
{ char data[MaxSize]; //存放运算符
int top; //栈顶指针
} SqStack;
void InitStack(SqStack *&s) //初始化栈
{ s=(SqStack *)malloc(sizeof(SqStack));
s->top=-1;
}
void DestroyStack(SqStack *&s) //销毁栈
{
free(s);
}
bool StackEmpty(SqStack *s) //判断栈是否为空
{
return(s->top==-1);
}
bool Push(SqStack *&s,char e) //进栈元素e
{ if (s->top==MaxSize-1)
return false;
s->top++;
s->data[s->top]=e;
return true;
}
bool Pop(SqStack *&s,char &e) //出栈元素e
{ if (s->top==-1)
return false;
e=s->data[s->top];
s->top--;
return true;
}
bool GetTop(SqStack *s,char &e) //取栈顶元素e
{ if (s->top==-1)
return false;
e=s->data[s->top];
return true;
}
//---------------------------------------------------------
void trans(char *exp,char postexp[]) //将算术表达式exp转换成后缀表达式postexp
{
char e;
SqStack *Optr; //定义运算符栈
InitStack(Optr); //初始化运算符栈
int i=0; //i作为postexp的下标
while (*exp!='\0') //exp表达式未扫描完时循环
{ switch(*exp)
{
case '(': //判定为左括号
Push(Optr,'('); //左括号进栈
exp++; //继续扫描其他字符
break;
case ')': //判定为右括号
Pop(Optr,e); //出栈元素e
while (e!='(') //不为'('时循环
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //继续出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为加或减号
case '-':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if (e!='(') //e不是'('
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else //e是'(时退出循环
break;
}
Push(Optr,*exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr,e); //取栈顶元素e
if (e=='*' || e=='/') //将栈顶'*'或'/'运算符出栈并存放到postexp中
{
postexp[i++]=e; //将e存放到postexp中
Pop(Optr,e); //出栈元素e
}
else //e为非'*'或'/'运算符时退出循环
break;
}
Push(Optr,*exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
default: //处理数字字符
while (*exp>='0' && *exp<='9') //判定为数字
{ postexp[i++]=*exp;
exp++;
}
postexp[i++]='#'; //用#标识一个数值串结束
}
}
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr,e); //出栈元素e
postexp[i++]=e; //将e存放到postexp中
}
postexp[i]='\0'; //给postexp表达式添加结束标识
DestroyStack(Optr); //销毁栈
}
//---------------------------------------------------------
//--操作数栈基本运算---------------------------------------
//---------------------------------------------------------
typedef struct
{ double data[MaxSize]; //存放数值
int top; //栈顶指针
} SqStack1;
void InitStack1(SqStack1 *&s) //初始化栈
{ s=(SqStack1 *)malloc(sizeof(SqStack1));
s->top=-1;
}
void DestroyStack1(SqStack1 *&s) //销毁栈
{
free(s);
}
bool StackEmpty1(SqStack1 *s) //判断栈是否为空
{
return(s->top==-1);
}
bool Push1(SqStack1 *&s,double e) //进栈元素e
{ if (s->top==MaxSize-1)
return false;
s->top++;
s->data[s->top]=e;
return true;
}
bool Pop1(SqStack1 *&s,double &e) //出栈元素e
{ if (s->top==-1)
return false;
e=s->data[s->top];
s->top--;
return true;
}
bool GetTop1(SqStack1 *s,double &e) //取栈顶元素e
{ if (s->top==-1)
return false;
e=s->data[s->top];
return true;
}
//---------------------------------------------------------
double compvalue(char *postexp) //计算后缀表达式的值
{
double d,a,b,c,e;
SqStack1 *Opnd; //定义操作数栈
InitStack1(Opnd); //初始化操作数栈
while (*postexp!='\0') //postexp字符串未扫描完时循环
{
switch (*postexp)
{
case '+': //判定为'+'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b+a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case '-': //判定为'-'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b-a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case '*': //判定为'*'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
c=b*a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
case '/': //判定为'/'号
Pop1(Opnd,a); //出栈元素a
Pop1(Opnd,b); //出栈元素b
if (a!=0)
{
c=b/a; //计算c
Push1(Opnd,c); //将计算结果c进栈
break;
}
else
{
printf("\n\t除零错误!\n");
exit(0); //异常退出
}
break;
default: //处理数字字符
d=0; //将连续的数字字符转换成对应的数值存放到d中
while (*postexp>='0' && *postexp<='9') //判定为数字字符
{
d=10*d+*postexp-'0';
postexp++;
}
Push1(Opnd,d); //将数值d进栈
break;
}
postexp++; //继续处理其他字符
}
GetTop1(Opnd,e); //取栈顶元素e
DestroyStack1(Opnd); //销毁栈
return e; //返回e
}
int main()
{
char exp[]="(56-20)/(4+2)";
char postexp[MaxSize];
trans(exp,postexp);
printf("中缀表达式:%s\n",exp);
printf("后缀表达式:%s\n",postexp);
printf("表达式的值:%g\n",compvalue(postexp));
return 1;
}
用栈求解迷宫问题
//用栈求解迷宫问题
#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+2]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
//---------------------------------------------------------
//--迷宫栈基本运算---------------------------------------
//---------------------------------------------------------
typedef struct
{
int i; //当前方块的行号
int j; //当前方块的列号
int di; //di是下一可走相邻方位的方位号
} Box;
typedef struct
{
Box data[MaxSize]; //存放方块
int top; //栈顶指针
} StType; //定义栈类型
void InitStack(StType *&s) //初始化栈
{ s=(StType *)malloc(sizeof(StType));
s->top=-1;
}
void DestroyStack(StType *&s) //销毁栈
{
free(s);
}
bool StackEmpty(StType *s) //判断栈是否为空
{
return(s->top==-1);
}
bool Push(StType *&s,Box e) //进栈元素e
{
if (s->top==MaxSize-1)
return false;
s->top++;
s->data[s->top]=e;
return true;
}
bool Pop(StType *&s,Box &e) //出栈元素e
{
if (s->top==-1)
return false;
e=s->data[s->top];
s->top--;
return true;
}
bool GetTop(StType *s,Box &e) //取栈顶元素e
{
if (s->top==-1)
return false;
e=s->data[s->top];
return true;
}
//---------------------------------------------------------
bool mgpath(int xi,int yi,int xe,int ye) //求解路径为:(xi,yi)->(xe,ye)
{
Box path[MaxSize], e;
int i,j,di,i1,j1,k;
bool find;
StType *st; //定义栈st
InitStack(st); //初始化栈顶指针
e.i=xi; e.j=yi; e.di=-1; //设置e为入口
Push(st,e); //方块e进栈
mg[xi][yi]=-1; //入口的迷宫值置为-1避免重复走到该方块
while (!StackEmpty(st)) //栈不空时循环
{
GetTop(st,e); //取栈顶方块e
i=e.i; j=e.j; di=e.di;
if (i==xe && j==ye) //找到了出口,输出该路径
{
printf("一条迷宫路径如下:\n");
k=0;
while (!StackEmpty(st))
{
Pop(st,e); //出栈方块e
path[k++]=e; //将e添加到path数组中
}
while (k>=1)
{
k--;
printf("\t(%d,%d)",path[k].i,path[k].j);
if ((k+2)%5==0) //每输出每5个方块后换一行
printf("\n");
}
printf("\n");
DestroyStack(st); //销毁栈
return true; //输出一条迷宫路径后返回true
}
find=false;
while (di<4 && !find) //找相邻可走方块(i1,j1)
{
di++;
switch(di)
{
case 0:i1=i-1; j1=j; break;
case 1:i1=i; j1=j+1; break;
case 2:i1=i+1; j1=j; break;
case 3:i1=i; j1=j-1; break;
}
if (mg[i1][j1]==0) find=true; //找到一个相邻可走方块,设置find我真
}
if (find) //找到了一个相邻可走方块(i1,j1)
{
st->data[st->top].di=di; //修改原栈顶元素的di值
e.i=i1; e.j=j1; e.di=-1;
Push(st,e); //相邻可走方块e进栈
mg[i1][j1]=-1; //(i1,j1)的迷宫值置为-1避免重复走到该方块
}
else //没有路径可走,则退栈
{
Pop(st,e); //将栈顶方块退栈
mg[e.i][e.j]=0; //让退栈方块的位置变为其他路径可走方块
}
}
DestroyStack(st); //销毁栈
return false; //表示没有可走路径,返回false
}
int main()
{
mgpath(1,1,M,N);
return 1;
}
1.3 环形队列
顺序队列(环形队列)基本运算算法
//顺序队列(环形队列)基本运算算法
#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
typedef char ElemType;
typedef struct
{
ElemType data[MaxSize];
int front,rear; //队首和队尾指针
} SqQueue;
void InitQueue(SqQueue *&q)
{ q=(SqQueue *)malloc (sizeof(SqQueue));
q->front=q->rear=0;
}
void DestroyQueue(SqQueue *&q)
{
free(q);
}
bool QueueEmpty(SqQueue *q)
{
return(q->front==q->rear);
}
bool enQueue(SqQueue *&q,ElemType e)
{ if ((q->rear+1)%MaxSize==q->front) //队满上溢出
return false;
q->rear=(q->rear+1)%MaxSize;
q->data[q->rear]=e;
return true;
}
bool deQueue(SqQueue *&q,ElemType &e)
{ if (q->front==q->rear) //队空下溢出
return false;
q->front=(q->front+1)%MaxSize;
e=q->data[q->front];
return true;
}
用队列求解迷宫问题
//用队列求解迷宫问题
#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+2]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
//----------------------------------------------------------
//-非环形队列的基本运算算法---------------------------------
//----------------------------------------------------------
typedef struct
{ int i,j; //方块的位置
int pre; //本路径中上一方块在队列中的下标
} Box; //方块类型
typedef struct
{
Box data[MaxSize];
int front,rear; //队头指针和队尾指针
} QuType; //顺序队类型
void InitQueue(QuType *&q) //初始化队列
{ q=(QuType *)malloc (sizeof(QuType));
q->front=q->rear=-1;
}
void DestroyQueue(QuType *&q) //销毁队列
{
free(q);
}
bool QueueEmpty(QuType *q) //判断队列是否为空
{
return(q->front==q->rear);
}
bool enQueue(QuType *&q,Box e) //进队列
{ if (q->rear==MaxSize-1) //队满上溢出
return false; //返回假
q->rear++; //队尾增1
q->data[q->rear]=e; //rear位置插入元素e
return true; //返回真
}
bool deQueue(QuType *&q,Box &e) //出队列
{ if (q->front==q->rear) //队空下溢出
return false;
q->front++;
e=q->data[q->front];
return true;
}
//----------------------------------------------------------
void print(QuType *qu,int front) //从队列qu中输出路径
{
int k=front,j,ns=0;
printf("\n");
do //反向找到最短路径,将该路径上的方块的pre成员设置成-1
{ j=k;
k=qu->data[k].pre;
qu->data[j].pre=-1;
} while (k!=0);
printf("一条迷宫路径如下:\n");
k=0;
while (k<MaxSize) //正向搜索到pre为-1的方块,即构成正向的路径
{ if (qu->data[k].pre==-1)
{ ns++;
printf("\t(%d,%d)",qu->data[k].i,qu->data[k].j);
if (ns%5==0) printf("\n"); //每输出每5个方块后换一行
}
k++;
}
printf("\n");
}
bool mgpath1(int xi,int yi,int xe,int ye) //搜索路径为:(xi,yi)->(xe,ye)
{
Box e;
int i,j,di,i1,j1;
QuType *qu; //定义顺序队指针qu
InitQueue(qu); //初始化队列qu
e.i=xi; e.j=yi; e.pre=-1;
enQueue(qu,e); //(xi,yi)进队
mg[xi][yi]=-1; //将其赋值-1,以避免回过来重复搜索
while (!QueueEmpty(qu)) //队不空且循环
{
deQueue(qu,e); //出队方块e,由于不是环形队列,该出队元素仍在队列中
i=e.i; j=e.j;
if (i==xe && j==ye) //找到了出口,输出路径
{
print(qu,qu->front); //调用print函数输出路径
DestroyQueue(qu); //销毁队列
return true; //找到一条路径时返回真
}
for (di=0;di<4;di++) //循环扫描每个方位,把每个可走的方块插入队列中
{
switch(di)
{
case 0:i1=i-1; j1=j; break;
case 1:i1=i; j1=j+1; break;
case 2:i1=i+1; j1=j; break;
case 3:i1=i; j1=j-1; break;
}
if (mg[i1][j1]==0)
{
e.i=i1; e.j=j1;
e.pre=qu->front; //指向路径中上一个方块的下标
enQueue(qu,e); //(i1,j1)方块进队
mg[i1][j1]=-1; //将其赋值-1,以避免回过来重复搜索
}
}
}
DestroyQueue(qu); //销毁队列
return false; //未找到任何路径时返回假
}
int main()
{
mgpath1(1,1,M,N);
return 1;
}
1.4 数组和特殊矩阵
1.4.1 数组的存储结构
列优先:存储单元先行后列
列优先:存储单元先列后行
1.4.2 特殊矩阵的压缩存储
压缩存储:多个值相同的元素只分配一个存储空间,对零元素不分配存储空间
对称矩阵:
三角矩阵:
1.4.3 稀疏矩阵
稀疏矩阵:
//稀疏矩阵的三元组表示-算法
#include <stdio.h>
#define M 6
#define N 7
#define MaxSize 100 //矩阵中非零元素最多个数
typedef int ElemType;
typedef struct
{
int r; //行号
int c; //列号
ElemType d; //元素值
} TupNode; //三元组定义
typedef struct
{
int rows; //行数
int cols; //列数
int nums; //非零元素个数
TupNode data[MaxSize];
} TSMatrix; //三元组顺序表
void CreatMat(TSMatrix &t,ElemType A[M][N]) //从一个二维稀疏矩阵创建其三元组表示
{
int i,j;
t.rows=M;t.cols=N;t.nums=0;
for (i=0;i<M;i++)
{
for (j=0;j<N;j++)
if (A[i][j]!=0) //只存储非零元素
{
t.data[t.nums].r=i;t.data[t.nums].c=j;
t.data[t.nums].d=A[i][j];t.nums++;
}
}
}
bool Value(TSMatrix &t,ElemType x,int i,int j) //三元组元素赋值
{
int k=0,k1;
if (i>=t.rows || j>=t.cols)
return false; //失败时返回false
while (k<t.nums && i>t.data[k].r) k++; //查找行
while (k<t.nums && i==t.data[k].r && j>t.data[k].c) k++;//查找列
if (t.data[k].r==i && t.data[k].c==j) //存在这样的元素
t.data[k].d=x;
else //不存在这样的元素时插入一个元素
{
for (k1=t.nums-1;k1>=k;k1--)
{
t.data[k1+1].r=t.data[k1].r;
t.data[k1+1].c=t.data[k1].c;
t.data[k1+1].d=t.data[k1].d;
}
t.data[k].r=i;t.data[k].c=j;t.data[k].d=x;
t.nums++;
}
return true; //成功时返回true
}
bool Assign(TSMatrix t,ElemType &x,int i,int j) //将指定位置的元素值赋给变量
{
int k=0;
if (i>=t.rows || j>=t.cols)
return false; //失败时返回false
while (k<t.nums && i>t.data[k].r) k++; //查找行
while (k<t.nums && i==t.data[k].r && j>t.data[k].c) k++;//查找列
if (t.data[k].r==i && t.data[k].c==j)
x=t.data[k].d;
else
x=0; //在三元组中没有找到表示是零元素
return true; //成功时返回true
}
void DispMat(TSMatrix t) //输出三元组
{
int i;
if (t.nums<=0) //没有非零元素时返回
return;
printf("\t%d\t%d\t%d\n",t.rows,t.cols,t.nums);
printf("\t------------------\n");
for (i=0;i<t.nums;i++)
printf("\t%d\t%d\t%d\n",t.data[i].r,t.data[i].c,t.data[i].d);
}
void TranTat(TSMatrix t,TSMatrix &tb) //矩阵转置
{
int p,q=0,v; //q为tb.data的下标
tb.rows=t.cols;tb.cols=t.rows;tb.nums=t.nums;
if (t.nums!=0) //当存在非零元素时执行转置
{
for (v=0;v<t.cols;v++) //tb.data[q]中的记录以c域的次序排列
for (p=0;p<t.nums;p++) //p为t.data的下标
if (t.data[p].c==v)
{
tb.data[q].r=t.data[p].c;
tb.data[q].c=t.data[p].r;
tb.data[q].d=t.data[p].d;
q++;
}
}
}
int main()
{
TSMatrix t,tb;
int x,y=10;
int A[6][7]={
{0,0,1,0,0,0,0},
{0,2,0,0,0,0,0},
{3,0,0,0,0,0,0},
{0,0,0,5,0,0,0},
{0,0,0,0,6,0,0},
{0,0,0,0,0,7,4}};
CreatMat(t,A);
printf("b:\n"); DispMat(t);
if (Assign(t,x,2,5)==true) //调用时返回true
printf("Assign(t,x,2,5)=>x=%d\n",x);
else //调用时返回false
printf("Assign(t,x,2,5)=>参数错误\n");
Value(t,y,2,5);
printf("执行Value(t,10,2,5)\n");
if (Assign(t,x,2,5)==true) //调用时返回true
printf("Assign(t,x,2,5)=>x=%d\n",x);
else //调用时返回false
printf("Assign(t,x,2,5)=>参数错误\n");
printf("b:\n"); DispMat(t);
TranTat(t,tb);
printf("tb:\n"); DispMat(tb);
return 1;
}
//稀疏矩阵的十字链表表示
#include <stdio.h>
#include <malloc.h>
#define M 3 //矩阵行
#define N 4 //矩阵列
#define Max ((M)>(N)?(M):(N)) //矩阵行列较大者
typedef int ElemType;
typedef struct mtxn
{
int row; //行号
int col; //列号
struct mtxn *right,*down; //向右和向下的指针
union
{
ElemType value;
struct mtxn *link;
} tag;
} MatNode; //十字链表类型
void CreatMat(MatNode *&mh,ElemType a[][N]) //创建a的十字链表
{
int i,j;
MatNode *h[Max],*p,*q,*r;
mh=(MatNode *)malloc(sizeof(MatNode));//创建十字链表的头结点
mh->row=M;mh->col=N;
r=mh; //r指向尾结点
for (i=0;i<Max;i++) //采用尾插法创建头结点h1,h2,…循环链表
{
h[i]=(MatNode *)malloc(sizeof(MatNode));
h[i]->down=h[i]->right=h[i]; //将down和right方向置为循环的
r->tag.link=h[i]; //将h[i]加到链表中
r=h[i];
}
r->tag.link=mh; //置为循环链表
for (i=0;i<M;i++) //处理每一行
{
for (j=0;j<N;j++) //处理每一列
{
if (a[i][j]!=0) //处理非零元素
{
p=(MatNode *)malloc(sizeof(MatNode)); //创建一个新结点
p->row=i;p->col=j;p->tag.value=a[i][j];
q=h[i]; //查找在行表中的插入位置
while (q->right!=h[i] && q->right->col<j)
q=q->right;
p->right=q->right;q->right=p; //完成行表的插入
q=h[j]; //查找在列表中的插入位置
while (q->down!=h[j] && q->down->row<i)
q=q->down;
p->down=q->down;q->down=p; //完成列表的插入
}
}
}
}
void DestroyMat(MatNode *&mh) //销毁十字链表
{
MatNode *pre,*p,*mp;
mp=mh->tag.link; //mp指向h[i]
while (mp!=mh) //释放所有数据结点
{
pre=mp->right; //pre指向h[i]的行首结点
if (pre!=mp) //h[i]不空
{
p=pre->right; //p指向结点pre的后继结点
while (p!=mp)
{
free(pre);
pre=p; p=p->right;
}
}
mp=mp->tag.link; //mp指向下一个头结点
}
//释放所有的头结点
pre=mh->tag.link; //pre指向h[i]
p=pre->tag.link; //p指向h[i+1]
while (p!=mh)
{
free(pre);
pre=p; p=p->tag.link;
}
free(mh);
}
void DispMat(MatNode *mh) //输出十字链表
{
MatNode *p,*q;
printf("行=%d 列=%d\n", mh->row,mh->col);
p=mh->tag.link;
while (p!=mh)
{
q=p->right;
while (p!=q) //输出一行非零元素
{
printf("%d\t%d\t%d\n", q->row,q->col,q->tag.value);
q=q->right;
}
p=p->tag.link;
}
}
//本主程序用于调试
int main()
{
ElemType a[M][N]={{1,0,0,2},{0,0,3,0},{0,0,0,4}};
MatNode *mx;
CreatMat(mx,a);
printf("a的十字链表:\n");DispMat(mx);
DestroyMat(mx);
return 1;
}
1.4.4 三对角矩阵
1.4.5 广义表
什么是广义表
广义表,又称列表,也是一种线性存储结构。
同数组类似,广义表中既可以存储不可再分的元素,也可以存储广义表,记作:LS = (a1,a2,…,an)
其中,LS 代表广义表的名称,an 表示广义表存储的数据。广义表中每个 ai 既可以代表单个元素,也可以代表另一个广义表。
原子和子表
通常,广义表中存储的单个元素称为 "原子",而存储的广义表称为 "子表"。
例如创建一个广义表 LS = {1,{1,2,3}},我们可以这样解释此广义表的构成:广义表 LS 存储了一个原子 1 和子表 {1,2,3}。
以下是广义表存储数据的一些常用形式:
- A = ():A 表示一个广义表,只不过表是空的。
- B = (e):广义表 B 中只有一个原子 e。
- C = (a,(b,c,d)) :广义表 C 中有两个元素,原子 a 和子表 (b,c,d)。
- D = (A,B,C):广义表 D 中存有 3 个子表,分别是A、B和C。这种表示方式等同于 D = ((),(e),(b,c,d)) 。
- E = (a,E):广义表 E 中有两个元素,原子 a 和它本身。这是一个递归广义表,等同于:E = (a,(a,(a,…)))。
注意,A = () 和 A = (()) 是不一样的。前者是空表,而后者是包含一个子表的广义表,只不过这个子表是空表。
广义表的表头和表尾
当广义表不是空表时,称第一个数据(原子或子表)为"表头",剩下的数据构成的新广义表为"表尾"。强调一下,除非广义表为空表,否则广义表一定具有表头和表尾,且广义表的表尾一定是一个广义表。
例如在广义表中 LS={1,{1,2,3},5} 中,表头为原子 1,表尾为子表 {1,2,3} 和原子 5 构成的广义表,即 {{1,2,3},5}。
再比如,在广义表 LS = {1} 中,表头为原子 1 ,但由于广义表中无表尾元素,因此该表的表尾是一个空表,用 {} 表示。
//广义表基本运算算法
#include <stdio.h>
#include <malloc.h>
#include "glnode.h"
int GLLength(GLNode *g) //求广义表g的长度
{
int n=0;
GLNode *g1;
g1=g->val.sublist; //g指向广义表的第一个元素
while (g1!=NULL)
{
n++; //累加元素个数
g1=g1->link;
}
return n;
}
int GLDepth(GLNode *g) //求广义表g的深度
{
GLNode *g1;
int maxd=0,dep;
if (g->tag==0) //为原子时返回0
return 0;
g1=g->val.sublist; //g1指向第一个元素
if (g1==NULL) //为空表时返回1
return 1;
while (g1!=NULL) //遍历表中的每一个元素
{
if (g1->tag==1) //元素为子表的情况
{
dep=GLDepth(g1); //递归调用求出子表的深度
if (dep>maxd) //maxd为同一层所求过的子表中深度的最大值
maxd=dep;
}
g1=g1->link; //使g1指向下一个元素
}
return(maxd+1); //返回表的深度
}
GLNode *CreateGL(char *&s) //创建由括号表示法表示s的广义表链式存储结构
{
GLNode *g;
char ch=*s++; //取一个字符
if (ch!='\0') //串未结束判断
{
g=(GLNode *)malloc(sizeof(GLNode));//创建一个新结点
if (ch=='(') //当前字符为左括号时
{
g->tag=1; //新结点作为表头结点
g->val.sublist=CreateGL(s); //递归构造子表并链到表头结点
}
else if (ch==')')
g=NULL; //遇到')'字符,g置为空
else if (ch=='#') //遇到'#'字符,表示为空表
g=NULL;
else //为原子字符
{
g->tag=0; //新结点作为原子结点
g->val.data=ch;
}
}
else //串结束,g置为空
g=NULL;
ch=*s++; //取下一个字符
if (g!=NULL) //串未结束,继续构造兄递结点
if (ch==',') //当前字符为','
g->link=CreateGL(s); //递归构造兄递结点
else //没有兄弟了,将兄弟指针置为NULL
g->link=NULL;
return g; //返回广义表g
}
void DestroyGL(GLNode *&g) //销毁广义表
{
GLNode *g1,*g2;
g1=g->val.sublist; //g1指向广义表的第一个元素
while (g1!=NULL) //遍历所有元素
{
if (g1->tag==0) //若为原子结点
{ g2=g1->link; //g2临时保存兄弟结点
free(g1); //释放g1所指原子结点
g1=g2; //g1指向后继兄弟结点
}
else //若为子表
{ g2=g1->link; //g2临时保存兄弟结点
DestroyGL(g1); //递归释放g1所指子表的空间
g1=g2; //g1指向后继兄弟结点
}
}
free(g); //释放头结点空间
}
void DispGL(GLNode *g) //输出广义表g
{
if (g!=NULL) //表不为空判断
{ //先处理g的元素
if (g->tag==0) //g的元素为原子时
printf("%c", g->val.data); //输出原子值
else //g的元素为子表时
{
printf("("); //输出'('
if (g->val.sublist==NULL) //为空表时
printf("#");
else //为非空子表时
DispGL(g->val.sublist); //递归输出子表
printf(")"); //输出')'
}
if (g->link!=NULL)
{
printf(",");
DispGL(g->link); //递归输出后续表的内容
}
}
}