INSERTION-SORT(A) for j=2 to A.length key = A[j] //insert A[j] into the sortes sequence A[1...j-1] i = j-1 while i>0 and A[i]>key A[i+1] = A[i] i = i-1 A[i+1] = key
javascript实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functioninsertion_sort(arr) { var key = 0; var i = 0; for(var j = 1; j < arr.length; j++) { key = arr[j]; i = j - 1; while (i >= 0 && arr[i] > key) { arr[i+1] = arr[i]; i = i - 1; } arr[i+1] = key; } return arr; }
通过调用一个辅助过程MERGE(A, p, q, r)来完成两个已排序序列的合并,其中A是一个数组,p、q和r是数组下标,满足 p<=q<r。该过程假设子数组A[p..q]和A[q+1..r]都已排好序。它合并两个子数组形成单一的已排好序的子数组并代替当前的子数组A[p..r]。
伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
MERGE(A, p, q, r) n1 = q - p + 1 n2 = r - q Let L[1..n1+1] and R[1..n2+1] be new arrays for i = 1 to n1 L[i] = A[p + i - 1] for j = 1 to n2 R[j] = A[q+j] //插入哨兵牌 L[n1 + 1] = ∞ R[n2 + 1] = ∞ i = 1 j = 1 for k = p to r if L[i] <= R[j] A[k] = L[i] i = i + 1 else A[k] = R[j] j = j + 1
1 2 3 4 5 6
MERGE-SORT(A, p, r) if p < r q = └(p + r)/2┘ MERGE-SORT(A, p, q) MERGE-SORT(A, q + 1, r) MERGE(A, p, q, r)
MERGE2(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 (L[i] <= R[j] or j > n2) A[k] = L[i] i = i + 1 elseif (L[i] > R[j] or i > n1) A[k] = R[j] j = j + 1
functiondetermineSumX(array, x) { var filtered = array.filter(function(e) { return e<x; }); var sorted = insertion_sort2(filtered); var start = 0, end = sorted.length-1; while(start < end) { if (sorted[start] + sorted[end] === x) { returntrue; } elseif (sorted[start] + sorted[end] < x) { start = start + 1; } elseif (sorted[start] + sorted[end] > x) { end = end - 1; } } returnfalse; } functionbinary_search(array, key) { var start = 0, end = array.length-1; var i = 0; while (start <= end) { i = Math.floor((start + end)/2); if (array[i] === key) { return i; } elseif (array[i] > key) { end = i - 1; } elseif (array[i] < key) { start = i + 1; } } return (start < end)? end: start; } functioninsertion_sort2(array) { for (var i = 1; i < array.length; i++) { var new_array = array.slice(0, i); var key = array[i]; var answer = binary_search(new_array, key); for(var j = i; j > answer; j--) { array[j] = array[j-1]; } array[answer] = key; } return array; }
2.1 (在归并排序中对小数组采用插入排序)虽然归并排序的最坏情况运行时间为Θ(nlgn),而插入排序的最坏运行时间为Θ(n^2),但是插入牌组中的常量因子可能使得它在n较小时,在许多机器上实际运行得更快。因此,在归并排序中,当子问题变得足够小时,采用插入排序来使递归的叶变粗是有意义的。考虑对归并排序的一种修改,其中使用插入排序来排序长度为k的n/k个子表,然后使用标准的合并机制来合并这些子表,这里k是一个待定的值。 a. 证明:插入排序最坏的情况可以在Θ(nk)时间内排序每个长度为k的n/k个子表。 b. 表明在最坏的情况下如何在Θ(nlg(n/k))时间内合并这些子表。 c. 假定修改后的算法的最坏情况运行时间为Θ(nk+nlg(n/k)),要使修改后的算法与标准的归并排序具有相同的运行时间,作为n的一个函数,借助Θ记号,k的最大值是什么? d. 在实践中,我们应该如何选择k? a. 因为插入排序的最坏运行时间为Θ(n^2),所以对于长度为k的1个子表,最坏运行时间为Θ(k^2); 又因为一共有n/k个子表,所以,最坏运行时间为(n/k)*Θ(k^2) = Θ(nk)。 b. 由结果递归树可得,最小的叶子节点为cn/k,共有k个叶子节点,所以树的高度为lg(n/k),又每层将贡献总代价cn,所以,总代价为cn(lg(n/k)+1),也就是Θ(nlog(n/k))。 c. Θ(nk + nlg(n/k)) = Θ(nlgn),=>Θ(k+lg(n/k)) = Θ(lgn),所以k<lgn,k的最大值应该是lgn。 d. 这是个实验问题,应该在k的合法范围内测试可能的k,用T-INSERTION-SORT(k)表示k个元素的插入排序时间,T-MERGE-SORT(k)表示k个元素的合并排序时间。该问题等价于测试求解T-INSERTION-SORT(k)/T-MERGE-SORT(k)比值最小的k值。
BUBBLESORT(A) 1for i = 1 to A.length - 1 2for j = A.length downto i + 1 3if A[j] < A[j-1] 4 exchange A[j] with A[j-1]
a. 假设A′表示BUBBLESORT(A)的输出。为了证明BUBBLESORT正确,我们必须证明它将终止并且有: A’[1] ≤ A’[2] ≤ … ≤ A’[n] (2.3) 其中 n=A.length。为了证明BUBBLESORT确实完成了排序我们还需要证明什么? 下面两个部分将证明不等式(2.3)。 b. 为第24行的for循环精确地说明一个循环不变式,并证明该循环不变式成立。你的证明应该使用本章中给出的循环不变式证明的结构。 c. 利用在(b)部分证明的循环不变式的终止条件,为第14行的for循环说明一个循环不变式,它可以用来证明不等式(2.3)。你的证明应采用本章中给出的循环不变式的证明结构。 d. 冒泡排序算法的最坏情况运行时间是多少?与插入排序的运行时间相比,其性能如何? a. 当n=1时,能够正确排序。 b. 对第24行的for循环,循环不变式是A[j]是子数组A[j…n]中的最小值,且子数组中的元素并未改变。约定:n=A.length。 初始化:开始时,j=n,子数组中只包含A[n],故循环不变式成立 保持:假设对于任意的一个j,使得A[j]是子数组A[j…n]中的最小值,在下一轮循环中,若A[j] < A[j-1],则A[j]和A[j-1]交换。使得A[j-1]是子数组A[j-1…n]中的最小值,循环不变式依然成立 终止:循环结束时j=i,A[j]是子数组A[j…n]中的最小值,且子数组中的元素并未改变。 c. 对于14行的for循环,循环不变式是每次循环前,A[1…i-1]中包含了整个数组中前i-1小的排好序的元素,而A[i…n]中包含剩下的元素。 初始化:第一次循环前i=1,子数组为空,循环不变式成立 保持:假设对于任意一个i,使得A[1…i-1]中包含了整个数组中前i-1小的排好序的元素,而A[i…n]中包含剩下的元素,则内层循环保证了A[i]是子数组A[i…n]中的最小元素,则A[1…i]中包含了整个数组中前i小的排好序的元素,而A[i+1…n]中包含剩下的元素。循环不变式成立 终止:循环结束时i=n+1,则A[1…n]中包含了整个数组中前n小的排好序的元素,即数组有序。 d. 冒泡排序最坏和最好运行时间均为Θ(n^2) 插入排序的最坏运行时间为Θ(n^2),但是最好运行时间为Θ(n) 排序前A所有元素已经有序时,插入排序达到最好运行时间。
2.3 (霍纳(Horner)规则的正确性)给定系数a0, a1, …, an和x的值,代码片段
1 2 3
1 y = 0 2 for i = n downto 0 3 y = ai + x·y
实现了用于求值多项式
的霍纳规则。 a. 借助Θ记号,实现霍纳规则的以上代码片段的运行时间是多少? b. 编写伪代码来实现朴素的多项式求值算法,该算法从头开始计算多项式的每个项。该算法的运行时间是多少?与霍纳规则相比,其性能如何? c. 考虑以下循环不变式: 在第2~3行for循环每次迭代的开始有 把没有项的和式解释为等于0。遵照本章中给出的循环不变式证明的结构,使用该循环不变式来证明终止时有 d. 最后证明上面给出的代码片段将正确地求由系数a0, a1, …, an刻画的多项式的值。 Answer: a. Θ(n) b. 伪代码如下:
MERGE(A, p, q, r) n1 = q - p + 1 n2 = r - q Let L[1..n1+1] and R[1..n2+1] be new arrays for i = 1 to n1 L[i] = A[p + i - 1] for j = 1 to n2 R[j] = A[q+j] //插入哨兵牌 L[n1 + 1] = ∞ R[n2 + 1] = ∞ i = 1 j = 1 for k = p to r if L[i] <= R[j] A[k] = L[i] i = i + 1 else A[k] = R[j] j = j + 1 count = count + 1