算法導論練習題

一份詳盡的答案:https://walkccc.github.io/CLRS/

1.1-1

現實生活中需要計算凸殼的例子

參考答案
凸殼:計算點集的直徑

1.1-2

除速度外,在真實環境中還可能使用哪些有關效率的量度

參考答案
內存效率和編碼效率

1.1-5

提供一個現實中的問題,只有最佳解才行;提供一個問題,近似最佳解的一個解也足夠好

參考答案
求兩個數的最大公約數
求微分方程的解

1.2-2

假設比較插入排序與歸併排序在相同機器上的實現。對規模爲n的輸入,插入排序運行8n28n^2步,而歸併排序運行64nlgn64nlgn步,則對於哪些n值,插入排序優於歸併排序

n<8lgn1<n<44n<8lgn \rightarrow 1<n<44
在這裏插入圖片描述

1.2-3

nn的最小值爲何值時,運行時間爲100n2100n^2的一個算法在相同機器上快於運行時間爲2n2^n的另一個算法

100n2<2nn>14100n^2<2^n \rightarrow n>14
手動計算交點在14.3~14.4之間
在這裏插入圖片描述
存疑
1-1 求解問題的算法需要f(n)f(n)毫秒,對下表中的每個函數f(n)f(n)和時間tt,確定可以在時間tt內求解的問題的最大規模nn

1秒鐘 1000ms1000ms 1分鐘 1小時 1天 1月 1年 1世紀
lgnlgn 1.0715e+3011.0715e+301
n\sqrt n 1.0e+61.0e+6
nn 10001000
nlgnnlgn
n2n^2
n3n^3
2n2^n
n!n!

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個數的序列A=<a1,a2,...,an>A=<a_1,a_2,...,a_n>和一個值vv
輸出:下標ii使得v=A[i]v=A[i]或者當vv不在AA中出現時,vv爲特殊值NIL
寫出線性查找的僞代碼,他掃描整個序列來查找v。使用一個循環不變式來證明正確性,確保循環不變式滿足三條必要的性質

參考答案

LINEAR-SEARCH(A, v)
    for i = 1 to A.length
       if A[i] == v
            return i
    return NIL

循環不等式:在for循環開始時,子序列A[1,...,i1]A[1,...,i - 1]都包含與vv不同的元素
Proof:

  1. 初始化:子數組爲空.
  2. 保持:在每一步都有A[1,...,i1]A[1,...,i - 1]不包含vv,接下來比較A[i]A[i],如果相同則返回ii,否則繼續下一步並且有A[1,...,i]A[1,...,i ]不包含vv,因此此步驟保留不變式.
  3. 終止:當i>A.lengthi > A.length時循環終止,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

選擇排序,僞代碼,循環不變式,用Θ\Theta表示最好情況與最壞情況的運行時間。

我的答案

  1. 僞代碼:
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])
  1. 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;
}
  1. 排序過程
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
  1. 循環不變式
    子序列A[1,...,i1]A[1,...,i - 1]AA中排序後的i1i-1個最小的數

  2. 運行時間 Θ(n2)\Theta(n^2)

2.2-3

線性查找問題,查找的元素等可能爲數組中的任意元素,平均需要檢查多少元素,最壞情況如何,平均與最壞的運行時間並證明

平均需要查找一半的元素,平均概率
平均與最壞的時間均爲Θ(n)\Theta(n)

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

插入排序遞歸過程,遞歸式
T(n)={Θ(1)if n=1T(n1)+Θ(n)if n>1 T(n)= \begin{cases} \Theta(1)& \text{if } n=1 \\ T(n-1)+\Theta(n) & \text{if } n> 1 \end{cases}
時間複雜度爲Θ(n2)\Theta(n^2)

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)

T(n)={Θ(1)if n=1T(n/2)+Θ(1)if n>1 T(n)= \begin{cases} \Theta(1)& \text{if } n=1 \\ T(n/2)+\Theta(1) & \text{if } n> 1 \end{cases}
時間複雜度:Θ(lgn)\Theta(lgn)

2.3-6

是否可以將插入排序中while循環中倒序查找的部分替換成二分查找,從而將時間複雜度變成Θ(nlgn)\Theta(nlgn)
不可
while循環中除了進行查找之外,還有數組元素移動的操作,換成二分查找,在查找部分的複雜度爲Θ(lgj)\Theta(lgj),而移動的複雜度仍爲Θ(j)\Theta(j)

2.3-7

設計一個運行時間爲Θ(nlgn)\Theta(nlgn)的算法,給定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

霍納規則求解多項式Pn(x)=k=0nakxk=anxnanxnaxaP_n(x)= \sum_{k=0}^{n}a_kx^k=a_nx_n +a_{n-1}x_{n-1}+…+a_1x+a_0

  1. 樸素算法:
    對每一項分別求值,並把每一項求的值累加起來,需要進行n(n1)1n(n1)/2n+(n-1)+…+1=n(n+1)/2次乘法運算和nn次加法運算。

僞代碼:

NAIVE-HORNER()
    y = 0
    for k = 0 to n
        temp = 1
        for i = 1 to k
            temp = temp * x
        y = y + a[k] * temp

Θ(n2)\Theta(n^2)

  1. 霍納法則
    Pn(x)=k=0nak=a0+x(a1+x(a2+...+x(an1+xan)...))P_n(x)= \sum_{k=0}^{n}a^k=a_0+x(a_1+x(a_2+...+x(a_{n-1}+xa_n)...))
Horner(a[0...n], x)  
y=0
for i = n downto 0 
	y=a[i]+x*y
return y;

Θ(n)\Theta(n)

循環不變式:for循環迭代開始時,有
y=k=0n(i+1)ak+i+1xky=\sum _{k=0}^{n-(i+1)}a_{k+i+1}x^k

  • 初始:y=0y=0
  • 保持:在第ii次迭代結束之後,有
    y=ai+xk=0n(i+1)ak+i+1xk=aix0+k=0ni1ak+i+1xk+1=aix0+k=1niak+ixk=k=0niak+ixk \begin{aligned} y&=a_i+x\sum _{k=0}^{n-(i+1)}a_{k+i+1}x^k\\ &=a_ix^0+\sum _{k=0}^{n-i-1}a_{k+i+1}x^{k+1}\\ &=a_ix^0+\sum _{k=1}^{n-i}a_{k+i}x^k\\ &=\sum _{k=0}^{n-i}a_{k+i}x^k\\ \end{aligned}
  • 終止:i=1i=-1y=k=0n(i+1)ak+i+1xk=k=0nakxky=\sum _{k=0}^{n-(i+1)}a_{k+i+1}x^k=\sum_{k=0}^{n}a_kx^k

3.1-7

在這裏插入圖片描述
存疑

爲什麼要取c1=c2c_1=c_2,當c1>c2c_1>c2時,交集不爲空

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)

https://blog.csdn.net/zj0395/article/details/76284342

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章