定义的解析
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
# define List_Init_Size 100
# define ListIncrement 10
typedef int ElemType;//类型声明,为什么定义 ElemType
typedef struct {//定义一个结构体
ElemType *elem; //存储空间基址
int length; //当前长度(数据元素个数),从这个基址开始存放Eeledef类型的元素
int ListSize;//当前分配的存储容量
} Sqlist;
/*抽象数据类型 : 结构 关系 操作
结构体顺序表包含:起始地址 目前存放的元素个数 当前分配的容量
数据与数据之间的关系:
顺序表:物理上的相邻体现了逻辑上的相连
链表:用指针表示逻辑上相连*/
//讨论:初始化汉化的功能:给结构体里的三个数据赋上初值
bool InitList (Sqlist *L){
L->elem=(ElemType *)malloc/*申请内存空间*/(List_Init_Size*sizeof(ElemType));//malloc申请内存空间,sizeof多大就看前面的ElemType 定义的什么类型的
//if(!L.elem) return false;如果申请到返回1.否则返回0,如果是0,0是系统空间的,所以如果地址是0是不可能分配给你的
if (!L->elem) exit(0) ;//判断基地址是否是0,如果是0往下执行,否则退出。
L->length=0;
L->ListSize=List_Init_Size;
return true;
}
插入解析
//插入
void InsertList(Sqlist *L,int i,ElemType x)
{ //int j;
if(i <1 || i >L->length+1)
{printf("Position error");
return;//输入的位置错误并输出Position error
}
if(L->length>=L->ListSize)
{
printf("overflow");
exit(EXIT_FAILURE);//如果表长和分配的空间相等的时的溢出
}
for(int j=L->length-1;j>= i-1;--j)//给定确定的位置插入
L->elem[j+1]=L->elem[j];
L->elem[i-1]=x;
++L->length;
return;
}
第一章 数据结构
- 1、数据结构的二元组(D/S),D表示数据 ,S表示结构
- 物理结构:在计算机内存中存储的
- 随机存储:找任何一个元素花的时间都是一样的 (预分配空间)
- 指针:内存单元的地址
- 顺序存储:时间不一样,越向后时间越长(没有预分配空间,空间利用率提 高)
- 随机存储:顺序存取(连续),顺序存取:链式存储
- 2、顺序存储可以随机存取,链式存储可以随机存取
- 3、物理结构:连续 非连续
- 物理结构:数据本身固有的,不存在计算机中(线性关系)
- 数据元素:每一行
- 数据项:每一列
- 数据对象:具有相同特性的数据元素的集合,是数据的子集
- 结构:数据元素之间的关系
- 数据结构:相互之间存在一种或多种特定关系的数据元素的集合,即带机构的数据元素的集合。
- 4、数据结构包括:物理结构和逻辑结构
- 逻辑结构:线性结构,集合,树形结构,图或者带权图既是网
- 物理结构:连续 非连续
- 5、连续存储的地址 顺序存取还有链式存取
- 6、数据类型与抽象数据类型的区别?(简答题)
- 数据类型是一个值的集合和定义在这个值集上的一组操作的总称
- 抽象数据类型是一个数学模型及定义在这个模型上的一组操作总称
- 数据类型是系统已经定义好的
- 抽象数据类型是根据问题用户自己定义的数据类型
- 7、抽象数据类型=数学模型+操作
- 三元组(D,S,P)
- 其中的D是指数据对象
- S是指D上关系集
- P是对D的基本操作集
- 第二章 线性表
- 顺序表在计算机中用数组来存
- 8、编程插入,删除,查找(考其中的一个)
- 插入
-
void InsertList(Sqlist *L,int i,ElemType x)
{ //int j;
if(i <1 || i >L->length+1)
{printf("Position error");
return;//输入的位置错误并输出Position error
}
if(L->length>=L->ListSize)
{
printf("overflow");
exit(EXIT_FAILURE);//如果表长和分配的空间相等的时的溢出
}
for(int j=L->length-1;j>= i-1;--j)//给定确定的位置插入
L->elem[j+1]=L->elem[j];
L->elem[i-1]=x;
++L->length;
return;
}
删除
void deleteList(Sqlist *L,int i)
{
int j;
if(i<1 || i>L->length)
{
printf("Position error");
return;
}
for(j=i;j<L->length;j++)
L->elem[j-1]=L->elem[j];
L->length--;
}
时间复杂度为:O(n)
查找
int Locate(Sqlist L, ElemType x)
{ int i=1;
while(i<=L.length && L.elem[i-1]!=x)
++i;
if(i<=L.length) return i;
else return 0;
}
- 9、考输入与输出
- 输入在一个算法中有0个或者多个
- 输出在一个算法中必须有1个
- 10、频度:指基本操作重复执行的次数
- 时间复杂度:常量的时间复杂度是O(1) 线性的时间复杂度是O(n)
- i=1;
- while(i<=n)
- i=i*2;
- 上面的时间复杂度是O(log2n)
- 11、线性表是一个几个数据元素 有限序列
- 利用两个线性表分别表示两个集合,然后求这里两个集合的并集 交集
- 合 并 三个程序出一个
- 并集
-
void unio(Sqlist *La,Sqlist Lb) {
ElemType e;
int La_len=La->length;
int Lb_len=Lb.length;
for(int i=1;i<=Lb_len;++i) {
GetElem(Lb,i,&e);
if(!Locate(*La,e))
InsertList(La,++La_len,e);
}
Output(*La);
}
交集 -
void jiaoji(Sqlist La,Sqlist Lb,Sqlist *Lc){
int k=0;
ElemType ai,bj;
int La_len=ListLength(La);
int Lb_len=ListLength(Lb);
for(int i=1;i<=La_len;i++)
{ for(int j=1;j<=Lb_len;j++)
{GetElem(La,i ,&ai);
GetElem(Lb,j ,&bj);
if(ai==bj){InsertList(Lc,++k,ai);}
}
}
Output(*Lc);
}
合并 -
void mergelist(Sqlist La,Sqlist Lb,Sqlist *Lc){
int i=1,j=1,k=0;
ElemType ai,bj;
int La_len=ListLength(La);
int Lb_len=ListLength(Lb);
while((i<=La_len)&&(j<=Lb_len))
{
GetElem(La,i,&ai);GetElem(Lb,j,&bj);
if(ai<=bj)
{
InsertList(Lc,++k,ai);++i;
}
else {InsertList(Lc,++k,bj);++j;}
}
while(i<=La_len){
GetElem(La,i++,&ai);InsertList(Lc,++k,ai);
}
while(j<=Lb_len){
GetElem(Lb,j++,&bj);InsertList(Lc,++k,bj);
}
Output(*Lc);
}
- 12、利用哪种存储结构并简述为什么
- 长时间保持元素基本不变用顺序存储
- 经常插入或删除用链式结构
- 13、链式存储的特点(判断题)
- 存储空间不一定连续
- 元素之间的反继关系是由指针来体现的
- 逻辑上相邻,物理上不一定相邻
- 非随机存取,及访问任何一个元素的时间不同
- 14、在P所指节点之后插入
- q->next=p->next;
- p->next=q;
- 第三章 栈和队列
栈
- 15、栈的数据结构
- 其插入、删除只能在表的一端进行
- 栈顶:插入,删除的一端
- 16、栈的特点:先进先出,后进先出
- 17、下列哪个出栈序列是不可能的?并说明原因。
- 18、给几种元素让写出有多少种出栈的顺序。(一般是4/5个元素)
- 如果是n种元素,
- 那么Mn=M0*Mn-1+M1*Mn-2+M2*Mn-3+...+Mn-1*M0
- 19、线性表是逻辑结构,栈是逻辑结构,
- 但是顺序栈就是物理结构,带什么方式的是物理结构
- 线性表、顺序栈都属于逻辑结构(X)
- 20、栈的应用 表达式求值
- 当读到一个符号时,两个栈表的状态是什么(没有处理符号时)
- 给出中缀的表达式让写出后缀的表达式
- 在回答问题的时候要画出两个表格,并标注出那个是数的那个是算符 的(算符的最下面一层是‘#’)
队列
- 21、队列的特点:先进先出,后进后出
rear :对头指针
front:队尾指针
- 22、顺序队列的空是怎么判断?
- Q.rear==Q.front//头指针==尾指针
- 23、顺序队列怎么判断是满?
- Q.rear==maxsize//头指针等于最大容量
- 24、顺序队列的入队和出队
- 入队: Q.base[Q.rear]=e;//把这个数赋给该队列的头指针
- Q.rear=Q.rear+1;//然后头指针加1
- 出队: x=Q.base[Q.front];//队列的特点是先进先出,所以把最位 的元素出队
- Q.front=Q.front+1;
- 25、循环队列的入队和出队
- 入队: Q.base[Q.raer]=x;
- Q.rear=(Q.rear+1)%maxsize;
- 出队: y=Q.base[Q.front];
- Q.front=(Q.front+1)%maxsize;
- 26、循环队列判断对满
- (Q.rear+1)%maxsize==Q.front;
- 27、队列中元素的个数是:(rear-front+maxsize)%maxsize;
- 28、线性表的定位,其他的结构体,初始化
- 29、顺序栈、链栈、顺序队列、链队列的结构体的定义 初始化,出栈,入栈(考其中一个)
- 顺序栈的结构体、初始化
- //结构体
- typedef struct
{
SElemType *base;
SElemType *top;
int stacksize;
} SqStack;//初始化栈
Status InitStack(SqStack *s)
{
s->base = (SElemType *)malloc(INIT_SIZE * sizeof(SElemType));
if(!s->base)
{
puts("存储空间分配失败!");
return Error;
}
s->top = s->base;
s->stacksize = INIT_SIZE;
return Ok;
} - //进栈
- bool Push(SeqStack &S,SelemType e)
- {
- S.elem[top++]=e;
- return true;
- }
- //出栈
- bool pop(SeqStack &S,SelemType &e)
- {
- if(S.top==S.base)
- return false;//判断是否为空
- e=S.elem[--top];
- return ture;
- }
- 链栈的结构体、初始化
- /* 链栈结构 */
typedef struct StackNode
{
SElemType data;
struct StackNode *next;
} StackNode,*LinkStackPtr;
typedef struct
{
LinkStackPtr top;
int count;
} LinkStack;
/* 构造一个空栈S */
Status InitStack(LinkStack *S)
{
S->top = (LinkStackPtr)malloc(sizeof(StackNode));
if(!S->top)
return ERROR;
S->top=NULL;
S->count=0;
return OK;
} -
/* 插入元素e为新的栈顶元素 */
Status Push(LinkStack *S,SElemType e)
{
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top; /* 把当前的栈顶元素赋值给新结点的直接后继 */
S->top=s; /* 将新的结点s赋值给栈顶指针,见图中② */
S->count++;
return OK;
}
/* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
Status Pop(LinkStack *S,SElemType *e)
{
LinkStackPtr p;
if(StackEmpty(*S))
return ERROR;
*e=S->top->data;
p=S->top; /* 将栈顶结点赋值给p */
S->top=S->top->next; /* 使得栈顶指针下移一位,指向后一结点 */
free(p); /* 释放结点p */
S->count--;
return OK;
}
- 顺序队列的结构体、初始化
- //结构体
- typedef struct
- {
- QelemType &base;//初始化的动态分配存储空间
- int front;//队头指针
- int rear;//队尾指针
- }//SqQueue;
- //初始化
- bool InitQeue(SqQueue &Q)
- {
- Q.base=(QelemType*)malloc(maxSize*sizeof(QelemType));
- if(Q.base==0)
- return false;
- Q.front=Q.rear=0;
- return true;
- }
- 链队列的结构体、初始化
- //结构体
-
typedef struct node
{
char data;
struct node *next;
}node,*queueptr;
typedef struct
{
queueptr front;
queueptr rear;
}linkqueue;
//初始化 -
void initqueue(linkqueue *q) //初始化操作,建立一个空队列
{
q->front=q->rear=(queueptr)malloc(sizeof(node));
if(!(q->front))
exit(1);
q->front->next=0;
}
- 第四章 串
- 30、KMP算法的特点:(主串)不回溯,效率较高
- 31、会计算 next[j]
- 失败函数是根据前面字符串的前缀与后缀是否相等来判断后面的相对应的数值是多少(取最大的个数)
- 32、会比较 ,相等的字符串的最大长度
- 33、简答题(简述KMP算法的特点,并求出字符串的失败函数)5分
- 34、改进的next[j]
- 改进的next【j】就是把之前的没有改进的next【j】对应的值,如果前面已经有了相对应字母的next[j],那么后面的next[j]均为第一个字母下 面对应的next[j]值。
- 第五章 数组
- 35、填空题 一位数组a[n] ,若a[0]存放于地址a,那么a[5],a[10],a[i]的内存地址(1分)
- 默认大小为L,a[5]=a+5L,a[10]=a+10L,a[i]=a+i*L
- 36、二维数组的地址,内存地址(填空题)
- 37、存放位置:从0开始的A[i][j]=1+2+3+4+、、、、、、+i+j;
- 从1开始的A[i][j]=1+2+3+、、、、、、+(i-1)+j;
- 38、给一个A[i][j]让写出:上三角矩阵的内存地址,位置
- 三对角矩阵的内存地址,位置
- 下三角矩阵元素位置
- 若i>=j,数组元素的A【i】【j】在数组B中的存放位置为 (i+1)*i/2+j
- 若j<i,数组元素的A【i】【j】在矩阵的上上三角部分,则在数组B中没有存放,可以找它的对称元素A【j】【i】 (j+1)*j/2+i
- 上三角矩阵元素存放位置
- 若i>=j,数组元素的A【i】【j】在数组B中的存放位置为 (2*n-i+1)*i/2+j【其中的n代表行数】
- 若j<i,数组元素的A【i】【j】在矩阵的下三角部分,则在数组B中没有存放,可以找它的对称元素A【j】【i】 (2*n-j+1)*j/2+i
- 39、简答题:给出一个稀疏矩阵
- 画出三元组(要写上n行列,非零元素的个数为多少)
- (稀疏矩阵是否节省空间)
- 40、写出稀疏矩阵的转置算法(把行变为列,列变为行,并从低到高)
- 普通转置
-
void TransposeSM(TSMatrix X,TSMatrix &T)
{
printf("现在对矩阵进行转置!\n");
cout<<"正在对矩阵进行转置!"<<endl;
T.mu=X.nu;
T.nu=X.mu;
T.tu=X.tu;
if(T.tu)
{
int q=1;
for(int col=1;col<=X.nu;++col)//注意不忘了等于
for(int p=1;p<=X.tu;++p)
if(X.data[p].j==col)
{
T.data[q].i=X.data[p].j;
T.data[q].j=X.data[p].i;
T.data[q].e=X.data[p].e;
++q;
}
}
- 时间复杂度为O(Cols*Terat)
- 快速转置
SparseMatrix FastTranspos(SparseMatrix A)
{
SparseMatrix B;
int rowSize[Cols];
int rowStart[Cols];
B.Rows=A.Cols;
B.Cols=A.Rows;
B.Terms=A.Terms;
if(A.Terms>0)
{
int i,j;
for(i=0;i<Cols;i++)
rowSize[i]=0;
for(i=0;i<Terms;i++)
rowSize[A.data[i].col]++;
rowStart[0]=0;
for(i=1;i<Cols;i++)
rowStat[i]=rowStart[i-1]+rowSize[i-1];
for(i=0;i<A.Terms;i++)
{
j=rowStart[A.data[i].col];
B.data[j].row=A.data[i].col;
B.data[j].col=A.data[i].row;
B.data[j].value=A.data[i].value;
rowStart[j]++;
}}
return B;
}
时间复杂度为 O(2Cols+2Terms)
-
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T) //快速转置
{ //采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
T.mu = M.nu;
T.nu = M.mu;
T.tu = M.tu;
if (T.tu)
{
int col;
int num[100], cpot[100];
for (col = 1; col <= M.nu; ++col)
num[col] = 0; //num数组的初始化
for (int t = 1; t <= M.tu; ++t)
++num[M.data[t].j]; //求M中每一列含有的非零元个数
cpot[1] = 1;
for (col = 2; col <= M.nu; ++col)
cpot[col] = cpot[col - 1] + num[col - 1]; //求cpot向量
int q;
for (int p = 1; p <= M.tu; ++p)
{
col = M.data[p].j;
q = cpot[col];
T.data[q].i = M.data[p].j;
T.data[q].j = M.data[p].i;
T.data[q].e = M.data[p].e;
++cpot[col];
}//for
}//if
return OK;
}//FastTransposeSMatrix - 第六章 二叉树
- 结点:数据元素+若干指向子树的分支
- 结点的度:分支的个数即结点拥有的子树数
- 树的度:一棵树种各结点度数的最大值
- 叶子结点:度为零的结点
- 分支结点:度大于零的结点
- 路径:由从根到该结点所经分支和结点的构成
- 结点的层次:规定根节点的层次为1,它的孩子为第2层
- 树的深度:树中叶子结点所在的最大层次
- 二叉树存在可能得形态:
- 二叉树的性质:
-
- 41、二叉树的遍历及其应用(简答题)
- 42、哈夫曼编码(简答题)
- 顺序二叉树只适合于满二叉树和完全二叉树
- 43、有几个结点的完全二叉树的深度为k=[log2(n+1)]
- 44、满二叉树是完全二叉树的一种特例(对)
- 45、二叉树的遍历及其应用
- 前序:中左右
- 中序:左中右
- 后序:左右中
- 让画出二叉树,然后给出线索二叉树(考一个)
- 线索二叉树:有子女的不用画线,其中1表示线索,0表示子女
- 46、算法
- 前、中、后序、深度、高度、节点个数
- //结构体
-
typedef struct node{
struct node *leftchild;
struct node *reathchild;
char data;
}BiTreeNode,*BiTree;
//初始化
void createBiTree (BiTree &T){
char ch;
scanf("%c",&ch);
if('#'==ch)
T=NULL;
else{
T=new BiTreeNode;
T->data=ch;
createBiTree(T->leftchild);
createBiTree(T->reathchild);
}
}
- //前序
-
void QX (BiTree T)
{
if(T!=NULL)
{
printf("%c",T->data);
QX(T->leftchild);
QX(T->reathchild);
}
}
//中序
void ZX (BiTree T)
{
if(T)
{
ZX(T->leftchild);
printf("%c",T->data);
ZX(T->reathchild);
}
}
//后序
void HX (BiTree T)
{
if(T)
{
HX(T->leftchild);
HX(T->reathchild);
printf("%c",T->data);
}
}
//高度 -
int GD(BiTree &T)
{
int i,j;
if(T==NULL)
{
return 0;
}
else
{
i=GD(T->leftchild);
j=GD(T->reathchild);
if(i<j)
{
return j+1;
}
else
{
return i+1;
}
}
} - //叶子结点
-
int YZJD(BiTree T,int &num)
{
if(T)
{
if(T->leftchild ==NULL &&T->reathchild==NULL)
num++;
YZJD(T->leftchild,num);
YZJD(T->reathchild,num);
}
return num;
}
- //结点
-
int JD(BiTree &T)
{
if(T==NULL)
{
return 0;
}
else
return JD(T->leftchild)+JD(T->reathchild)+1;
}
- 给出二叉树结构体的遍历+前、中、后序算法
- 47、给出前、中、后序遍历,画出一棵二叉树或者给出任意两种遍历写出其余的一种遍历(简答题)
- 48、给出前序、中序写出后序(填空题)
- 49、给出一个二叉树,画出线索二叉树
- 一棵二叉树上有(N+1)个空节点
- 50、树的子女、兄弟二叉树的画法(左子女、右兄弟)
- 兄弟姊妹的在右,孩子在左
- 51、二叉树与森林的相互转换(考一种)
- 51、广度优先遍历(层次序列遍历)(考研题)
- 森林的遍历
- 森林的先根次序遍历:相当于二叉树的前序遍历
- 森林的后跟次序遍历:相当于二叉树的中序遍历
- 52、Huffman数的构造过程以及Huffman编码
- 构造过程是从小数开始,然后小的放在左边,大的放在右边
- 编码是左0右1
路径长度L:是连接两结点的路径上分支数
外部路径长度:是各叶节点(外节点)到根结点的路径长度之和EPL
树的内部路径长度:是各非结点(内结点)到根结点的路径长度之和IPL
树的路径长度PL=EPL+IPL
- 带权路径长度:第i个外结点的权值为W,它到根的路径长度为L ,则该外结点到根的带全路径长度为W*L
- 53、练习与讨论
- 54、堆排序的时间复杂度
- O(nlog2n)
- 不稳定 最小堆(写出每次排列后的结果)
- 第七章 图
- 55、图的存储表示(8种)考其中一种
- 56、图的遍历
- 57、图的最小生成树
- 58、网络(重点)带权的网称为网络。
- 59、有8个顶点无向图最多可能有多少条边
- 60、有n个顶点的有向图中,顶点的度最大可达----2(n-1)
- 所有顶点的度的和-----2n(n-1)
- 有n个顶点的无向图中,顶点的度最大可达----(n-1)
- n个顶点的连通无向图中,其边的条数至少为-----(n-1)
- n个顶点的强连通图,至少有n条弧
- 61、邻接矩阵
- 无向图 无向带权连通图
- 有向图 有向带权连通图
- 62、G1是一个稀疏图,适合用什么存储? --------邻接表
- G1是一个稠密图,适合用什么存储?---------邻接矩阵
- 为什么?
- 稀疏图用邻接表更节省空间,主要是为了节省空间。
- 63、给出一个图,求出这个图的度、邻接矩阵
- 或者根据邻接矩阵画出图
- 64、课件例3,已知某网的邻接(出边)表,请画出该网络(重点)
- 65、给出一个图(告诉是邻接矩阵)
- 写出该图的深度优先遍历序列且画出深度优先生成树
- (画出邻接矩阵)
- 66、判断
- 深度优先搜索用栈
- 广度优先搜索用队列
- 67、给出一个图(邻接矩阵)
- 写出深度优先遍历切画出深度优先遍历的生成树
- 画出邻接矩阵 广度优先遍历
- 68、遍历非连通图【广度、深度】
- 生成森林
- 69、普利姆算法(稠密)与克鲁斯卡尔算法(稀疏)的构造最小生成树的过程
- 写步骤
- 普利姆算法:先去一个顶点A,然后找出与顶点相邻的且权值最小值的,然后连接到B。以此类推
- 克鲁斯卡尔:把所有顶点都写上,然后在图中找出最短的权值并且不形成回路
- 70、最早发生时间,最迟发生时间,最早发生活动,最迟发生活动,关键路径(活动)(5分)
第8章 搜索结构
- 71、二分法顺序表(true) 并且有序且顺序
- 查找关键字与哪些数做了比较(选择题) 二分法不能用链表
- 72、创建二叉搜索树,并查找关键字(大向右,小向左,找一个元素分别和谁比)
- 哈希搜索
- 散列 负数+表长 大于或等于表长时,对表长%
- 给出关键字,哈希函数解决冲突的办法
- 让画出哈希表(先做出下标)(查找成功的次数)
- 算出成功查找的平均长度(二次散列)
- 在查找的时候是右一次,左一次,并且每次都是平方的关系
- 73、
- 构造哈希表时有关因素主要有 :装填因子,哈希函数,哈希冲突函数(填空题或者选择题)
- 第九章 排序
- 74、(一个大题)希尔排序:给出一组数,然后写每一次饿排序的结果,一般是gap=3
- 希尔排序是一种不稳定的排序方法
- 75、对于n较大时的平均情况而言,希尔排序比较快
- 基本有序的用 起泡排序,n是比较小的
- 无序的用快速算法并且n比较大,并且只能用顺序表
- 当n非常小的时候用直接插入排序
- 时间复杂度不确定
- 直接选择排序 是不稳定的,时间复杂度是O(n的平方)
- 76、堆排序是不稳定的,时间复杂度O(nlog2n)
- 归并排序是稳定的 空间复杂度是O(n)
- 分配排序是稳定的 从低位向高位排序 空间复杂度至少为O(n)