算法与数据结构应知应会知识点
1、顺序存储结构的特点是什么?
顺序存储时,相邻数据元素的存放地址也相邻(逻辑与物理的统一);要求内容中可用存储单元的地址必须是连续的。存储密度大(=1),存储空间利用率高,但是插入或删除元素时不方便。
2、链式存储结构的特点是什么?
链式存储时,相邻数据元素可随意存放,但所占存储空间分两部分,一部分存放结点值,另一部分存放表示结点间关系的指针。
插入或删除元素时很方便,使用灵活。但是存储密度小(<1),存储空间利用率低。
3、顺序队列的“假溢出”是怎样产生的?
一般的一维数组队列的尾指针已经到了数组的上界,不能再有入队操作,但其实数组中还有空位置,这就叫做“假溢出”。采用循环队列是解决假溢出的途径。
4、如何知道循环队列是空还是满?
解决队满队空的办法有三:
①设置一个布尔变量以区别队满还是队空;
②浪费一个元素的空间,用于区别队满还是队空;
我们常采用法②,即队头指针、队尾指针中有一个指向实元素,另一个指向空闲元素。
③使用一个计数器记录队列中元素的个数(即队列长度);
判断循环队列的空标志是:f=rear 队满的标志是 f=(rear+1)%N
5、常用的五种数据结构运算?
插入、删除、查找、修改、排序。
6、线索化二叉链表的基本操作函数
**二:线索二叉树
线索二叉树的定义:n个结点的二叉链表中含有n+1[2n-(n-1)=n+1]个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")。
因为n个节点有2n个指针
又因为n个节点中有n-1条边(除了头结点没有边,其余节点都有一个父节点,相当于都有1条边,共n-1条)
剩下的空链域就是2n-(n-1)=n+1,即n+1个空指针
2.线索二叉树结构实现:线索化的实质就是将二叉链表中的空指针改为指指向前驱和后继的线索,这种过程在遍历二叉树时进行**
作用:线索化的作用是将树可以像链表一样遍历每一个节点 ,而不是用栈或递归的方法遍历
注:不同的线索化方式对应不同的线索化的遍历(并且一棵树只能线索化一次)
前序线索化-----前序线索化遍历 (根左右)
中序线索化-----中序线索化遍历 (左根右)
后序线索化-----后序线索化遍历 (左右根)
/*
递归形似完成前序遍历
*/
void preVisted(BitTree *root)
{
if(rootNULL) return ;
printf("%d ",root->data);
preVisted(root->lchild);
preVisted(root->rchild);
}
void nonPreVisted(BitTree *root)
{
if(rootNULL) return ;
Stack s;
init(&s);
push(&s,root);
while(!isEmpty(&s))
{
root=pop(&s);
printf("%d ",root->data);
if(root->rchild!=NULL) push(&s,root->rchild);
if(root->lchild!=NULL) push(&s,root->lchild);
}
}
7、无向图中的节点数量与边的关系函数是什么?
无向完全图 节点数为n,则有n(n-1)/2条边。(有向完全图有n(n-1)条边。
8、稀疏矩阵的基本存储方式,三类特殊矩阵的存储方式。
①三元组表、十字链表。
②对称矩阵:aij=aji,对称矩阵中的元素关于主对角线对称,故存储矩阵中上三角或下三角中的元素,每两个对称的元素共享一个存储空间。这样,能节约近一半的存储空间。一般情况下,按行优先顺序存储主对角线(包括对角线)以下的元素。
三角矩阵:以主对角线划分,三角矩阵有上三角和下三角两种。右上左下。
三角矩阵中的重复元素c可共享一个存储空间,其余元素正好有n(n-1)/2个,因此,三角矩阵可压缩存储到向量sa[nx(n-1)/2+1]中,其中c存放在向量的最后一个分量中。
对角矩阵: 对角矩阵中,所有非零元素都集中在以对角线为中心的带状区域中,即除主对角线和主对角线邻近的上、下方,其他元素均为0.
对角矩阵可按行优先顺序或对角线的顺序,将其压缩存储到一个向量中,并且也能找到每个非零元素与向量下标的对应关系。
9、冒泡排序、希尔排序、快速排序的实现过程
希尔排序:缩小增量排序,
思想:首先,取一个小于n的整数d1作为第一个增量,把所需的所有数据分成d1个组,所有元素位置的距离为d1的整数倍放在同一数组中,在各组内进行直接插入排序;然后取第二个增量d2
代码:
void shellSort(int a[],int n)
{
int temp,i;
for(i=n/2;i>=1;i--)
{//增量序列i由n/2一次递减直至i=1
for(int j=0;ja[j+i];
a[i]=a[j+i];
a[j+i]=temp;
}
}
}
冒泡排序
它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
void bubbleSort(int data[],int n)
{
int i,j,t;
for(i=0;idata[j+1])
{
t=data[j];
data[j]=data[j+1];
data[j+1]=t;
}
}
//快速排序(老师)
快速排序又称划分交换排序。其基本思想:从一组待排的序列中,选一个“哨兵”,把所有
小于标兵的放在它的前面,大于标兵的放在它的后面。
通过这样一次划分,就把原来的序列划分成两个序列,然后再对左右两边的序列做同样的快速排序,直至划分后的序列只有一个元素,整个排序结束。
int quickOne(int a[],int low,int high)
{
int temp=a[low];//默认第一个为哨兵,存到一个临时空间
while(lowa[low]) low++;//从low向右扫描,找出第一个大于哨兵的元素。
a[high]=a[low];//把它存入high所指的位置;a[low]位置空间空余
}
a[low]=temp;//基准temp已被最后定位,此时low=high;
return low;
}
void quickSort(int a[],int low,int high)
{
int i=quickOne(a,low,high);
if(lowi+1)quickSort(a,i+1,high);//递归处理右边的数据
}
10、常用查找的平均查找长度计算公式,如二分查找、二叉排序树查找、线性探查法、拉链法
顺序查找法:ASL=(n+1)/2
二分查找:每一次最多查2(n-1)个,ASL=(1*1+2*2+3*4+4*8+···+i*(n-2(i-1)))/n
n是总数,次数每次查找的个数,最后一次不一定是2^(次数-1)个,是剩下的个数。别忘了除以总数。
二叉排序树查找:ASL=(第一次(层)查找个数+第二层查找个数+···第i层查找个数)/总数
线性探查法、拉链法
11、二叉链表算法
BitTree *createBitTree()
{
BitTree *root=NULL;
int m;
scanf("%d",&m);
if(m>0)
{
root=(BitTree*)malloc(sizeof(BitTree));
root->data=m;
root->lchild=createBitTree();
root->rchild=createBitTree();
}
return root;
}
12、双向链表的基本操作
双链表,在双链表中的每个结点除数据域外还有两个指针域:一个指向其前驱结点;另一个指向其后继结点。
13、插入排序的基本查找次数如何计算
答案:B
//插入排序
void InsertSort(int a[],int n)
{
int i,j,temp;
for(i=1;i=0&&temp
-------------------------)
14、堆栈的基本操作
栈是一种特殊的线性表,允许插入和删除运算的一算称为栈顶,不允许插入和删除运算的一算称为栈底。
/*
栈的定义:
栈是限定在表位插入和删除操作的线性表
允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)
不含任何数据元素的栈称为空栈。
栈的特点:
先进后出
后进先出(Last In First Out) 简称LIFO结构
栈的插入操作称为进栈,也称为压栈、入栈(push)
*/
/*链栈的结点*/
typedef struct StackNode
{
ElementType data;//节点中保存的元素
struct StackNode* next;//指向下一个结点的指针
}StackNode;
/*链栈结构*/
typedef struct LinkedStack
{
StackNode*top;//栈顶指针
int length;//链栈的长度(元素个数)
}LinkedStack;
/*链栈的初始化*/
void InitLinkStack(LinkedStack*linkedStack)
{
//1.栈顶指针指向空
linkedStack->top=NULL;
//2.长度为0
linkedStack->length=0;
}
/*压栈并返回结果*/
int PushLinkedStack(LinkedStack*linkedStack,ElementType element)
{
//1.创建一个新结点
StackNode*newNode=(StackNode*)malloc(sizeof(StackNode));
newNode->data=element;
//新结点的next域指向当前的栈顶
newNode->next=linkedStack->top;
linkedStack->top=newNode;
linkedStack->length++;
return TRUE;
}
/*出栈并返回出栈的元素*/
int PopLinkedStacked(LinkedStack*linkedStack,ElementType*element)
{
if(linkedStack->top==NULL)
{
printf("这是一个空栈,出栈操作失败!\n");
return FALSE;
}
//返回栈顶元素
*element=linkedStack->top->data;
StackNode*node=linkedStack->top;
//栈顶指针下移一位
linkedStack->top=linkedStack->top->next;
//释放原栈顶空间
free(node);
linkedStack->length--;
return TRUE;
}
15、广义表的原子操作
广义表表示
(1)广义表常用表示
① E=()
E是一个空表,其长度为0。
② L=(a,b)
L是长度为2的广义表,它的两个元素都是原子,因此它是一个线性表
③ A=(x,L)=(x,(a,b))
A是长度为2的广义表,第一个元素是原子x,第二个元素是子表L。
④ B=(A,y)=((x,(a,b)),y)
B是长度为2的广义表,第一个元素是子表A,第二个元素是原子y。
⑤ C=(A,B)=((x,(a,b)),((x,(a,b)),y))
C的长度为2,两个元素都是子表。
⑥ D=(a,D)=(a,(a,(a,(…))))
D的长度为2,第一个元素是原子,第二个元素是D自身,展开后它是一个无限的广义表。
(2)广义表的深度
一个表的"深度"是指表展开后所含括号的层数。
【例】表L、A、B、C的深度为分别为1、2、3、4,表D的深度为∞。
(3)带名字的广义表表示
如果规定任何表都是有名字的,为了既表明每个表的名字,又说明它的组成,则可以在每个表的前面冠以该表的名字,于是上例中的各表又可以写成:
①E()
②L(a,b)
③A(x,L(a,b))
④B(A(x,L(a,b)),y)
⑤C(A(x,l(a,b)),B(A(x,L(a,b)),y))
⑥D(a,D(a,D(…)))
注意:
广义表()和(())不同。前者是长度为0的空表,对其不能做求表头和表尾的运算;而后者是长度为l的非空表(只不过该表中惟一的一个元素是空表),对其可进行分解,得到的表头和表尾均是空表()。
16、树的层次与节点计算的2种算法
17、二叉树的三种遍历算法,根据其中2种遍历结果推算出第3中遍历结果;中序遍历线索图的绘制过程。
(在另一个试卷中)
18、大根堆和小根堆如何构建
Heap是一种数据结构具有以下的特点:
1)完全二叉树;
2)heap中存储的值是偏序;
Min-heap: 父节点的值小于或等于子节点的值;
Max-heap: 父节点的值大于或等于子节点的值;
堆的存储:
一般都用数组来表示堆,i结点的父结点下标就为(i–1)/2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
19、哈夫曼编码和WPL计算
(笔记2)
20、无向图的邻接矩阵及其对应的顺序表,从顶点A出发的深度DFS遍历序列和广度WFS遍历序列
(笔记2)
21、普里姆(Prim)算法和鲁斯卡尔算法求其最小生成树
普里姆(Prim)算法
从指定顶点开始将它加入集合中,然后将集合内的顶点与集合外的顶点所构成的所有边中选取权值最小的一条边作为生成树的边,
并将集合外的那个顶点加入到集合中,表示该顶点已连通.
再用集合内的顶点与集合外的顶点构成的边中找最小的边,并将相应的顶点加入集合中,如此下去直到全部顶点都加入到集合中,即得最小生成树.
鲁斯卡尔算法
方法:将图中边按其权值由小到大的次序顺序选取,若选边后不形成回路,则保留作为一条边,若形成回路则除去.依次选够(n-1)条边,即得最小生成树.(n为顶点数)
步骤:
第一步:由边集数组选第一条边,即权值为1的边;
第二步:选第二条边,即权值为2的边;
第三步:选第三条边,即权值为3的边;
第四步:选第四条边,即权值为4的边;
第五步:选第五条边。
22、有向图G如下图所示,将其看成AOE网给出关键路径和关键路径长度
带权有向图,顶点表示事件,弧表示活动。弧上的权值表示活动持续时间,此有向图称为AOE网,在建筑学也称为关键路线。AOE网常用于估算工程完成时间。
ve(j)
【含义】 事件(顶点)最早发生的时间
【解释】这个事件(这个工作,这个工程)最早什么时候能开始
【定性来谈】ve(j)=从源点到顶点j的最长路径长度
汇点(终点)的【最早发生时间】:即为整个工程能够【最早完成的时间】
【解释】中途不拖拉,一个工作完成,下一个工作立刻动手。整个工程保质保量的干下来(效率最高),最早能够完成的时间。我们记为plan_time,后面要用到
计算技巧:
(1)从前向后,取大值:直接前驱结点的Ve(j)+到达边(指向顶点的边)的权值,有多个值的取较大者
(2)首结点Ve(j)已知,为0
vl(k)
【含义】事件(顶点)的最迟发生时间vl(k)
【解释】上面谈到plan_time,与这个有关。假设,我们要在plan_time的时间内把整个工作做完,每个小工作最迟开始的时间称之为【最迟发生时间】。也就是说,【小工程】最迟什么时候开始,它不会影响总工程的按时的完成
【定性来谈】vl(k)=从顶点k到汇点(终点)的最短路径长度
最迟发生时间:Ø l(i): 若活动ai由弧
计算技巧: (1)从后向前,取小值:直接后继结点的Vl(j) –发出边(从顶点发出的边)的权值,有多个值的取较小者;
(2)终结点Vl(j)已知,等于它的Ve(j)
23、二维数组的行优先和列优先的存储地址的计算
**行优先顺序:将数组元素按行向量排列,第i+1个行向量紧接在第i行向量之后,以二维数组为例,按行优先顺序存储的线性序列为:
a11,a12,,a1n,a21,a22,
,a2n,am1,am2,amn 列优先顺序:将数组元素按列向量排列,第j+1个列向量紧接在第j个列向量之后,A的m*n个元素按列优先顺序存储的线性序列为: a11,a21,
,am1,a12,a22,,am2,
,a1n,a2n,amn
二维数组A[4][5]按行优先顺序存储,若每个元素占2个存储单元,且第一个元素A[0][0]的存储地址为1000
则数组元素A[3][2]存储地址为?求详解 请给详细过程和思路解答 这种题该怎么做?
A[i][j]的首地址 =
数组的在内存中的基地址(=1000)
24、采用堆栈实现二叉树的深度算法
int Getdepth(BTree*T)
{ int ld,rd;
if(!=T) return 0;
else{
ld=Getdepth(T->lchild);
rd=Getdepth(T->rchild);
return ld>rd?(ld+1):(rd+1);
}
}
25、时间复杂度计算方法
1、常数阶
首先顺序结构的时间复杂度。
这个算法的运行次数函数是f (n) =3。 根据我们推导大0阶的方法,第一步就是把常数项3 改为1。在保留最高阶项时发现,它根本没有最高阶项,所以这个算法的时间复杂度为0(1)。
*另外,我们试想一下,如果这个算法当中的语句 sum = (1+n)n/2; 有10 句,则与示例给出的代码就是3次和12次的差异。这种与问题的大小无关(n的多少),执行时间恒定的算法,我们称之为具有O(1)的时间复杂度,又叫常数阶。对于分支结构而言,无论是真,还是假,执行的次数都是恒定的,不会随着n 的变大而发生变化,所以单纯的分支结构(不包含在循环结构中),其时间复杂度也是0(1)。
2、线性阶
线性阶的循环结构会复杂很多
由于每次count乘以2之后,就距离n更近了一分。 也就是说,有多少个2相乘后大于n,则会退出循环。 由2^x=n 得到x=logn。 所以这个循环的时间复杂度为O(logn)。
4、平方阶
下面例子是一个循环嵌套,它的内循环刚才我们已经分析过,时间复杂度为O(n)。
而对于外层的循环,不过是内部这个时间复杂度为O(n)的语句,再循环n次。 所以这段代码的时间复杂度为O(n^2)。
如果外循环的循环次数改为了m,时间复杂度就变为O(mXn)。
这里循环了(12+22+32+……+n2) = n(n+1)(2n+1)/6次,按照上述大O阶推导方法,时间复杂度为O(n^3)。
26、数据结构的表达表示方法
?
27、带头结点和无头结点的单链表L的判空条件
带头结点的单链表的L指向头结点
则L->nextNULL
不带头结点的单链表的L指向第一个结点
则LNULL
28、堆栈的基本操作算法
/*
栈的定义:
栈是限定在表位插入和删除操作的线性表
允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)
不含任何数据元素的栈称为空栈。
栈的特点:
先进后出
后进先出(Last In First Out) 简称LIFO结构
栈的插入操作称为进栈,也称为压栈、入栈(push)
*/
//定义数据元素
typedef struct
{
int x;
int y;
int type;
}ElementType;
/*链栈的结点*/
typedef struct StackNode
{
ElementType data;//节点中保存的元素
struct StackNode* next;//指向下一个结点的指针
}StackNode;
/*链栈结构*/
typedef struct LinkedStack
{
StackNode*top;//栈顶指针
int length;//链栈的长度(元素个数)
}LinkedStack;
/*链栈的初始化*/
void InitLinkStack(LinkedStack*linkedStack)
{
//1.栈顶指针指向空
linkedStack->top=NULL;
//2.长度为0
linkedStack->length=0;
}
/*压栈并返回结果*/
int PushLinkedStack(LinkedStack*linkedStack,ElementType element)
{
//1.创建一个新结点
StackNode*newNode=(StackNode*)malloc(sizeof(StackNode));
newNode->data=element;
//新结点的next域指向当前的栈顶
newNode->next=linkedStack->top;
linkedStack->top=newNode;
linkedStack->length++;
return TRUE;
}
/*出栈并返回出栈的元素*/
int PopLinkedStacked(LinkedStack*linkedStack,ElementType*element)
{
if(linkedStack->top==NULL)
{
printf("这是一个空栈,出栈操作失败!\n");
return FALSE;
}
//返回栈顶元素
*element=linkedStack->top->data;
StackNode*node=linkedStack->top;
//栈顶指针下移一位
linkedStack->top=linkedStack->top->next;
//释放原栈顶空间
free(node);
linkedStack->length--;
return TRUE;
}
29、散列法存储存在几种冲突,常用的冲突处理方法有?
在线性表的散列存储中,处理冲突的常用方法有()和()两种。
开放定址法;链接法
30、共享栈的基本概念和操作
共享栈,即是两个栈使用同一段存储空间。
第一个栈从数组头开始存储,第二个栈从数组尾开始,两个栈向中间拓展。
当top1+1top2或者top1top2-1时,即staock overflow!.
与普通栈一样,共享栈出栈入栈的时间复杂度仍为O(1).
31、KMP算法的特点
KMP算法最大的特点就是指示主串的指针不需要回溯,因此指针不可能变小
32、邻接表是一种结合了顺序存储与链式存储的存储格式,在那些结构中被使用
图的存储结构
33、Dijkstra方法计算有向图G的最短路径生成和计算
我记得这个方法好像有问题,我有点忘记了,但是其他的方法在另一个笔记中。过了好几个月了,我基本上都还回去了。
给自己讲的:我记得了,试卷别丢啊,这个是一个同学院同学给我的复习题里面的,在那个里面手写了这个比较好算的方法可能。我真的是越来越健忘了,救救孩子吧。