算法导论习题解答Chapter2(学习笔记)

本文详细解答了《算法导论》第二章的多个习题,包括2.1-1至2.4的题目。内容涉及升序排列的插入过程,查找算法的证明,二进制整数的运算,以及冒泡排序的时间复杂度分析。通过对这些习题的解析,深入理解排序和查找的基本概念与算法实现。

目录

Chapter2

2.1-1

2.1-2

2.1-3

2.1-4

2.2-1:

2.2-2:

2.2-3:

2.3.1:

2,3-2:

 

2.3-5:

2-4:


Chapter2

2.1-1

以升序排列为例

首先取出数组中的第二个元素,然后和这个元素的前面的元素组成的子数组从子数组中的最后一个元素开始比较,如果该元素大于待插入的元素,则将该元素向后移动一位,然后将带插入的元素与之前的元素比较,循环往复,如果待插入的元素大于比较的元素,则退出移动数组元素的操作,此时将带插入的元素插入到这个比它小的元素的后面,即之前比带插入元素大的元素的前面,此时完成了该待插入元素的插入,然后将第三个元素取出来重复上面的步骤,直到将数组最后一个元素插入即可结束。

 

2.1-2

public static void insertionSort_desc(int[] arr) {
		for(int j = 1; j<arr.length; j++) {
			int key = arr[j];//取出待插入的元素
			int i = j-1;//子数组的最后一个元素的下标(已排序好的数组)
			while(i>=0 && arr[i]<key) {//寻找插入的合适位置
				arr[i+1] = arr[i];
                i--;
			}
			arr[i+1] = key;//插入到合适位置
		}
	}

2.1-3

伪代码如下

FIND_VALUE(v, A)  //v表示要查找的数, A表示查找的数组
    for i=1 to A.length:
        if v == A[i]:
            break
    if i <= A.length:
        return i
    else:
        v = NIL
        return v

下标i指出要比较的元素的位置,每次循环开始前,包含元素A[1,...,i-1]元素的子数组中不存在和v相等的值,而子数组A[i+1,...,A.length]  中是还未比较的数,当取出最后一个元素之后,当i = A.length+1 或者当A[i]  == v 时 ,退出循环,那么退出循环后,如果 i <= A.length 那么就说明在数组中找到了和v相等的值,否则就是没有找到, 让v = NIL

证明过程如下:

初始化:当第一次循环开始之前,即i = 0, 子数组A[1,0]不存在,所以子数组中没有值和v相等

保持:如果当第i循环之前,在子数组A[1,i-1]中不存在和v相等的值,那么当循环结束后,子数组A[1,i-1]中仍然没有和v相等的值

结束:如果在数组中找到了和v相等的值,那么退出循环,此时A[i] == v, 并且肯定是第一个和v相等的值,那么A[1,i-1] 中一定没有和v相等的值。如果在数组中没有找到和v相等的值,那么当退出循环之前, i = A.length+1 那么此时在子数组A[1,A.length]中没有和v相等的值

综上,循环不变式成立

2.1-4

算法描述如下:

输入:两串数组用来表示两个二进制形式的整数,并且两个数组的长度相等,都是n

输出:一串长度是n+1的数组

伪代码如下:

ADD_BINARY(A,B) //A,B表示要加的两个长度是n的二进制数
    C = new int[n+1] ; //创建长度是n+1的数组
    carry  = 0 ;    // 表示进位
    for i=1 to A.length:
        C[i] = (A[i]+B[i]+carry)%2
        carry = (A[i] + B[i] + carry) /2
    C[i] = carry
    return C

2.2-1:

theta(n^3)

2.2-2:

伪代码如下:

//降序排列
Select-Sort(A):
    for count = 1 to A.length-1   //待交换的元素的位置   
        min = count;//用来记录每次选择的最小元素         
        for i = count+1 to A.length:                     
            if A[min] > A[i]:                           
                min = i;                        
        temp = A[i];                             
        A[i] = A[min];
        A[min] = temp;

循环不变式:count指出将要交换的元素的位置, 在外层for循环每次迭代的开始, 子数组A[1,count-1]中的元素是数组中最小的元素,并且按照升序排列, 并且在内层每次迭代之前, min所指向的元素是子数组A[count, i-1]中最小的元素

为什么是A.length-1呢?

因为当最后一次迭代之后, count=A.length此时子数组A[1, count-1]中的元素是最小的元素,而子数组A[count, A.length]中只有一个元素并且一定比前面的都大,所以已经有序了

运行时间如下:

最好情况是原数组已经有序,则为 theta(n^2)

最坏情况是原数组反序, 则为theata(n^2)

 

2.2-3:

平均情况是可能有一半的元素会被访问到, 即n/2,最坏情况是全部都访问到, 即n, 运行时间都是theta(n)

 

2.3.1:

首先因为输入规模大于1,所以先将原规模分解成两个小的规模,然后对这两个规模更小的数组进行处理。如此往复, 只要输入规模为1时,则进行合并操作, 将有序的两个数组合并成一个有序的数组

因为原数组为:A{3, 41, 52, 26, 38, 57, 9, 49}

因为输入规模大于1, 所以分解成两个数组, A1{3, 41, 52, 26},   B1{38, 57, 9, 49}             (1)

因为输入规模仍然大于一,所以继续分解, 此时先分解左边的, A2{3, 41}, C2{52, 26}       (2)

因为输入规模仍大于一,所以继续分解,此时先分解左边的, A3{3}, D3{41}

此时输入规模为1,递归开始回升, 回到(2)的位置, 然后此时左边的排序结束,A2{3, 41}, 开始处理右边的

此时输入规模大于一, 继续分解...               C3{52}, E3{26}

此时输入规模为1, 递归开始回升, 回到(2) 的位置, 此时C2{26, 52} 已经有序, 开始合并A2和 C2, 递归回升到(1)处

此时A1{3, 26, 41, 52} 已经有序, 开始处理右边的B1

此时输入规模大于1一 , 分解后,   B2{38, 57}, F2{9, 49}, (3)

此时输入规模仍然大于一, 分解, B3{38}, G3{57},

此时输入规模为1, 递归开始回升, 此时回到(3)处, B2{38, 57} 已经有序, 开始处理F2,

此时输入规模大于一, 分解, F3{9},  H3{49},

此时输入规模为1, 递归开始回升, 此时回到(3)处, 此时两个数组都已经有序了,开始回升到(1), 此时B1{9, 38, 49, 57}已经有序, 两个数组都已经有序, 所以继续回升

降格A1和B1合并后, 原数组就有序了

 

2,3-2:

java代码如下:

class MergeSortWithoutSentinel{
	private static void merge(int[] arr, int p, int q, int r) {
		int n1 = q-p;
		int n2 = r-q;
		int[] arr1 = new int[n1];
		int[] arr2 = new int[n2];
		for(int i= 0; i< arr1.length; i++) {
			arr1[i] = arr[p+i];
		}
		for(int i = 0; i< arr2.length; i++) {
			arr2[i] = arr[q+i];
		}
		
		for(int k=p, i=0, j=0; k< r; k++) {
			if(i >= n1) {
				arr[k] = arr2[j];
				j++;
			}else if(j>=n2) {
				arr[k] = arr1[i];
				i++;
			}else if(arr1[i] <= arr2[j]) {
				arr[k] = arr1[i];
				i++;
			}else {
				arr[k] = arr2[j];
				j++;
			}
		}
	}
	
	public static void mergeSort(int[] arr, int p, int r){
		if(p< r-1) {
			int q = (p+r)/2;
			mergeSort(arr, p, q);
			mergeSort(arr, q, r);
			merge(arr, p, q, r);
		}
	}
}

 

2.3-5:

伪代码如下:

harfSearch(A, low, high, key):  //A已经升序排列
    if low > high:
        return NIL;
    mid = (low+high)/2;
    if A[mid] > key:
        harfSearch(A, low, mid-1, key);
    else if A[mid] < key:
        harfSearch(A, mid+1, high, key);
    else:
        return mid;

 

 

2-4:

a: (2,1),(3,1),(8,1),(6,1),(8,6)

b:降序数组逆序对最多, 有(n-1)n/2对逆序对

c:逆序对越多, 插入排序降序的运行时间更长

d:计算给定的一组元素中的逆序对的个数:

merge(A, p, q, r):
    inversion = 0; //如果左边的比右边的大, 则算作一个逆序对, 通过这个变量记录逆序对的个数
    n1 = q-p+1;
    n2 = r-q;
    let L[n1] and R[n2] to be new arrays;
    for i=1 to n1:
        L[i] = A[p+i-1];
    for i=1 to n2:
        R[i] = A[q+i];
    
    i = 1;
    j = 1;
    for k=p to r:
        if i>n1:
            A[k] = R[j];
            j++;
        else if j>n2:
            A[k] = L[i];
            i++;
        else if L[i] > R[j]:
            A[k] = L[i];
            i++;
            inversion += 1;
        else:
            A[k] = R[j];
            j++;
        return inversion;

mergeSort(A, p, r):
    inversion = 0;
    if p < r:
        q = (p + r)/2;
        inversion += mergeSort(A, p, q);
        inversion += mergeSort(A, q+1, r);
        inversion += merge(A,p, q, r);
        
    return inversion;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值