Introduction to Algorithms 算法导论 第2章 算法入门 学习笔记及习题解答

本文是《Introduction to Algorithms》第二章的学习笔记,主要探讨了算法的正确性分析、资源需求预测,以及通用处理器和随机存取RAM的实现模型。介绍了插入排序的最坏情况运行时间为O(n^2)。同时,解释了分治法在合并排序中的应用,并详细展示了冒泡排序的正确性和效率分析。此外,还讨论了霍纳规则在计算多项式中的应用。
摘要由CSDN通过智能技术生成
2.1 插入排序

插入排序解决的问题:
    输入:n个数构成的序列<a1, a2, ..., an>
    输出:排序输入序列<a1, a2, ..., an>为<a1', a2', ..., an'>,满足a1' ≤ a2' ≤ ... ≤ an'

伪码:
    INSERTION-SORT(A)
      for j <- 2 to length[A]
        do key <- A[j]
            i <- j - 1
            while i > 0 and A[i] > key
                do A[i+1] <- A[i]
                    i <- i - 1
            A[i+1] = key

C代码:(C的数组下标从0开始,而伪码中从1开始)
void insertion_sort(int *arr, size_t size)
{
    int i, j, key;

    assert(NULL != arr);

    for (j = 1; j < size; ++j) {
        key = arr[j];

        for (i = j - 1; i >= 0 && arr[i] > key; --i)
            arr[i+1] = arr[i];

        arr[i+1] = key;
    }
}

正确性分析:

    说明:
        插入算法在执行循环之前,A[1,j-1]是已排好序的,而每次循环都保持这个情况,因此当j从2到length[A]循环完成后,整个数组已排序。
    初始:
        j=2,A[1,j-1]只包含一个元素A[1],因此是已排序的;
    保持:
        对于任意j(2 ≤ j ≤ length[A]),保存A[j]到key
        内部循环总是从A[j-1], A[j-2], ..., A[1]序列中依次查找所有大于A[j]的值并依次后移。
        当循环结束时,A[i+1,j]中存放所有大于key的值,而A[1,i-1]中存放所有不大于key的值,且A[i]是空闲的。
        存放key到A[i]。
        因此每次循环后,A[1,j]是有序的。
    退出:
        j > length[A]时循环结束,此时A[1,length[A]]全部有序。

数学归纳法证明:
    当j=2时,A[1,j-1]只包含一个元素A[1],因此肯定是有序的。
    假定对于任意j=m(2 ≤ m < length[A]),循环完成后有A[1,j]有序
    则当j=m+1时:
        A[1,m]肯定有序。
        保存A[m+1]到key。
        内层循环结束时,A[1,i]子数组中所有元素不大于key,而A[i+2,m+1]子数组中所有元素大于key,A[i+1]不持有有效数据。
        保存key到A[i+1]。
        综上可知:A[1,m+1]此时是有序的。

    因此对于所有的j(2 ≤ j ≤ length[A]),在任意时刻,有A[1,j]有序。
    当循环迭代到j=length[A]算法结束时,数组A已排序。

测试程序:
    可以通过如下程序测试插入排序算法的正确性。其中辅助函数print_arr、init_arr可能在以后多次用到:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void insertion_sort(int *arr, size_t size)
{
	int key;
	int i, j;

	assert(NULL != arr);

	for (j = 1; j < size; ++j) {
		key = arr[j];
		for (i = j - 1; i >= 0 && arr[i] > key; --i)
			arr[i+1] = arr[i];
		arr[i+1] = key;
	}
}

void print_arr(const int *arr, size_t size, const char *info)
{
	int i;

	assert(NULL != arr);

	printf("%s: ", info);
	for (i = 0; i < size; ++i)
		printf("%d ", arr[i]);
	printf("\n");
}

void init_arr(int *arr, size_t size)
{
	int i;

	assert(NULL != arr);

	srand((unsigned int)time(NULL));

	for (i = 0; i < size; ++i)
		arr[i] = rand()%100;
}

int main()
{
	int arr[10];

	init_arr(arr, 10);
	print_arr(arr, 10, "before");
	insertion_sort(arr, 10);
	print_arr(arr, 10, "after");

	return 0;
}


习题:

2.1-1 以图2-2为模型,说明INSERTION-SORT在数组A=<31,41,59,26,41,58>上的执行过程。
如下:
    31,    41(j), 59,    26,    41,    58
    31(j), 41,    59(j), 26,    41,    58
    26,    31,    41,    59(j), 41,    58
    26,    31,    41,    41,    59(j), 58
    26,    31,    41,    41,    58,    59(j)
标注为x(j)的元素表示此时正在以j执行循环。

2.1-2 重写过程INSERTION-SORT,使之按非升序(而不是按非降序)排序。
只需要更改一行:
更改
    while i > 0 and A[i] > key

    while i > 0 and A[i] < key
即可。

2.1-3 考虑下面的查找问题:
    输入:一列数A=<a1,a2,…,an>和一个值V。
    输出:下标i,使得V=A[i],或者当V不再A中出现时为NIL。
写出针对这个问题的线性查找的伪代码,它顺序地扫描整个序列以查找V。利用循环不变式证明算法的正确性。确保所给出的循环不变式满足三个必要的性质。
FIND-LINEAR(A, v, i)
    i <- NIL
    j <- 1
    while j ≤ length[A] and A[j] != v
        do j <- j + 1
    if j ≤ length[A]
        then i <- NIL
int find_linear(const int *arr, size_t size, int v)
{
    int i;

    for (i = 0; i < size && arr[i] != v; ++i);

    if (i == size) i = -1;    /* -1 means NIL */

    return (int)i;
}

证明:
    初始:
        i=NIL,初始化为未查找到。

    保持:
        从j=1到length[A],依次判断,如果A[j]等于v,则结束循环。

    退出:
        循环结束时,判断j是否有效
        如果j在有效范围(小于等于length[A]),则证明查找成功,因此设置i为正确的索引。
        否则,循环因为越界结束,v没有找到,i依然为NIL。

2.1-4 有两个各存放在数组A和B中的n位二进制整数,考虑它们的相加问题。两个整数的和以二进制形式存放在具有(n+1)个元素的数组C中。请给出这个问题的形式化描述,并写出伪代码。

存储说明:最高位存在数组的第一个元素中,最低位存在数组的最后一个元素中。
    因为A/B各有n个元素而C有n+1个元素,因此对于任意i(1 ≤ i ≤ n),A[i]、B[i]和C[i+1]总在相同位(权数)。如下:

                
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值