数据结构 复试考点

1、数组和链表的区别。(频繁考点)*

从逻辑结构上来看:
a)数组必须事先定义固定的长度,不能适应数据动态增减的情况,即数组的大小一旦定义就不能改变。当数据增加时,可能超过原先定义的元素的个数;当数据减少时,造成内存浪费;
b)链表动态进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。
从内存存储的角度看:
a)数组从栈中分配空间(用new则在堆上创建),对程序员方便快速,但是自由度小;
b) 链表从堆中分配空间,自由度大但是申请管理比较麻烦。
从访问方式类看:
a)数组在内存中是连续的存储,因此可以利用下标索引进行访问(随机访问);
b)链表是链式存储结构,在访问元素时候只能够通过线性方式由前到后顺序的访问,所以访问效率比数组要低。(顺序访问)

线性表的链式存储性能分析

(1)查找:由于查找需通过指针从第一个结点开始查找,因此,最好情况时间复杂度为O(1),最坏情况事件复杂度为O(n)
(2)删除、插入:单链表的插入和删除主要由两部分组成:第一部分是遍历查询第i个元素;第二部分就是插入和删除元素
★从整个算法来说,我们很容易推导出:它们的时间复杂度都是O(n)。如果在我们不知道第i个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储之间是没有太大的优势。但是如果我们希望从第i个位置,插入10个元素,对于顺序存储结构意味着,每一次插入都需要移动n-i个元素,每次都是O(n)。而对于单链表,我们只需要在第一次找到第i个位置的指针,此时为O(n),接下来只是简单地通过赋值移动指针而已,时间复杂度都是O(1)。
适用范围:对于插入或删除数据越频繁的操作,单链表的效率优势就越明显。

2单链表结构与顺序存储结构的优缺点(频繁考点)

◆存储分配方式
(1)顺序存储结构用一段连续的存储单元依次存储线性表的数据元素
(2)单链表采用链式存储结构,用一组任意的存储单元存放线性表的元素
◆时间性能
(1)查找:顺序存储结构O(1);单链表O(n)
(2)删除和插入:顺序存储结构需要平均移动表长一半的元素,时间为O(n);单链表在指出某位置的指针后,插入和删除时间仅为O(1)
◆空间性能
(1)顺序存储结构需要预分配存储空间,分大了,浪费,分小了易发生溢出;
(2)单链表不需要分配存储空间,只要有就可以分配,元素个数不受限制
头指针与头结点区别
(1)头指针:头指针是指向第一个结点存储位置的指针,具有标识作用(即常用头指针冠以链表的名字)。无论链表是否为空,头指针均不为空,是链表的必要元素。
(2)头结点:头结点放在第一个元素结点之前,方便在第一个元素结点前进行插入和删除操作。头结点的数据域可以不存储信息或存储线性表长度等信息,头结点不是链表的必要元素。
双向链表
双向链表是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。与单链表的主要区别是,双链表中的结点都有两个指针域,一个指向直接后继,另一个指向直接前驱。用空间换来时间上的性能改进。

3、逻辑结构:数据之间的相互关系。

集合 结构中的数据元素除了同属于一种类型外,别无其它关系。
线性结构 数据元素之间一对一的关系
树形结构 数据元素之间一对多的关系
图状结构或网状 结构中的数据元素之间存在多对多的关系。

4、邻接矩阵与邻接表 对比

1)在邻接矩阵表示中,无向图的邻接矩阵是对称的。矩阵中第 i 行或 第 i 列有效元素个数之和就是顶点的度。
在有向图中 第 i 行有效元素个数之和是顶点的出度,第 i 列有效元素个数之和是顶点的入度。
2)在邻接表的表示中,无向图的同一条边在邻接表中存储的两次。如果想要知道顶点的度,只需要求出所对应链表的结点个数即可。
有向图中每条边在邻接表中只出现一次,求顶点的出度只需要遍历所对应链表即可。求入度则需要遍历其他顶点的链表。
3)邻接矩阵与邻接表优缺点:
邻接矩阵的优点是可以快速判断两个顶点之间是否存在边,可以快速添加边或者删除边。而其缺点是如果顶点之间的边比较少,会比较浪费空间。因为是一个 n∗n 的矩阵。
而邻接表的优点是节省空间,只存储实际存在的边。其缺点是关注顶点的度时,就可能需要遍历一个链表。

5.用循环比递归效率高吗?

递归和循环两者完全可以互换。不能完全决定性地说循环地效率比递归的效率高。
2.1递归算法:
优点:代码简洁、清晰,并且容易验证正确性。
缺点:它的运行需要较多次数的函数调用,如果调用层数比较深,需要增加额外的堆栈处理(还有可能出现堆栈溢出的情况),比如参数传递需要压栈等操作,会对执行效率有一定影响。但是,对于某些问题,如果不使用递归,那将是极端难看的代码。在编译器优化后,对于多次调用的函数处理会有非常好的效率优化,效率未必低于循环。
2.2循环算法:
优点:速度快,结构简单。
缺点:并不能解决所有的问题。有的问题适合使用递归而不是循环。如果使用循环并不困难的话,最好使用循环。

6. 贪心算法和动态规划的区别

贪心算法:局部最优,划分的每个子问题都最优,得到全局最优,但是不能保证是全局最优解,所以对于贪心算法来说,解是从上到下的,一步一步最优,直到最后。

动态规划:将问题分解成重复的子问题,每次都寻找左右子问题解中最优的解,一步步得到全局的最优解.重复的子问题可以通过记录的方式,避免多次计算。所以对于动态规划来说,解是从小到上,从底层所有可能性中找到最优解,再一步步向上。

分治法:和动态规划类似,将大问题分解成小问题,但是这些小问题是独立的,没有重复的问题。独立问题取得解,再合并成大问题的解。
链表:

7.单链表和双链表(频繁考点)

当需要存储多个相同数据类型的时候,可以使用数组存储,数组可以通过下标直接访问,但数组有个缺点就是无法动态的插入或删除其中的元素(特别是操作第一个位置上的元素),而链表弥补了这个缺陷,对于元素的插入和删除操作是很方便的,不过访问元素的“性能”就差很多了。

所谓单链表,即只有一个指针,指向下一个元素(结点)的地址,只要知道单链表的首地址,就可以遍历整个链表了。由于链表结点是在堆区动态申请的,其地址并不是连续的,因此无法进行随机访问,只有通过前一结点的next指针才能定位到下一个结点的指针。

单链表只能向后遍历,不能逆序遍历,所以有了使用更广泛的双链表。即结点多了一个存储前一个结点地址的prev指针。双链表可以双向遍历,但也只能按顺序访问。

8.栈和队列的区别:(频繁考点)

队列:

队列就像我们平时排队一样,按照数据到达的顺序进行排队,每次新插入的一个结点排在队尾,删除一个结点只能从头才能出队。简言之,对元素的到达顺序,按照“先进先出”的原则。由于队列频繁的插入和删除,一般为了高效,使用固定长度的数组实现,并且可循环使用数组空间,在操作之前要判断处理的队列是否已满或为空。如果要动态长度,可以用链表实现,只要同时记住链表的首地址(队头front)和尾地址(队尾rear)。

栈:
栈的特点正好与队列相反,按照数据进栈的逆序出栈,即“先进后出”,每次入栈将元素放在栈顶,出栈时也只能从栈顶出栈,与队列类似,一般用定长数组存储栈元素,而不是动态的申请结点空间。进栈一般被叫做压栈,出栈被叫做弹栈。
由于压栈弹栈都在栈顶,所以只需要一个size字段存储当前栈的大小,初始化size为0,每次压栈时,size+1,注意栈是否已满,弹栈则size-1。

栈和队列的相同之处和不同之处?

相同点:①都是线性结构 ②插入操作都是在表尾进行 ③ 插入和删除的时间复杂度都是O(1),在空间复杂度上也相同 ④都可以通过顺序结构和链表实现 ⑤多链栈和多链队列的管理模式可以相同。

不同点:①删除数据元素的位置不同,栈在表尾进行,队列在表头进行 ②顺序栈能够实现多栈空间共享,而顺序队列不能。

9.算法特点:

算法五个特性: 有穷性、确定性、可行性、输入、输出
时间复杂度:算法的执行时间与原操作执行次数之和成正比。
空间复杂度:若输入数据所占空间只取决于问题本身,和算法无关,则只需要分析除输入和程序之外的辅助变量所占额外空间。

10、线性链表

数据结构中的每一个结点对应于一个存储单元,这种存储单元称为存储结点,简称结点。
结点由两部分组成:(1)用于存储数据元素值,称为数据域;(2)用于存放指针,称为指针域,用于指向前一个或后一个结点。
在链式存储结构中,存储数据结构的存储空间可以不连续,各数据结点的存储顺序与数据元素之间的逻辑关系可以不一致,而数据元素之间的逻辑关系是由指针域来确定的。
链式存储方式即可用于表示线性结构,也可用于表示非线性结构。
线性链表,head称为头指针,head=null(或0)称为空表,如果是两指针:左指针(llink)指向前件结点,右指针(rlink)指向后件结点。
线性链表的基本运算:查找、插入、删除。

11.树与二叉树 (频繁考点)

树是一种简单的非线性结构,所有元素之间具有明显的层次特性。
在树结构中,每一个结点只有一个前件,称为父结点,没有前件的结点只有一个,称为树的根结点,简称树的根。每一个结点可以有多个后件,称为该结点的子结点。没有后件的结点称为叶子结点。
在树结构中,一个结点所拥有的后件的个数称为该结点的度,所有结点中最大的度称为树的度。树的最大层次称为树的深度。
二叉树的特点:(1)非空二叉树只有一个根结点;(2)每一个结点最多有两棵子树,且分别称为该结点的左子树与右子树。
满二叉树是指除最后一层外,每一层上的所有结点有两个子结点,则k层上有2k-1个结点深度为m的满二叉树有2m-1个结点。
完全二叉树是指除最后一层外,每一层上的结点数均达到最大值,在最后一层上只缺少右边的若干结点。
二叉树存储结构采用链式存储结构,对于满二叉树与完全二叉树可以按层序进行顺序存储。
二叉树的遍历:
(1)前序遍历(dlr),首先访问根结点,然后遍历左子树,最后遍历右子树;
(2)中序遍历(ldr),首先遍历左子树,然后访问根结点,最后遍历右子树;
(3)后序遍历(lrd)首先遍历左子树,然后访问遍历右子树,最后访问根结点。

12.图(掌握基础概念)

G= ( V , E ) = ( 顶点,边)
无向完全图有n(n - 1)/ 2 个边 ,有向完全图有n(n - 1)个边 。n表结点。
边无向(),弧有向<>
迪杰斯特拉(Dijkstra)算法
是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。注意该算法要求图中不存在负权边。
弗洛伊德(Floyd)算法<邻接矩阵求>
是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
普里姆(Prim)算法
普里姆算法的基本思想:
从连通网络N= {V,E}中的某一顶点u0出发,选择与它关联的具有最小权值的边(u0,v),将其顶点加入到生成树顶点集合S中。以后每一步从一个顶点在S中而另一个顶点不在S中的各条边中选择权值最小的边(u,v),把它的顶点加入到集合S中。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合S中为止。
克鲁斯卡尔(Kruskal)算法
克鲁斯卡尔算法的基本思想:
设有一个有n个顶点的连通网络N= {V,E},最初先构造一个只有n个顶点,没有边的非连通图T= {V,∅},图中每个顶点自成一个连通分支。当在E中选到一条具有最小权值的边时,若该边的两个顶点落在不同的连通分支上,则将此边加入到T中;否则将此边舍去,重新选择一条权值最小的边。如此重复下去,直到所有顶点在同一个连通分支上为止。
图常用的遍历:一、深度优先搜索;二、广度优先搜索

13.深度优先搜索(遍历)步骤

① 访问起始点 v;

② 若v的第1个邻接点没访问过,深度遍历此邻接点;

③ 若当前邻接点已访问过,再找v的第2个邻接点重新遍历。

基本思想:——仿树的先序遍历过程。

遍历结果:v1->v2->v4->v8->v5-v3->v6->v7

14广度优先搜索(遍历)步骤:

① 在访问了起始点v之后,依次访问 v的邻接点;

② 然后再依次(顺序)访问这些点(下一层)中未被访问过的邻接点;

③ 直到所有顶点都被访问过为止。
4.图的连通性问题

1)对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。

2)最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树。

构造最小生成树有很多算法,但是他们都是利用了最小生成树的同一种性质:MST性质(假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集,如果(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必定存在一颗包含边(u,v)的最小生成树),下面就介绍两种使用MST性质生成最小生成树的算法:普里姆算法和克鲁斯卡尔算法。

Kruskal算法特点:将边归并,适于求稀疏网的最小生成树。

Prime算法特点: 将顶点归并,与边数无关,适于稠密网。

15.拓扑排序

拓扑排序对应施工的流程图具有特别重要的作用,它可以决定哪些子工程必须要先执行,哪些子工程要在某些工程执行后才可以执行。

我们把顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。

一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行(对于数据流来说就是死循环)。在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

拓扑排序的实现:
a.在有向图中选一个没有前驱的顶点并且输出
b.从图中删除该顶点和所有以它为尾的弧(白话就是:删除所有和它有关的边)
c.重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的,因此,也可以通过拓扑排序来判断一个图是否有环。

16关键路径

AOE-网是一个带权的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间。通常,AOE-网可用来估算工程的完成时间。
关键路径:在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。
关键活动:关键路径上的活动称为关键活动。关键活动:e[i]=l[i]的活动
由于AOE网中的某些活动能够同时进行,故完成整个工程所必须花费的时间应该为始点到终点的最大路径长度。关键路径长度是整个工程所需的最短工期。

与关键活动有关的量:
(1)事件的最早发生时间ve[k]:ve[k]是指从始点开始到顶点vk的最大路径长度。这个长度决定了所有从顶点vk发出的活动能够开工的最早时间。
(2)事件的最迟发生时间vl[k]:vl[k]是指在不推迟整个工期的前提下,事件vk允许的最晚发生时间。
(3)活动的最早开始时间e[i]:若活动ai是由弧<vk , vj>表示,则活动ai的最早开始时间应等于事件vk的最早发生时间。因此,有:e[i]=ve[k]
(4)活动的最晚开始时间l[i]:活动ai的最晚开始时间是指,在不推迟整个工期的前提下, ai必须开始的最晚时间。若ai由弧<vk,vj>表示,则ai的最晚开始时间要保证事件vj的最迟发生时间不拖后。因此,有:l[i]=vl[j]-len<vk,vj>

17查找

查找表是称为集合的数据结构。是元素间约束力最差的数据结构:元素间的关系是元素仅共在同一个集合中。(同一类型的数据元素构成的集合)

1.静态查找表

1)顺序查找(线性查找)

技巧:把待查关键字key存入表头或表尾(俗称“哨兵”),这样可以加快执行速度。

cint Search_Seq( SSTable  ST , KeyType  key ){

ST.elem[0].key =key;

for( i=ST.length; ST.elem[ i ].key!=key;  - - i  );

     return i;

} // Search_Seq

//ASL=(1+n)/2,时间效率为 O(n),这是查找成功的情况:
顺序查找的特点:
优点:算法简单,且对顺序结构或链表结构均适用。

缺点: ASL 太大,时间效率太低。

2)折半查找(二分查找)——只适用于有序表,且限于顺序存储结构。

若关键字不在表中,怎样得知并及时停止查找?

典型标志是:当查找范围的上界≤下界时停止查找。

ASL的含义是“平均每个数据的查找时间”,而前式是n个数据查找时间的总和,所以:

3)分块查找(索引顺序查找)

思路:先让数据分块有序,即分成若干子表,要求每个子表中的数据元素值都比后一块中的数值小(但子表内部未必有序)。然后将各子表中的最大关键字构成一个索引表,表中还要包含每个子表的起始地址(即头指针)。
特点:块间有序,块内无序。
查找:块间折半,块内线性

查找步骤分两步进行:
① 对索引表使用折半查找法(因为索引表是有序表);
② 确定了待查关键字所在的子表后,在子表内采用顺序查找法(因为各子表内部是无序表);

2.动态查找表

1)二叉排序树和平衡二叉树

二叉排序树的定义----或是一棵空树;或者是具有如下性质的非空二叉树:

(1)若它的左子树不空,则左子树上所有结点的值均小于根的值;

(2)若它的右子树不空,则右子树的所有结点的值均大于根的值;

(3)它的左右子树也分别为二叉排序树。

18二叉排序树又称二叉查找树。
平衡二叉树
又称AVL树,即它或者是一颗空树,或者具有如下性质:它的左子树和右子树都是平衡二叉树,且左子树与右子树的深度之差的绝对值不超过1。
平衡因子:该结点的左子树的深度减去它的右子树的深度。
平衡二叉树的特点:任一结点的平衡因子只能取:-1、0 或 1。
如果在一棵AVL树中插入一个新结点,就有可能造成失衡,此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转。

平衡旋转可以归纳为四类:单向右顺时针旋转(LL);单向左逆时针旋转(RR);双向旋转先左逆时针后右顺时针(LR);双向旋转先右顺时针后左逆时针(RL)

19哈希表

哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

1)哈希函数构造方法

直接定址法

取关键字或关键字的某个线性函数值为散列地址。

即 H(key) = key 或 H(key) = a*key + b,其中a和b为常数。

除留余数法

取关键字被某个不大于散列表长度 m 的数 p 求余,得到的作为散列地址。

即 H(key) = key % p, p < m。

数字分析法

当关键字的位数大于地址的位数,对关键字的各位分布进行分析,选出分布均匀的任意几位作为散列地址。

仅适用于所有关键字都已知的情况下,根据实际应用确定要选取的部分,尽量避免发生冲突。

平方取中法

先计算出关键字值的平方,然后取平方值中间几位作为散列地址。

随机分布的关键字,得到的散列地址也是随机分布的。

折叠法(叠加法)

将关键字分为位数相同的几部分,然后取这几部分的叠加和(舍去进位)作为散列地址。

用于关键字位数较多,并且关键字中每一位上数字分布大致均匀。

随机数法

选择一个随机函数,把关键字的随机函数值作为它的哈希值。

通常当关键字的长度不等时用这种方法。

构造哈希函数的方法很多,实际工作中要根据不同的情况选择合适的方法,总的原则是尽可能少的产生冲突。

通常考虑的因素有关键字的长度和分布情况、哈希值的范围等。

如:当关键字是整数类型时就可以用除留余数法;如果关键字是小数类型,选择随机数法会比较好。

2)哈希冲突的解决方法

开放定址法

Hi=(H(key) + di) MOD m i=1,2,…,k (k<=m)

当冲突发生时,使用某种探测技术在散列表中形成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则表明表中无待查的关键字,即查找失败。

当冲突发生时,使用某种探查(亦称探测)技术在散列表中寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。

按照形成探查序列的方法不同,可将开放定址法区分为线性探查法、二次探查法、双重散列法等。

a.线性探查法

hi=(h(key)+i) % m ,0 ≤ i ≤ m-1

基本思想是:探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+1],…,直到 T[m-1],此后又循环到 T[0],T[1],…,直到探查到 有空余地址 或者到 T[d-1]为止。

b.二次探查法

hi=(h(key)+i*i) % m,0 ≤ i ≤ m-1

基本思想是:探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+12],T[d+22],T[d+3^2],…,等,直到探查到 有空余地址 或者到 T[d-1]为止。缺点是无法探查到整个散列空间。

c.双重散列法

hi=(h(key)+i*h1(key)) % m,0 ≤ i ≤ m-1

基本思想是:探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+h1(d)], T[d + 2*h1(d)],…,等。

该方法使用了两个散列函数 h(key) 和 h1(key),故也称为双散列函数探查法。

定义 h1(key) 的方法较多,但无论采用什么方法定义,都必须使 h1(key) 的值和 m 互素,才能使发生冲突的同义词地址均匀地分布在整个表中,否则可能造成同义词地址的循环计算。

该方法是开放定址法中最好的方法之一。

链接法(拉链法)

将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为 m,则可将散列表定义为一个由 m 个头指针组成的指针数组 T[0…m-1] 。

凡是散列地址为 i 的结点,均插入到以 T[i] 为头指针的单链表中。

T 中各分量的初值均应为空指针。

在拉链法中,装填因子 α 可以大于 1,但一般均取 α ≤ 1。

20、内部排序

排序:将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。

稳定性——若两个记录A和B的关键字值相等,且排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。

1.插入排序

思想:每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。

简言之,边插入边排序,保证子序列中随时都是排好序的。

  1. 直接插入排序

    在已形成的有序表中线性查找,并在适当位置插入,把原来位置上的元素向后顺移。

时间效率: 因为在最坏情况下,所有元素的比较次数总和为(0+1+…+n-1)→O(n^2)。

其他情况下也要考虑移动元素的次数。 故时间复杂度为O(n^2)

空间效率:仅占用1个缓冲单元——O(1)

算法的稳定性:稳定

直接插入排序算法的实现:


```c
void InsertSort ( SqList &L ) 

{ //对顺序表L作直接插入排序

 for ( i = 2;  i <=L.length; i++) //假定第一个记录有序

{

     L.r[0]= L.r[i];

       j=i-1 ;                      //先将待插入的元素放入“哨兵”位置

     while(L[0] .key<L[j].key)

    {  

        L.r[j+1]= L.r[j];

        j--  ;                    

    }      //只要子表元素比哨兵大就不断后移

    L.r[j+1]= L.r[0];      //直到子表元素小于哨兵,将哨兵值送入

                                 //当前要插入的位置(包括插入到表首)

}

}

2)折半插入排序

子表有序且为顺序存储结构,则插入时采用折半查找定可加速。

优点:比较次数大大减少,全部元素比较次数仅为O(nlog2n)。

时间效率:虽然比较次数大大减少,可惜移动次数并未减少, 所以排序效率仍为O(n^2) 。

空间效率:仍为 O(1)

稳 定  性: 稳定
3)希尔排序—不稳定
基本思想:先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

优点:让关键字值小的元素能很快前移,且序列若基本有序时,再用直接插入排序处理,时间效率会高很多。

时间效率:当n在某个特定范围内,希尔排序所需的比较和移动次数约为n^1.3,当n->无穷,可减少到n(log2n)^2

空间效率:O(1)

4)快速排序

基本思想:从待排序列中任取一个元素 (例如取第一个) 作为中心,所有比它小的元素一律前放,所有比它大的元素一律后放,形成左右两个子表;然后再对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个。此时便为有序序列了。

优点:因为每趟可以确定不止一个元素的位置,而且呈指数增加,所以特别快!
前提:顺序存储结构

时间效率:O(nlog2n) —因为每趟确定的元素呈指数增加

空间效率:O(log2n)—因为递归要用栈(存每层low,high和pivot)

稳 定 性: 不 稳 定 — —因为有跳跃式交换。

算法:

```c
int partition(SqList &L,int low,int high)

{

  L.r[0] = L.r[low];

  pivot key = L.r[low].key;

    while(low < high)

    {

     while(low<high&&L.r[high]>=pivot) high--;

     L.r[low] = L.r[high];

     while(low<high&&L.r[low]<=pivot) low++;

     L.r[high] = L.r[low];

    }

    L.r[low] = pivot;

    return low;

}

5)冒泡排序

基本思路:每趟不断将记录两两比较,并按“前小后大”(或“前大后小”)规则交换。

优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;一旦下趟没有交换发生,还可以提前结束排序。
前提:顺序存储结构

冒泡排序的算法分析:

时间效率:O(n^2) —因为要考虑最坏情况

空间效率:O(1) —只在交换时用到一个缓冲单元

稳 定 性: 稳定 —25和25*在排序前后的次序未改变

冒泡排序的优点:每一趟整理元素时,不仅可以完全确定一个元素的位置(挤出一个泡到表尾),还可以对前面的元素作一些整理,所以比一般的排序要快。
选择排序:选择排序的基本思想是:每一趟在后面n-i 个待排记录中选取关键字最小的记录作为有序序列中的第i 个记录。

6)简单选择排序

思路异常简单:每经过一趟比较就找出一个最小值,与待排序列最前面的位置互换即可。
——首先,在n个记录中选择最小者放到r[1]位置;然后,从剩余的n-1个记录中选择最小者放到r[2]位置;…如此进行下去,直到全部有序为止。
优点:实现简单
缺点:每趟只能确定一个元素,表长为n时需要n-1趟
前提:顺序存储结构
时间效率:O(n^2)——虽移动次数较少,但比较次数较多
空间效率:O(1)
算法稳定性——不稳定

Void SelectSort(SqList  &L ) 
{

for (i=1;  i<L.length; ++i)
{
     j = SelectMinKey(L,i);  //在L.r[i..L.length]中选择key最小的记录

     if( i!=j )   r[i] <--> r[j]; //与第i个记录交换

      } //for

  }  //SelectSort

7)堆排序

设有n个元素的序列 k1,k2,…,kn,当且仅当满足下述关系之一时,称之为堆。
如果让满足以上条件的元素序列 (k1,k2,…,kn)顺次排成一棵完全二叉树,则此树的特点是:树中所有结点的值均大于(或小于)其左右孩子,此树的根结点(即堆顶)必最大(或最小)。
堆排序算法分析:

时间效率:O(nlog2n)。因为整个排序过程中需要调用n-1次HeapAdjust( )算法,而算法本身耗时为log2n;
空间效率:O(1)。仅在第二个for循环中交换记录时用到一个临时变量temp。
稳定性: 不稳定。
优点:对小文件效果不明显,但对大文件有效。

8)归并排序----稳定

将两个或两个以上有序表组合成一个新的有序表。
时间复杂度:O(nlogn)
空间复杂度:和待排记录等数量的辅助空间。

9)基数排序
时间复杂度:对于n各记录(每个记录含d个关键字,每个关键字取值范围为rd个值)进行链式基数排序的时间复杂度为O(d(n+rd)),其中每一趟分配的时间复杂度为O(n),每一趟收集的时间复杂度为O(rd)
10)各种内部排序方法的比较讨论
(1) 从平均时间性能看,快速排序最佳,其所需时间最省,但快速排序的最坏情况下的时间性能不如堆排序和快速排序。后两者相比较,在n较大时,归并排序所需时间较堆排序省,但它所需的辅助存储量最多。

(2)基数排序的时间复杂度可写成O(dn)。因此,它最适用于n值很大而关键字较小的序列。

(3)从方法的稳定性来比较,基数排序是稳定的内排方法,所需时间复杂度为O(n^2)的简单排序方法也是稳定的,然而,快速排序、堆排序和希尔排序等时间性能较好的排序方法是不稳定的。

  • 8
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在CSDN数据结构复试中,我将会展示我的数据结构知识和解决问题的能力。首先,我会通过面试官提供的问题来展示我对数据结构的理解和应用能力。我将会运用各种数据结构,例如数组、链表、栈、队列、树和图等,来解决不同类型的问题。同时,我会说明我选择数据结构的原因,以及每种数据结构的优缺点。 其次,我将会展示我对算法的理解和能力,因为数据结构和算法是紧密相连的。我会解释常见的排序算法,例如冒泡排序、插入排序、快速排序和归并排序,并分析它们的时间和空间复杂度。此外,我还会介绍常见的查找算法,例如二分查找和哈希查找。 除了理论知识,我还将通过编程实例来展示我的能力。我将会编写一些常见的数据结构和算法的代码,例如链表的插入和删除操作、树的遍历和搜索算法等。通过实际编程,我可以展示我的编码风格和注重细节的能力。 最后,在复试过程中,我会积极与面试官进行互动和交流。我会回答问题时展示我的思考过程,并提出我的解决方案。同时,我也会乐于接受面试官的指导和建议,以便提升自己的能力。 总的来说,通过在CSDN数据结构复试中展示我的数据结构知识、解决问题的能力和编程实践经验,我希望能够展现自己作为一名合格的数据结构工程师的潜力和价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值