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); //出栈元素ewhile (e!='(') //不为'('时循环{postexp[i++]=e; //将e存放到postexp中Pop(Optr,e); //继续出栈元素e}exp++; //继续扫描其他字符break;case '+': //判定为加或减号case '-':while (!StackEmpty(Optr)) //栈不空循环{GetTop(Optr,e); //取栈顶元素eif (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); //取栈顶元素eif (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); //出栈元素epostexp[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); //出栈元素aPop1(Opnd,b); //出栈元素bc=b+a; //计算cPush1(Opnd,c); //将计算结果c进栈break;case '-': //判定为'-'号Pop1(Opnd,a); //出栈元素aPop1(Opnd,b); //出栈元素bc=b-a; //计算cPush1(Opnd,c); //将计算结果c进栈break;case '*': //判定为'*'号Pop1(Opnd,a); //出栈元素aPop1(Opnd,b); //出栈元素bc=b*a; //计算cPush1(Opnd,c); //将计算结果c进栈break;case '/': //判定为'/'号Pop1(Opnd,a); //出栈元素aPop1(Opnd,b); //出栈元素bif (a!=0){c=b/a; //计算cPush1(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); //取栈顶元素eDestroyStack1(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; //定义栈stInitStack(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); //取栈顶方块ei=e.i; j=e.j; di=e.di;if (i==xe && j==ye) //找到了出口,输出该路径{ printf("一条迷宫路径如下:\n");k=0;while (!StackEmpty(st)){Pop(st,e); //出栈方块epath[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++; //队尾增1q->data[q->rear]=e; //rear位置插入元素ereturn 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; //定义顺序队指针quInitQueue(qu); //初始化队列que.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; //失败时返回falsewhile (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; //失败时返回falsewhile (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;elsex=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) //调用时返回trueprintf("Assign(t,x,2,5)=>x=%d\n",x);else //调用时返回falseprintf("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) //调用时返回trueprintf("Assign(t,x,2,5)=>x=%d\n",x);else //调用时返回falseprintf("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) //为原子时返回0return 0;g1=g->val.sublist; //g1指向第一个元素if (g1==NULL) //为空表时返回1return 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 //没有兄弟了,将兄弟指针置为NULLg->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); //递归输出后续表的内容}}
}