一份詳盡的答案: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)