一份详尽的答案:https://walkccc.github.io/CLRS/
目录
1.1-1
现实生活中需要计算凸壳的例子
参考答案
凸壳:计算点集的直径
1.1-2
除速度外,在真实环境中还可能使用哪些有关效率的量度
参考答案
内存效率和编码效率
1.1-5
提供一个现实中的问题,只有最佳解才行;提供一个问题,近似最佳解的一个解也足够好
参考答案
求两个数的最大公约数
求微分方程的解
1.2-2
假设比较插入排序与归并排序在相同机器上的实现。对规模为n的输入,插入排序运行 步,而归并排序运行 步,则对于哪些n值,插入排序优于归并排序
1.2-3
的最小值为何值时,运行时间为 的一个算法在相同机器上快于运行时间为 的另一个算法
手动计算交点在14.3~14.4之间
存疑
1-1 求解问题的算法需要
毫秒,对下表中的每个函数
和时间
,确定可以在时间
内求解的问题的最大规模
1秒钟 | 1分钟 | 1小时 | 1天 | 1月 | 1年 | 1世纪 | |
---|---|---|---|---|---|---|---|
2.1-2
重写插入排序,使之为非升序排序
我的答案
伪代码:
INSERTION-SORT(A)
for i=2 to A.length
key=A[i]
j=i-1
while j>0 and A[j]<key
A[j+1]=A[j]
j=j-1
A[j+1]=key
2.1-3
考虑查找问题:
输入:n个数的序列
和一个值
输出:下标
使得
或者当
不在
中出现时,
为特殊值NIL
写出线性查找的伪代码,他扫描整个序列来查找v。使用一个循环不变式来证明正确性,确保循环不变式满足三条必要的性质
参考答案
LINEAR-SEARCH(A, v)
for i = 1 to A.length
if A[i] == v
return i
return NIL
循环不等式:在for循环开始时,子序列
都包含与
不同的元素
Proof:
- 初始化:子数组为空.
- 保持:在每一步都有 不包含 ,接下来比较 ,如果相同则返回 ,否则继续下一步并且有 不包含 ,因此此步骤保留不变式.
- 终止:当 时循环终止,A中所有的元素均被检查且不包含v,因此返回NIL,算法正确.
2.1-4
考虑把两个n位二进制整数加起来的问题,这两个整数分别存储在两个n元数组A和B中,这两个整数的和应按照二进制形式存储在一个(n+1)元数组C中。请给出该问题的形式化描述,并写出伪代码。
参考答案
ADD-BINARY(A, B) //取商取余,很nice
C = new integer[A.length + 1]
carry = 0
for i = 1 to A.length
C[i] = (A[i] + B[i] + carry) % 2 // remainder
carry = (A[i] + B[i] + carry) / 2 // quotient
C[i] = carry
return C
网上版本:
binary_add(A,B,C)
flag=0
for j=1 to n do
key=A[j]+B[j]+flag
C[j]=key mod 2
if key>1
flag=1
else
flag=0
if flag=1
C[n+1]=1
这两个版本符合原题,因为英文原题有一句“least-significant digit first”,最低位在最前面
若是最高位在第一个,循环从第n位开始,且第i位相加得到第i+1位的结果,最后判断最高位C[0]是否取1
我的答案
伪代码
binary_add(A,B,C)
flag=0
for j=n to 1 do
key=A[j+1]+B[j]+flag
C[j]=key mod 2
if key>1
flag=1
else
flag=0
if flag=1
C[0]=1
C++代码
#include<stdio.h>
#include <iostream>
using namespace std;
void Binary_Add(int A[], int B[], int length)
{
int C[100];
int key = 0, flag = 0;
for (int i = length-1; i >=0; i--)
{
key = A[i] + B[i] + flag;
C[i+1] = key % 2;
if (key > 1)
flag = 1;
else
flag = 0;
}
if (flag == 1)
C[0] = 1;
for (int i = 0; i <= length; i++)
cout << C[i] << " ";
}
void main()
{
int A[] = { 1, 0, 1, 1, 0, 1 };
int B[] = { 1, 1, 0, 0, 0, 0 };
Binary_Add(A, B, 6);
cout<< endl;
}
2.2-2
选择排序,伪代码,循环不变式,用 表示最好情况与最坏情况的运行时间。
我的答案
- 伪代码:
SelectSort(A,n)
for i=1 to n-1
min=i
for j=i+1 to n
if A[j]<A[min]
min=j
swap(A[min],A[i])
- C++代码
#include<stdio.h>
#include <iostream>
using namespace std;
//假交换,只是在函数内部临时变量间的交换
//所以当函数退出,函数栈帧被释放,原本的值并没有被交换。
/*int swap(int a, int b)
{
int k = a;
a = b;
b = k;
return a, b;
}*/
//取两个数的地址,在swap方法中再用指针指向地址交换
//此时为数值交换(函数调用结束后原空间的值也得到了交换)
int swap(int*x, int*y)//主函数中把两个数的地址传过来
{
int tmp = *x;//定义中间变量 然后交换两个数
*x = *y;
*y = tmp;
return *x, *y;
}
int *SelectSort(int a[],int n)
{
for (int i = 0; i < n-1; i++)
{
int min = i;
for (int j = i+1; j < n; j++)
{
if (a[j] < a[min])
min = j;
}
swap(a[i],a[min]);
}
return a;
}
void main()
{
int a[10] = { 120, 34, 6, 54, 6, 8, 3, 555, 78 ,12 };
SelectSort(a, 10);
//输出排序后的序列
cout << "排序后的数组为:" << endl;
for (int i = 0; i < 10; i++)
cout << a[i] << " ";
cout << endl;
}
- 排序过程
120 | 34 | 6 | 54 | 6 | 8 | 3 | 555 | 78 | 12 |
---|---|---|---|---|---|---|---|---|---|
3 | 34 | 6 | 54 | 6 | 8 | 120 | 555 | 78 | 12 |
3 | 6 | 34 | 54 | 6 | 8 | 120 | 555 | 78 | 12 |
3 | 6 | 6 | 54 | 34 | 8 | 120 | 555 | 78 | 12 |
3 | 6 | 6 | 8 | 34 | 54 | 120 | 555 | 78 | 12 |
3 | 6 | 6 | 8 | 12 | 54 | 120 | 555 | 78 | 34 |
3 | 6 | 6 | 8 | 12 | 34 | 120 | 555 | 78 | 54 |
3 | 6 | 6 | 8 | 12 | 34 | 54 | 555 | 78 | 120 |
3 | 6 | 6 | 8 | 12 | 34 | 54 | 78 | 555 | 120 |
3 | 6 | 6 | 8 | 12 | 34 | 54 | 78 | 120 | 555 |
-
循环不变式
子序列 为 中排序后的 个最小的数 -
运行时间
2.2-3
线性查找问题,查找的元素等可能为数组中的任意元素,平均需要检查多少元素,最坏情况如何,平均与最坏的运行时间并证明
平均需要查找一半的元素,平均概率
平均与最坏的时间均为
2.3-2
重写merge,不使用哨兵
MERGE(A, p, q, r)
n1 = q - p + 1
n2 = r - q
let L[1..n1] and R[1..n2] be new arrays
for i = 1 to n1
L[i] = A[p + i - 1]
for j = 1 to n2
R[j] = A[q + j]
i = 1
j = 1
for k = p to r
if i > n1
A[k] = R[j]
j = j + 1
else if j > n2
A[k] = L[i]
i = i + 1
else if L[i] ≤ R[j]
A[k] = L[i]
i = i + 1
else
A[k] = R[j]
j = j + 1
2.3-4
插入排序递归过程,递归式
时间复杂度为
2.3-5
二分查找伪代码与最坏情况运行时间
参考答案
迭代算法:
ITERATIVE-BINARY-SEARCH(A, v, low, high)
while low ≤ high
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
low = mid + 1
else high = mid - 1
return NIL
递归算法:
RECURSIVE-BINARY-SEARCH(A, v, low, high)
if low > high
return NIL
mid = floor((low + high) / 2)
if v == A[mid]
return mid
else if v > A[mid]
return RECURSIVE-BINARY-SEARCH(A, v, mid + 1, high)
else return RECURSIVE-BINARY-SEARCH(A, v, low, mid - 1)
时间复杂度:
2.3-6
是否可以将插入排序中while循环中倒序查找的部分替换成二分查找,从而将时间复杂度变成
不可
while循环中除了进行查找之外,还有数组元素移动的操作,换成二分查找,在查找部分的复杂度为
,而移动的复杂度仍为
2.3-7
设计一个运行时间为 的算法,给定n个整数的集合S和另一个整数x,该算法能确定S中是否存在两个其和刚好为x的元素
Find_x(A,n,x)
Merge_sort(A,1,n)
for i=1 to n
k=Binary_search(A, x-i, 1, n)
if k!=NIL
print A[i],k
2.3
霍纳规则求解多项式
- 朴素算法:
对每一项分别求值,并把每一项求的值累加起来,需要进行 次乘法运算和 次加法运算。
伪代码:
NAIVE-HORNER()
y = 0
for k = 0 to n
temp = 1
for i = 1 to k
temp = temp * x
y = y + a[k] * temp
- 霍纳法则
Horner(a[0...n], x)
y=0
for i = n downto 0
y=a[i]+x*y
return y;
循环不变式:for循环迭代开始时,有
- 初始:
- 保持:在第
次迭代结束之后,有
- 终止:
3.1-7
存疑
为什么要取 ,当 时,交集不为空
3-1
存疑
4.1-2
暴力求解最大子数组问题伪代码
参考答案
BRUTE-FORCE-FIND-MAXIMUM-SUBARRAY(A)
n = A.length
max-sum = -∞
for l = 1 to n
sum = 0
for h = l to n
sum = sum + A[h]
if sum > max-sum
max-sum = sum
low = l
high = h
return (low, high, max-sum)
4.1-5
最大子数组问题,线性时间算法
参考答案
ITERATIVE-FIND-MAXIMUM-SUBARRAY(A)
n = A.length
max-sum = -∞
sum = -∞
for j = 1 to n
currentHigh = j
if sum > 0
sum = sum + A[j]
else
currentLow = j
sum = A[j]
if sum > max-sum
max-sum = sum
low = currentLow
high = currentHigh
return (low, high, max-sum)