数据结构期末复习笔记(NEU版)

绪论

我相信大家都厌烦了只讲解决方法,而不讲原理的博客了,本篇力求将将难点的原理和容易让人困惑的地方加上原理和原因。

数据结构的概念

非数值计算解决方法的一般步骤:

  1. 数据间的关系如何表示 :利用数据的逻辑结构。
  2. 数据在计算机内如何存储:数据的存储结构(物理结构)。
  3. 求解问题,即数据的操作:数据的运算,定义在数据的逻辑结构上的抽象的操作。

因此:.数据结构是研究数据的物理结构、逻辑结构以及它们之间的相互关系。

数据结构的分类

  • 按数据元素之间的关系分:线性结构和非线性结构。‘
  • 按逻辑结构分:集合结构,线性结构,树型结构,图型结构。
  • 按存储方式分:顺序存储、链式存储、索引存储结构、散列存储结构。

数据结构研究的内容

逻辑结构、存储结构和操作。

算法的重要特性

有穷性、确定性、可行性、输入、输出。

算法的设计要求

正确性、可读性、健壮性、效率与低存储量要求。

题目汇总

  1. 二元组 Date = (D,S) : D 表示 数据元素的有限集 ,S 表示D上的关系的有限集。
  2. 数据元素相互之间的关系称为 :结构。
  3. 非线性结构是元素之间存在一种 : 多对多关系。
  4. 算法分析的目的是:分析算法的效率以求改进。
  5. 计算机算法指的是:解决问题的有限运算序列。
  6. 数据元素是数据的基本单位,数据项才是数据不可分割的最小单位。

线性表

首元结点:指链表中存储第一个数据元素a1的结点。

头结点:是为了操作方便在首元结点前复设的结点。

头指针:指向链表中第一个结点(或为头结点或为首元结点)的指针。

如何没有头指针,那么不带头结点的线性链表只有在删除第一个节点,即首元结点时,需要更改头指针。如果删除其他结点,则不需要更改头指针。而如果带了头结点的话就不用特判了,便于处理。

  1. 在顺序表中插入和删除一个结点平均移动多少个结点:在等概率情况下,顺序表中插入一个结点需要平均移动n/2个结点删除一个结点需要平均移动(n-1)/2个结点。
    原因:1、当对n个元素进行插入操作时,有n+1个位置可以进行插入,如下所示(“.”代表可以插入的位置)。
    .1.2.3.4. -- .n.
    在每个位置插入时需要移动的元素个数分别为n,n-1,n-2…,1,0,所以,总共需要移动的元素个数为(1+2+3+4+…+n)=n*(n+1)/2。
    故平均需要移动的元素个数为n*(n+1)/2(n+1)=n/2;
    2、当对N个元素进行删除操作时,有N个位置可以删除。
    1,2,3,4…n
    每个位置需要移动的元素个数分别为n-1,n-2,n-3…1,0个。所以平均需要移动的元素个数为(n-1)n/2n=(n-1)/2个。
  2. 任意单元的地址: a i = a 1 + ( i − 1 ) d a_i = a_1 + (i - 1) d ai=a1+(i1)d
  3. 在一个长度为n的顺序表中,在第i个元素之前插入一个元素 ,需要向后移动:n - i + 1 个元素。原因:第i个元素之前那么后边元素为i~n ,那么n - i 得到的是是i后边的元素个数,因此加上i , 故为 n - i + 1 。
  4. t指向最后一个元素的意思是,t就是最后一个元素的指针,切记。
  5. 在一个长度为n的顺序表中,删除第i个元素 ,需要向前移动:n - i 个元素。原因:第i个元素之前那么后边元素为i+1~n ,那么n - (i + 1 ) 得到的是是i+1后边的元素个数,因此加上i+1这个元素 , 故为 n - (i + 1 ) + 1 = n - i 。
  6. 10.链接存储的特点是利用__指针______来表示数据元素之间的逻辑关系。

11.顺序存储结构是通过__物理上相邻______表示元素之间的关系的;链式存储结构是通过___指针_____表示元素之间的关系的。

  1. 对于双向链表,在两个结点之间插入一个新结点需修改的指针共 __4____个,单链表为____2___个。

  2. 循环单链表的最大优点是:从任一结点出发都可访问到链表中每一个元素

  3. 带头结点的双循环链表L中只有一个元素结点的条件是:___ L->next->next==L _____

  4. 在单链表L中,指针p所指结点有后继结点的条件是:__ p->next!=null

  5. 带头结点的双循环链表L为空表的条件是:___ L->nextL && L->priorL_____。

  6. 带头结点的单循环链表L为空表的条件是:___ L->next==L _____。

栈和队列

  1. 对于顺序表,设base 为栈底指针,top为栈顶指针,则判断栈空的条件是:base == top ,栈中元素个数 top - base.(当栈顶指针指向当前栈顶元素)
  2. 当栈顶指针指向当前栈顶元素,那么插入(进栈)操作是 :top++ , base[top] = val, 退栈操作是:top--; 当栈顶指针指向栈顶元素的下一个位置,那么 进栈操作是base[top] = val , top++ , 退栈操作是 top--。感觉没啥用,不过就是这样。。。
  3. 前缀表达式、中缀表达式和后缀表达式:(以运算符所在的位置来命名的)在这里插入图片描述

转化方法

补充一个:利用栈将中缀转化为后缀的方法:从左到右遍历中缀表达式的每个数字和符号,若是数字则直接输出,若是符号,则判断其与栈顶符号的优先级,是右括号或者优先级低于栈顶符号,则栈顶元素依次出栈并输出,直到遇到左括号或栈空才将低优先级的那个符号入栈。

  1. 表达式求值。第一步先将中缀转化为后缀。然后利用后缀进行计算。计算方法如下:
    建立两个栈,一个为数值栈,一个为运算符栈。当遇到数值就直接压入数值栈中。当遇到运算符,分为两种情况,一种是输入运算符优先级大于栈顶优先级,直接压栈,另一种是输入运算符优先级小于栈顶优先级(符号相同也算),先从数据栈中弹出两个元素,与运算符弹出的栈顶符号相运算,运算结果压入,后将运算符压入栈。

队列

  1. 在循环队列中,队空标志为front == rear , 队满标志为:(reat + 1 ) % m == front ; 当 rear >= front 时 ,队列长度为 rear - front , 反之 为 (rear + m - front) % m
  2. 带头结点的链队列Q , 判定队列中只有一个元素的条件是:Q.front - > next = Q.rear
  3. 顺序队列 ,对于一个一维数组 ,有两种实现方法,一种是 front 与 rear 最初同时指向一个位置(一般是0) 即rear 指向的是队尾下一个元素的位置, 那么 进队操作是 :q[rear] = val , rear++ , 出队操作是:if(front < rear) front ++ 对空操作是 :rear == front; 另一种实现方式是 front = 0 ,rear = - 1 ; 即rear 指向的是队尾元素的位置. 进队操作:q[++rear] = val , 出队操作是:if( front <= rear) front++
  4. 对于链式队列 , 起初 front 和 rear 指针同时指向一个起始地址 , 入队操作:p->data = val , p->next = NULL , Q.rear->next = p ; Q.rear = p; 所以 rear 指针指向的是队尾元素的位置 , 判断对空操作:Q.rear == Q.front

题目

  1. 空间大小为n 的循环队列 ,执行出队操作后,front = (front+1) % n
  2. 长度为N的循环队列,其队头指针为P , 头尾指针为R ,则 当前队列中元素个数为:(R-P + N )% N;

数组与广义表

数组与广义表

二叉树

  1. 对于完全二叉树,如果节点个数为奇数,那么就不存在度为1的节点。当节点个数为偶数的时候,当且仅当存在一个度为1的节点。
  2. 二叉树的第k层最多有 2 k − 1 2^{k-1} 2k1 个节点。因为第1层即根节点有 2 0 2^{0} 20 ,而下一层最多是上一层的两倍。
  3. 高度为k的二叉树最多有 2 k − 1 2^{k} - 1 2k1个节点。依次为 2 0 、 2 1 . . . . 2 k 这 是 一 个 首 项 为 1 公 比 为 2 , 项 数 为 k 的 等 比 数 列 2^0 、2^1 .... 2^k 这是一个首项为1 公比为2,项数为k的等比数列 2021....2k12k
  4. 对于任意一颗二叉树, n 2 为 度 为 2 的 节 点 , n 1 为 度 为 1 的 节 点 , n 0 为 度 为 0 得 节 点 ( 即 叶 子 ) n_2 为度为2的节点 , n_1 为度为1的节点 , n_0 为度为0得节点(即叶子) n22n11n00 那么有如下关系: n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
  5. 具有n个节点的完全二叉树,数的高度 : k = ⌊ log ⁡ 2 n ⌋ + 1 k = \lfloor \log_{2}{n} \rfloor+1 k=log2n+1。因为完全二叉树有一个性质是高度为k的树,那么k-1 层往上是满二叉树。那么 k-1 层以上包括k-1 层的节点个数为 2 k − 1 − 1 2^{k-1} - 1 2k11 ,那么有节点个数n , 2 k − 1 − 1 < n < = 2 k − 1 2^{k-1} - 1 < n <= 2^{k} - 1 2k11<n<=2k1 ,两边同时加一有 , 2 k − 1 < = n < 2 k 2^{k-1} <= n < 2^{k} 2k1<=n<2k 两边同时取对数,得到: k − 1 < = log ⁡ 2 n < k k-1 <= \log_{2}{ n} < k k1<=log2n<k , k 和 k-1 是相邻的两个数,因此有 k − 1 = ⌊ log ⁡ 2 n ⌋ k -1 = \lfloor \log_{2}{n} \rfloor k1=log2n ,即 k = ⌊ log ⁡ 2 n ⌋ + 1 k = \lfloor \log_{2}{n} \rfloor+1 k=log2n+1
  6. 对于节点个数为n的完全二叉树,编号为i 的节点 ,如果 2i > n ,那么节点i无左孩子,如果 2i +1 > n , 那么节点i无右孩子。

二叉树的遍历

二叉树的各种遍历方法

先序:根、左、右
中序:左、根、右
后序:左、右、根

  1. 有二叉树的先序、中序 或者 后序、中序 的结果才可以唯一确定一颗树。因为先序和后序的作用是得到根节点的位置,而中序是确定节点的左右次序。

给定二叉树先序和中序画出二叉树
先序数列:ABDGHCEFI
中序数列:GDHBAECIF

先序序列的第一个值为根节点:A为根节点
再到中序数列中找到根节点,以根节点A为界分为左右两部分,左:GDHB、右:ECIF
在先序序列找到与中序数列左右部分对应的序列部分,左:BDGH、右:CEFI,可得到左边结点为B,右边结点为C

在这里插入图片描述

分别对左子树和右子树进行上述操作,左子树(先序:CEFI,中序:ECFI)以C为界限分为 左:E、右:IF,因为先序序列F在I前所以F在I上一层,因为中序数列I在F前面,所以I在左分支;右子树(先序:BDGH,中序:GDHB)以B为界限分为左:GDH,因为先序中D在第一个,所以D为第一个结点针对(先序:DGH,中序:GDH)重复上述方法可得:

在这里插入图片描述
由上面后序的节点排布得知,后序和中序与其类似,只不过是最后一个节点是根节点。

二叉树的基本操作

线索二叉树

  • . 如何画中序线索二叉树:1. 写出二叉树的中序遍历序列。2、依据这个序列来进行画图。(节点又前驱且左子树为空则连上前驱节点,后继同理).

  • 对于用二叉链存储的二叉树,如果有n个节点,那么 会有 n + 1 个空指针。由 n = n 0 + n 1 + n 2 与 n 0 = n 2 + 1 得 到 n = 2 n 0 + n 1 + 1 n = n_0 + n_1 + n_2 与 n_0 = n_2 + 1得到 n = 2n_0+n_1+1 n=n0+n1+n2n0=n2+1n=2n0+n1+1,那么由一个度为0的节点会有两个空指针,度为1的有一个空指针。所以上式 将1往左移即得到结果: n + 1 = 2 n 2 + n 1 n + 1 = 2n_2 + n_1 n+1=2n2+n1

何为线索二叉树呢? 就是在原来二叉树的基础上,如果左孩子为空,那么就将这个左孩子指向该节点的前驱,同理 右孩子是指向该节点的后继。**那么什么叫节点的前驱和后继呢?**我们的线索二叉树分为前序线索二叉树,中序线索二叉树和后序线索二叉树。顾名思义,一个节点的前驱和后继就是按照遍历的方式,得到一个遍历的序列,在这个序列里边再该节点前边的节点就是其前驱,后边即后驱。

线索二叉树的查找

  • 在中序线索二叉树中查找给定节点的前驱节点

分为两种情况考虑。
1、如何该节点的左标志为1,即无左孩子,那么其左指针域指向的节点便是它的前驱节点。
2、如何该节点的左标志为0,即有左孩子,那么它的前驱节点则沿着左子树的右指针链(左子树的右孩子)向下寻找,直到找到一个没有右孩子的节点为止,即此时右标志为1的节点就是我们要找的前驱节点。即左子树中“最右下”的节点。

  • 在中序线索二叉树中查找给定节点的前驱节点

与查找前驱类似,也是有两种情况,第一组太简单一般不会考。第二种情况,类比与前驱,右子树中“最左下”的节点。

这里可能有人会问为什么只讲了,中序呢?难道只考中序的查找嘛?
对的,对于这一类问题只会考中序线索二叉树。原因是查找先序前驱和后继节点和后序前驱和后继节点常常要用到该定节点的双亲节点才能找到,而线索二叉树中的节点没有指向其双亲节点的指针。

哈夫曼树

带权路径长度

路径是从根节点到该节点经过的边的个数(层数减一)。

所以叶子节点(因为叶子节点就是编码的节点)* 路径 的总和。

基本概念

  • 稀疏图:指的是边数相对较少的图。存储稀疏图,用邻接表比邻接矩阵更省空间。
  • 在具有n个顶点,e条边的无向图中,所有顶点度的和是边的和的两倍。解释:对于每一条边,会对于两个顶点做出贡献。
  • 具有n个顶点,e条边的有向图中,所有顶点的入度 + 出度等于边数(弧数)两倍。
  • 简单路径:如果路径上的各顶点均不互相重复,称这样的路径为简单路径。
  • 回路(cycle)或环或圈:如果路径上的第一个顶点与最后一个顶点重合,这样的路径称为回路。回路显然不是简单路径。
  • AOV-网:用顶点表示活动,用弧表示活动间的优先关系的有向无环图
  • AOE-网:是带权的有向无环图,顶点表示事件,弧表示活动,权表示活动持续的时间。用来估计工程的完成时间。
  • 关键路径:完成工程的最短时间是从开始起点到完成点的最长路径长度,这个路径长度最长的的路径就叫关键路径。关键路径查找
  • 关键活动: L ( i ) = e ( i ) L(i) = e(i) L(i)=e(i)的活动。关键路径上的所有活动都是关键活动。
  • 只有在不改变网的关键路径的情况下,提高关键路径上活动的速度才有效。

图的常见考点

  1. 如果n个顶点的图是一个环,则它有多少个生成树?:n个。原因:生成树需要有n-1条边,那么我们在n条边选出一条边去掉就成立生成树。n条边中选一条,一共有n中选法。
  2. 无向连通图特性:所有顶点的度之和为偶数。原因:每条边连接两个顶点,所有顶点的度之和等于边数的2倍,是偶数。
  3. 若无向图 G 中含 7 个顶点,则保证图 G 在任何情况下都是连通的,则需要的边数:16.原因: 在任何情况下,意思就是说,只要有给定的边数则必定会连通,无论你的边怎么安排,怎么放,图G都能构成连通。
    因为,只需要n-1个顶点构成完全无向图,再加上1条边和剩下的顶点相连,就能让n个顶点连通。所有是 (6 * 5 ) / 2 + 1 = 16 。6条边是图 G 是连通的最少边数,不是在任何情况下的。
  4. 若用邻接矩阵存储有向图,矩阵中主对角线以下的元素均为零,则关于该图拓扑序列的结论是:存在且可能不唯一。原因:上三角矩阵说明有向图只有序号小的节点单向指向序号大的节点,所以存在拓扑序列。加上条件A[i][i+1]全为1才是唯一的,题目没有说明这一点,所以是可能唯一 (很经典,唯一性)。
  5. 关于最小生成树的叙述中,一定正确的是:最小生成树的代价唯一。
  6. 如何判断一个图是否有环:三种方法
  7. 连通无向图构成条件:边 = 顶点数 * ( 顶点数-1 ) /2
    所以28个条边的连通无向图顶点数最少为8个
    所以28条边的非连通无向图为9个(加入一个孤立点)

查找

静态查找表

顺序查找

讨论的是从后往前查找的方式。

  1. 查找第i个元素 需要比较 n − i + 1 n - i +1 ni+1次,则查找成功的平均查找长度为 A S L s s = 1 n ∑ i = 1 n ( n − i + 1 ) = n + 1 2 ASL_{ss} = \frac{1}{n} \sum_{i=1}^n (n - i +1) = \frac{n + 1}{2} ASLss=n1i=1n(ni+1)=2n+1

二分查找(折半查找)

折半查找成功或失败的平均查找长度

分块查找

在这里插入图片描述

动态查找表

二叉排序树

二叉排序树的介绍
二叉排序树为一颗二叉树,或者为空,或者满足如下条件:

  1. 如果它的左子树不为空,那么左子树上的所有结点的值均小于它的根结点的.值
  2. 如果它的右子树不为空,那么右子树上的左右结点的值均大于它的根结点的值
  3. 根结点的左子树和右子树又是二叉排序树。

二叉排序树通常采用二叉链表作为存储结构。中序遍历二叉排序树便可得到一个有序递增序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即是对无序序列进行排序的过程。

  • 对于一个给定的二叉搜索树,平均查找长度:这和折半查找的平均查找长度的计算是一样的,可以参考上面的方法。

删除操作

删除分为三种情况。

  1. 删除结点为叶子结点(左右子树均为NULL),所以我们可以直接删除该结点,如下图所示:
    在这里插入图片描述
  2. **删除结点只有左子树或者右子树。**此时只需要让其左子树或者右子树直接替代删除结点的位置,称为删除结点的双亲的孩子,就可以了,如下图所示:
    在这里插入图片描述
  3. **当要删除的那个结点,其左右子树都存在的情况下,**则要在中序遍历中将该节点(p)的前驱节点(s)的值替代该节(p)的值再删除s节点。即从从左子树中找出一个最大的值那个结点来替换我们需要删除的结点,因为对于二叉排序树中序遍历得到的是一个递增的序列那么在p节点的前驱即时其左子树中的最大值,即如下图所示:
    在这里插入图片描述
    在这里插入图片描述

平衡二叉树

平衡二叉树的构造

哈希表

重点考察的是在解决冲突这块。因此我们重点讲解这里。

一般来说有两种类型的解决方法:

1、开放地址法

散列地址的计算公式是:
H i = ( H ( k e y ) + d i ) ) m o d m ( i = 1 , 2 , . . . , k ( k < = m − 1 ) ) H_i = (H(key) + d_i)) mod\quad m (i = 1,2,...,k ( k <= m-1)) Hi=(H(key)+di))modm(i=1,2,...,k(k<=m1))
其中H(key)为哈希函数;m为散列表的长度 ; d i d_i di为第i次探测时的增量序列。 d i 可 以 有 下 列 3 种 取 法 d_i可以有下列3种取法 di3,下面只介绍常考的两种。

  1. 线性探测再散列
    d i = 1 , 2 , . . , m − 1 d_i = 1,2,..,m-1 di=1,2,..,m1它是从发生冲突的d单元起,依次探查下一个单元,直到碰到一个空闲单元为止。
  2. 二次探测再散列
    d i = 1 2 , − 1 2 , 2 2 , − 2 2 . . . . ( + − ) k 2 ( k < = m / 2 ) d_i = 1^2, - 1^2 , 2^2,-2^2 .... (+-) k^2 (k<=m/2) di=12,12,22,22....(+)k2(k<=m/2) ,当发生冲突时,在表的左右进行跳跃探测。

2、链地址法

将有相同哈希值的记录链成一个单链表,m个哈希地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。有冲突的元素可以插在表尾也可以插在表头。链地址法用于经常进行插入和删除操作的情况。

3、散列表查找成功和失败的平均查找长度

假设 , K 为关键字的个数(即有k个值),m 为散列表的长度。

  • 首先理解查找成功和失败的时候的分母是多少。首先要知道,我们求平均查找长度其实在求概率,对于成功的平均查找长度,查找每个元素的概率是1/k,这个k就是关键字个数。就是说,查找成功你只能选择k个中的一个,否则成功不了,所以都是在等概率1/k情况下。查找失败也是一样,这里对每个位置的查找概率都是1/m,注意m是你要模的数字,就是说,你随便拿几个数,模m,只能是0…m-1,其中一种情况,不可能是m、m+1以后的数字,所以只需要计算地址在0-(m-1)中的概率。

不过要注意一个特殊的情况,如下题型H(key)=key%7:
在这里插入图片描述

1. 查找成功

查找成功是除以关键字个数,但是失败不是8而是7。原因在上边,因此我们在理解的时候,将散列表的长度应理解为 求余的那个数。

  • 讲了原理之后我们就来谈谈如何高效的去做这一类问题吧。一般我们会做出一个表,如下图,散列地址和关键词没有什么好说的了。不过有一点注意的是,好比上图,我开始一个数是7 ,7%7 = 0 ,放在了0地址的位置,当我后一个是14 , 的时候发现0地址位置已经被占了,那么我们去找其存放的位置就应该按题意来,看是线性探测再散列,还是二次探测再散列。 回到正题,关键是第三行,冲突次数。
    8个数字 key%11在这里插入图片描述
    对于成功的平均查找长度,我们只需把没一个关键字下边的所有的(冲突次数+1)的和 除以 关键字个数即可。(1 + 1 + 1 + 3 + 4 + 1 + 2 + 8) /8. 发现写冲突次数还需要加1,防止我们加的时候出错,我们可以建立如下表格。
关键字3311312
比较次数1113

下面我们就来讲讲 ,这个比较次数如何来的吧。好比33 ,我们得到地址为0,一看就是,那好我们看了一眼对吧,那就是比较了一次。而想12,我们得到的地址是1,去1位置看呀不是,如果这个是由线性探测再散列建立的,那么我们往后看现在还不是到了位置3发现对上了,我们看看,我们和位置1,2,3比较够一共三次因此是3. (到这里我们发现,选择什么方式解决哈希冲突,那么在求解的时候就不一样了)。

2.查找失败
什么叫做查找失败?比如你要查55这个关键字,你必须%11,等于0,查地址0为33,不对,下一位1,不对,继续往下查找直到出现空的才知道这个关键字一定不存在,否则就放在空着的那个地址。所以以上表,计算地址为0的关键字查了9次才才知道没有这个关键字称为查找失败;计算地址为1的关键字只有探测完1-8号才能确定该元素不存在,以此类推。但是注意了,如果计算地址为8的或者9的、10的,只需要查找一次就知道该地址为空,失败了。因此查找一次就行。而且要知道如果查找成功是除以关键字个数,但是查找失败是除以你要模的数字本题是11,千万记住了,不是地址个数,不是关键字个数。综上所述,查找失败的平均查找长度为(9+8+7+6+5+4+3+2+1+1+1)/11=47/11。

下面来讲一下,链地址法平均查找长度

在这里插入图片描述

常见考点

  1. 为提高散列(Hash)表的查找效率,可以采取的正确措施是:设计冲突(碰撞)少的散列函数、处理冲突(碰撞)时避免产生聚集(堆积)现象、减小装填(载)因子。

排序

为什么将排序分为插入、选择、交换等几大类

首先我们来聊聊其中的原理:

  1. **插入排序:**将一个待排序的记录集合分为两个部分,一部分为有序集,一部分为无序区。每次从无序区中取一个记录,插入到有序区的适当位置,直到全部记录插入到有序区为止。简单来说,这个 插入 指的是由于我们从无序区拿到的记录是随机的,因此我们把这个记录放到前边的有序区,我们需要找到一个正确的位置然后插入进去。 插入指的是将记录插入到有序区中合适的位置
  2. 选择排序:每趟排序都从待排序的记录中选出关键字最小的记录,顺序放到已排好序的记录序列的最后,直到全部排序完为止。 这个选择指的是我们每一次从无序区从找到一个适合的,因为是适合的,所以当我们将其放到前边的时候就直接放置,选择指的是从无序区找到一个合适的记录
  3. 交换排序:这个就不想前两种分为无序区和有序区了,原理是 待排序记录的关键字两两比较,如果发生逆序,则交换

排序算法的稳定性

如下图:
在这里插入图片描述

  1. 稳定:插入排序,冒泡排序,归并排序,基数排序。
  2. 不稳定:希尔排序,快速排序,直接选择排序,堆排序。

下面将解释为什么稳定为什么不稳定!
何为稳定性

排序算法的稳定性,简单地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同,在简单形式化一下,如果A1 = A2,序列中A1在A2位置前,排序后A1还是要在A2位置前,那么这个算法就算稳定。

稳定的解释

  1. 插入排序:我们从原理出发,因为我们总是从后边无序的区间里边得到记录,假设前边有序区有一个大小与其相同,那么我们在将这个记录插入到有序区间的时候,我们比较的思路是如果这个记录比有序区间的元素小则继续往前比较,那么当比较到相同元素时,因不成立,则直接插入到相同元素后边,因此相对顺序是不变的。所以稳定。
  2. 冒泡排序:冒泡排序就是把小的元素往前调或者把大的元素往后沉。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,没必要再进行交换。如果两个相等的元素没有相邻,那么通过前面的两两交换这两个元素也会相邻,仍然不需要交换。所以相同元素的前后顺序并没有改变。
  3. 归并排序: 归并排序使得了递归的思想,把序列递归的分割成小序列,然后合并排好序的子序列。分割肯定不会影响稳定性的,所以我们重点关注合并的时候做了什么。在合并的时候我们是用到了一个辅助数组,通过前部分与后部分进行比较然后放到辅助数组中,那么是如何比较的呢?一般我们采用的是如果前边元素小于等于后边元素那么就将前边元素先放到辅助数组中,即大小如果相同的,在前边的在合并后仍然在前边。因此其是稳定的。
  4. 基数排序基数排序因为在是把低位按顺序映射到一个临时序列中去,是依次序映射,没有涉及到数据位置的变动.然后再按高位顺序映射.所以相同元素也是按次序映射过去.所以是稳定的。

不稳定的解释

  1. 选择排序:简单选择排序由于选出最小值后需要交换位置,位置一变就会变得不稳定.例如8 3 8 1.当从左往右遍历找最小值时,找到了1,这就需要把8跟1交换.这样两个相等元素8的位置就变了.
  2. 堆排序:堆排序的话,也会存在跟上面一样的交换最大值的位置会导致不稳定.例如有大堆 8 8 6 5 2.先选出第一个最大值8,放最末尾.此时就不稳定了.因为第二个8就跑它前面去了.
  3. 快速排序: 是不稳定的.举例8 5 6 6 .以8为基准,第一趟交换后最后一个6跑到第一位,8到最后.第二趟交换.这个6跑到5的位置.变成有序的了.两个6位置变了,所以是不稳定的.
  4. 希尔排序:希尔排序是按照不同增量对元素进行插入排序,当刚开始元素很无序的时候,增量最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,增量很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比O(n^2)好一些。由于多次插入排序,虽然一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。

排序中的常考点

以下因为篇幅原因和时间原因,大部分说明解题的技巧。

  1. 问题:不可能是快速排序第2趟排序结果的是?:我们先把题目中所问序列的排序完成后的结果写出来,如果是第二趟的话,那么正确位置的个数必须是大于等于2的,因此如果小于2的话既是问题答案。因为,每经过一趟快排,轴点元素都必然就位。也就是说,一趟下来至少有1个元素在其最终位置。
  2. 常用的几个排序算法的特征:对于冒泡排序和选择排序,每一趟都能确定一个元素的最终位置2路归并排序,第一趟排序结束都可以得到若干个有序子序列。 插入排序在每趟排序后能确定前面的若干元素是有序的。
  3. 对顺序表进行快速排序,关于递归次数的问题:递归次数,取决于递归树,而递归树取决于轴枢的选择。树越平衡,递归次数越少。而对分区的长短处理顺序,影响的是递归时对栈的使用内存,而不是递归次数。因此,递归次数与初始数据的排列次序有关,递归次数与初始数据的排列次序无关。
  4. 每一趟排序结束都至少能够确定一个元素最终位置的方法是:简单选择排序 ,简单选择排序 ,堆排序 ,和 快速排序。
  5. 对同一待排序序列分别进行折半插入排序和直接插入排序,两者之间可能的不同之处是:元素之间的比较次数。原因:折半插入排序,是对插入排序算法的一种改进,由于排序算法过程中,就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。 所以,很明显比较的次数减少了 。
  6. 对给定的关键字序列110, 119, 007, 911, 114, 120, 122 进行基数排序, 则第 2 趟分配收集后得到的关键字序列是( )
  • 007, 110, 119, 114, 911, 120, 122
  • 007, 110, 119, 114, 911, 122, 120
  • 007, 110, 911, 114, 119, 120, 122
  • 110, 120, 911, 122, 114, 007, 119
    解析:C
    基数排序是通过“分配”和“收集”过程来实现排序。

1) 首先根据个位数值(只看个位)来排序:

110 120 911 122 114 007 119

2) 再看十位(只看十位数值大小)来排序:

007 110 911 114 119 120 122

3) 最后看百位:

007 110 114 119 120 122,所以 第 2 趟分配收集后得到的关键字序列是 { 007 110 911 114 119 120 122 }。

  1. 用希尔排序方法对一个数据序列进行排序时,若第1趟排序结果为9,1,4,13,7,8,20,23,15,则该趟排序采用的增量(间隔)可能是:解析:
    首先,第二个元素为1,是整个序列中的最小元素,所以可知该希尔排序为从小到大排序。然后考虑增量问题,若增量为2,第1+2个元素4明显比第1个元素9要大,A排除;若增量为3,第i、i+3、i+6个元素都为有序序列(i=1,2,3),符合希尔排序的定义;若增量为4,第1个元素9比第1+4个元素7要大,C排除;若增量为5,第1个元素9比第1+5个元素8要大,D排除,选B。
  2. 移动次数和关键字顺序无关的有:顺口溜:一堆(堆排序)海龟(归并排序)选(选择排序)基(基数排序)友。
  3. **已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是() 。**解析:
    将堆画成完全二叉树的形式,堆删除堆顶元素后,是将二叉树最后的叶子节点12放到堆顶,然后将12与其子节点15和10相比较,当15>12时,堆顶12不动,将12与10判断,12>10,不符合小根堆,所以将10和12对调,然后还要将12与其子节点16比较。 所以总共比较3次 疑问:为什么将最后的叶子节点和删除的根节点换
    答:
    移除根节点后,树就不再是完整的了,数组(这里考虑的是用数组实现堆)中就会有一个空的数据单元,可以把数组中所有的数据都向前移动一个单元,但是这种方法比较慢,所以考虑用最后一个节点填补空的单元,再向下筛选.
  4. 对10TB的数据文件进行排序,应使用的方法是:归并排序.原因:外部排序指待排序文件较大,内存一次性放不下,需存放在外部介质中。外部排序通常采用归并排序法。
  5. 小根堆的建立:小根堆的建立
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落春只在无意间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值