目录
[1.摆渡的士兵 n个士兵组成的小分队必须越过一条又深又宽又没有桥的河。他们注意到在岸旁有两个12岁大的小男孩在玩划艇。然而船非常小,只能容纳两个男孩或者一名士兵。怎样才能让士兵渡过河并且留下两个男孩共同操纵这条船?这条船要在岸与岸之间横渡多少次?](#1.摆渡的士兵 n个士兵组成的小分队必须越过一条又深又宽又没有桥的河。他们注意到在岸旁有两个12岁大的小男孩在玩划艇。然而船非常小,只能容纳两个男孩或者一名士兵。怎样才能让士兵渡过河并且留下两个男孩共同操纵这条船?这条船要在岸与岸之间横渡多少次?)
[2.交替放置的玻璃环 有2n个玻璃杯挨个排成一行,前n个装满苏打水,其余n个杯子为空。交换杯子的位置,使之按照满一空一满一空的模式排列,而且杯子移动的次数要最少([Gar78], p.7)。](#2.交替放置的玻璃环 有2n个玻璃杯挨个排成一行,前n个装满苏打水,其余n个杯子为空。交换杯子的位置,使之按照满一空一满一空的模式排列,而且杯子移动的次数要最少([Gar78], p.7)。)
[3.标记单元格 为下列任务设计一个算法。n为任意偶数,在一张无限大的绘图格子纸上标记n个单元格,使得每个被标记的单元格有奇数个相邻的标记单元格。相邻是指两个单元格在水平方向或垂直方向上相邻,但非对角方向上相邻。被标记的单元格必须形成连续域,也就是说区域中任意一对标记单元格之间有一条经过一系列相邻标记单元格的路径。([Kor05])](#3.标记单元格 为下列任务设计一个算法。n为任意偶数,在一张无限大的绘图格子纸上标记n个单元格,使得每个被标记的单元格有奇数个相邻的标记单元格。相邻是指两个单元格在水平方向或垂直方向上相邻,但非对角方向上相邻。被标记的单元格必须形成连续域,也就是说区域中任意一对标记单元格之间有一条经过一系列相邻标记单元格的路径。([Kor05]))
4.设计一个减一算法,生成一个n元素集合的幂集(一个集合S的幂集是S的所有子集的集合,包括空集和S本身)。
[6.队伍排序 给定一个完全循环赛的比赛结果,其中 n个队伍两两比赛一次。每场比赛以一方胜出或者平局结束。设计一个算法,把n个队伍排序,序列中每个队伍都不曾输给紧随其后的那个队。说明该算法的时间效率类型。](#6.队伍排序 给定一个完全循环赛的比赛结果,其中 n个队伍两两比赛一次。每场比赛以一方胜出或者平局结束。设计一个算法,把n个队伍排序,序列中每个队伍都不曾输给紧随其后的那个队。说明该算法的时间效率类型。)
[7.应用插入排序将序列E, X, A, M, P, L, E按照字母顺序排序。](#7.应用插入排序将序列E, X, A, M, P, L, E按照字母顺序排序。)
a.对于插入排序来说,为了避免在内部循环的每次迭代时判断边界条件j≥0,我们应该在待排序数组的第一个元素前放一个什么样的限位器?
[9.能不能实现一个对链表排序的插入排序算法?它是不是和数组版本都一样有着 编辑 效率呢?](#9.能不能实现一个对链表排序的插入排序算法?它是不是和数组版本都一样有着 编辑 效率呢?)
[11.设A[0..n-1]是n个可排序元素的数组(简单起见,假设所有元素互不相同)。对于(A[i],A[j])这样的对, 如果i A[j], 我们将其称为一个倒置。](#11.设A[0..n-1]是n个可排序元素的数组(简单起见,假设所有元素互不相同)。对于(A[i],A[j])这样的对, 如果i A[j], 我们将其称为一个倒置。)
a.规模为n的数组在什么情况下具有最大数量的倒置?倒置数为多少?如果问的是最小数量的倒置呢?
[12.希尔排序(由D. L.希尔发明)是一种重要的排序算法,它对一个给定序列的若干步长子序列分别应用插入排序。对序列的每一遍操作,都根据一些事先定义好的递减的步长队列编辑 来构造所要求的子序列,这个步长队列必须以1作为结尾。(该算法对任意步长队列都有效,但有些步长队列的效率要比其他的高。例如,对于希尔排序来说,步长队列1,4,13,40,121的效率是最高的。当然,使用的时候要反过来。)](#12.希尔排序(由D. L.希尔发明)是一种重要的排序算法,它对一个给定序列的若干步长子序列分别应用插入排序。对序列的每一遍操作,都根据一些事先定义好的递减的步长队列编辑 来构造所要求的子序列,这个步长队列必须以1作为结尾。(该算法对任意步长队列都有效,但有些步长队列的效率要比其他的高。例如,对于希尔排序来说,步长队列1,4,13,40,121的效率是最高的。当然,使用的时候要反过来。))
S,H,E,L,L,S,O,R,T,I,S,U,S,E,F,U,L
c.任意选择一种语言实现希尔排序、直接插入排序、选择排序以及冒泡排序,然后对于序列大小为10ⁿ,n=2,3,4,5,6的随机序列、升序序列和降序序列分别比较它们的性能。
1.摆渡的士兵 n个士兵组成的小分队必须越过一条又深又宽又没有桥的河。他们注意到在岸旁有两个12岁大的小男孩在玩划艇。然而船非常小,只能容纳两个男孩或者一名士兵。怎样才能让士兵渡过河并且留下两个男孩共同操纵这条船?这条船要在岸与岸之间横渡多少次?
首先,两个男孩乘船到对岸,然后其中一个人把船划回来。接着,一名士兵乘船到对岸并留在那里,而另一个男孩则把船划回来。这四次渡船将问题中大小为n,因此最终要横渡4n次
2.交替放置的玻璃环 有2n个玻璃杯挨个排成一行,前n个装满苏打水,其余n个杯子为空。交换杯子的位置,使之按照满一空一满一空的模式排列,而且杯子移动的次数要最少([Gar78], p.7)。
- 只交换 ** 偶数位置(第 2、4、6... 位)** 的满杯与后半部分的空杯。
- 每次交换能同时固定两个杯子的位置,因此交换次数最少。最少交换次数为 ⌊(n−1)/2⌋。
3.标记单元格 为下列任务设计一个算法。n为任意偶数,在一张无限大的绘图格子纸上标记n个单元格,使得每个被标记的单元格有奇数个相邻的标记单元格。相邻是指两个单元格在水平方向或垂直方向上相邻,但非对角方向上相邻。被标记的单元格必须形成连续域,也就是说区域中任意一对标记单元格之间有一条经过一系列相邻标记单元格的路径。([Kor05])
把 n 个单元格连成一条 "阶梯形" 或 "之字形",每个单元格只和前一个相邻,这样每个单元格都只有 1 个邻居(奇数),并且整体连通。
4.设计一个减一算法,生成一个n元素集合的幂集(一个集合S的幂集是S的所有子集的集合,包括空集和S本身)。
算法:PowerSet(S, n)
输入:n 个元素的集合 S
输出:S 的幂集
1. if n = 0 then
2. return { 空集 }
3. P = PowerSet(S, n-1) // 减一:求前 n-1 个元素的幂集
4. elem = S 中第 n 个元素
5. Q = 所有 P 中的子集加入 elem 得到的新集合
6. return P ∪ Q
5.对于以邻接矩阵定义的图,用如下算法检测其连通性。
算法 Connected(A[0..n-1,0..n-1])
//输入:无向图G的邻接矩阵A[0..n-1,0..n-1])
//输出:如果G是连通的,输出为 1(true),否则输出为0(false)
if n=1 return 1//单一顶点的图显然是连通的
else
if not Connected(A[0..n-2,0..n-2]) return 0
else for j←0 to n-2 do
if A[n-1,j] return 1
return 0
该算法是否对每个n>0个顶点的无向图能够正确运行?如果回答是,请说明最坏情况下的算法效率类型;如果回答否,请说明为什么。
算法假设:只要前 n-1 连通 + 新点连到其中任意一个,整个图就连通。这个假设是对的,但算法漏掉了一种关键情况:前 n-1 个顶点本身是【不连通的】,但新顶点把它们连起来了!
6.队伍排序 给定一个完全循环赛的比赛结果,其中 n个队伍两两比赛一次。每场比赛以一方胜出或者平局结束。设计一个算法,把n个队伍排序,序列中每个队伍都不曾输给紧随其后的那个队。说明该算法的时间效率类型。
本质上是插入排序。使用减一(插入)策略 :先对 n−1 个队伍排序,再将第 n 个队伍插入到序列的合法位置,使得它没有输给紧随其后的队伍。
- 基准情况:如果只有 1 个队伍,序列已经有序。
- 递归 / 减一处理 :先将前 n-1 个队伍排成满足条件的序列。
- 插入第 n 个队伍 :将第 n 个队伍插入到序列的最前面 。
- 如果它没有输给当前第一个队伍 → 放置完成。
- 如果它输给了 当前第一个队伍 → 把它放到下一个位置继续比较。(因为是完全赛,总能找到合法位置)
算法效率为O(n^2)
7.应用插入排序将序列E, X, A, M, P, L, E按照字母顺序排序。

8.
a.对于插入排序来说,为了避免在内部循环的每次迭代时判断边界条件j≥0,我们应该在待排序数组的第一个元素前放一个什么样的限位器?
放一个值为负无穷(或小于数组中所有元素的最小值)的限位器(哨兵)。
b.带限位器的版本和原版本的效率类型相同吗?
效率类型完全相同。
9.能不能实现一个对链表排序的插入排序算法?它是不是和数组版本都一样有着
效率呢?
能实现链表版本的插入排序算法。 它的效率类型和数组版本一样,都是 O (n²)。
算法:LinkedListInsertionSort(head)
// 输入:链表头指针 head
// 输出:排序后的链表头指针(升序)
如果 head = null 或者 head.next = null
返回 head // 空链表或只有一个节点,直接返回
sortedHead = head // 已排序部分初始为第一个节点
unsorted = head.next // 未排序部分从第二个节点开始
sortedHead.next = null // 断开已排序与未排序部分
// 遍历所有未排序节点
while unsorted ≠ null
current = unsorted // 取出当前待插入节点
unsorted = unsorted.next // 未排序指针后移
// 插入到已排序链表的正确位置
// 情况1:插入到已排序链表头部
if current.key < sortedHead.key
current.next = sortedHead
sortedHead = current
// 情况2:在已排序链表中找插入位置
else
temp = sortedHead
// 找合适的前驱节点
while temp.next ≠ null AND temp.next.key < current.key
temp = temp.next
// 插入
current.next = temp.next
temp.next = current
返回 sortedHead
11.设A[0..n-1]是n个可排序元素的数组(简单起见,假设所有元素互不相同)。对于(A[i],A[j])这样的对, 如果i<j且A[i]>A[j], 我们将其称为一个倒置。
a.规模为n的数组在什么情况下具有最大数量的倒置?倒置数为多少?如果问的是最小数量的倒置呢?
数组严格递减有序时最大, 数量:Cmax=n(n−1)/2.。数组严格递增有序时最小,C为0
b.为什么插入排序的平均键值比较次数符合以下公式?

-
插入排序的键比较次数 ≈ 数组中的倒置数 。
-
对随机排列 的数组:任意一对 i<j,有 50% 概率是倒置,50% 不是。
-
总共有
对,平均倒置数为:Cavg≈
≈
-
所以插入排序的平均键值比较次数 Cavg(n)≈
12.希尔排序(由D. L.希尔发明)是一种重要的排序算法,它对一个给定序列的若干步长子序列分别应用插入排序。对序列的每一遍操作,都根据一些事先定义好的递减的步长队列
来构造所要求的子序列,这个步长队列必须以1作为结尾。(该算法对任意步长队列都有效,但有些步长队列的效率要比其他的高。例如,对于希尔排序来说,步长队列1,4,13,40,121的效率是最高的。当然,使用的时候要反过来。)
a.对下列序列应用希尔排序:
S,H,E,L,L,S,O,R,T,I,S,U,S,E,F,U,L
O, H, E, E, L, S, R, T, S, I, S, F, S, L, U, L
E, H, E, F, L, L, O, I, R, S, S, S, S, T, U, L
E, E, F, H, I, L, L, L, O, R, S, S, S, S, T, U
b.希尔排序是一个稳定的排序算法吗?
希尔排序按大步长 分组插入排序,会跨远距离交换元素 ,导致相同值的元素前后位置被颠倒 ,因此不具备稳定性。
c.任意选择一种语言实现希尔排序、直接插入排序、选择排序以及冒泡排序,然后对于序列大小为10ⁿ,n=2,3,4,5,6的随机序列、升序序列和降序序列分别比较它们的性能。
#include <stdio.h>
// 希尔排序函数
void shellSort(int arr[], int n) {
int h, i, j, temp;
// 生成初始步长 h (Knuth 最优步长: h = 3*h + 1)
h = 1;
while (h < n / 3) {
h = 3 * h + 1;
}
// 步长递减到 1 为止
while (h >= 1) {
// 对每个步长分组,执行插入排序
for (i = h; i < n; i++) {
temp = arr[i]; // 待插入元素
// 分组内向前比较、移动
for (j = i; j >= h && arr[j - h] > temp; j -= h) {
arr[j] = arr[j - h];
}
arr[j] = temp; // 插入正确位置
}
h = h / 3; // 缩小步长
}
}
// 打印数组
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 主函数测试
int main() {
int arr[] = {12, 34, 54, 2, 3, 87, 45, 9};
int n = sizeof(arr) / sizeof(arr[0]);
printf("排序前:");
printArray(arr, n);
shellSort(arr, n);
printf("排序后:");
printArray(arr, n);
return 0;
}
希尔排序 > 插入排序 ≈ 选择排序 > 冒泡排序