您的位置:首页 > 其它

《算法导论》系列课后思考题之-第二章《算法入门》

2011-11-10 16:52 260 查看
2-1 在合并排序中对小数组采用插入排序

由题意可知,程序的设计如下所示:

#include "stdafx.h"
#include <string.h>
#include <iostream>
using namespace std;

void Merge(int* arr, int head, int mid, int tail);
void InsertSort(int* arr, int k, int head, int tail) // 进行k个大小的数组插入排序
{
if (head + k - 1< tail) {
int mid = (head + tail)	/ 2;
InsertSort(arr, k, head, mid);
InsertSort(arr, k, mid + 1, tail);
Merge(arr, head, mid, tail);
}
else {
int key, i, j;
for (i =  head + 1; i <= tail; i++)
{
key = arr[i];
for (j = i - 1; j >= head && key < arr[j]; j--)
arr[j+1] = arr[j];
arr[j + 1] = key;

}
}
}

void Merge(int* arr, int head, int mid, int tail)
{
int n1 = mid - head + 1;
int n2 = tail - mid;
int* arr1 = new int[n1 + 1];
int* arr2 = new int[n2 + 1];
int i, j, k;

for(int i = 0; i < n1; i++)
arr1[i] = arr[head + i];
for(int i = 0; i < n2; i++)
arr2[i] = arr[mid + 1 + i];

arr1[n1] = 1000000000;	//哨兵
arr2[n2] = arr1[n1];

i = j = 0;
k = head;
while(k <= tail) {
if (arr1[i] <= arr2[j]) {
arr[k] = arr1[i];
i++;
k++;
}
else {
arr[k] = arr2[j];
j++;
k++;
}
}
delete[] arr1;
delete[] arr2;
}


测试后通过。关键代码是InsertSort函数中的“if (head + k - 1< tail)”,这句决定了可以进行多大范围的插入排序。

那么a).每次插入排序最坏情况下显然为O(n^2),共进行了n/k次插入排序,显然是在O(nk)时间内完成;

b).由于有n/k个子列表,那么共有log(n/k)+1层。每一层的代价是O(n),因此总共的时间复杂度为O(nlog(n/k));

c).标准的合并算法时间复杂度为O(nlog(n)),要使修改后的算法具有与标准合并排序算法一样的渐进运行时间,k的最大渐进值是logn,原来时间复杂度为O(nk + nlog(n/k)),现在变为了O(nlogn + nlog(n/logn)),忽略nlog(n/logn)的影响,这样即为O(nlogn);

d).在满足插入排序比合并排序更快的前提下,k取最大值。

2-2 冒泡排序算法的正确性

a)A'中的值全部来自A;

b)起始:A数组的初始状态为A[1]、A[2]、A[3]、……A
,对于for的第一个元素,即j=n,A
来说,其元素本身对于只有该元素的数组来说已排好序;

保持:当i=k时,对于j = k-1个元素来说,已经是排好的最小的k-1个元素,当j=n时,A
需要与A[n-1]比较,当j为k+1时,A[k+1]是A
~A[k]中次小或者最小的数,交换后A[k]变为第k个最小的元素,此时也已经排好序;

终止:当i=n-1时,我们已经排好n-2个最小的元素,j=n时,A[j]只需和A[j-1]比较一次即排好序。

c)起始:A'数组的初始状态为空

保持:若i=k,则A'[1]、A'[2]、……A'[i-1]已经排好序,由b)可知,排序后A'[1]、A'[2]、……、A‘[i]为排好序的;

终止:i=n时,所有数都已经排好序了。

d)O(n^2),最坏情况下也是Σ(n-i)

2-3 霍纳规则的正确性

a)霍纳规则的代码渐进运行时间是n?;

b)朴素多项式?伪代码是:

void Ploynomial()
{
int t;
sum = a[0];
for (i = 1; i < n; i++)
{
sum += a[i]*x;
x *= x;
}
}

那么运行时间应该是2n吧?与上述相比是2倍时间长度;

c)起始:因为y初始状态为0,i为n,因此在第一轮迭代的开始时有

,这时y正好为0;

保持:利用数学归纳法,若i = k+1时,迭代前为

,此时成立;那么对于下一轮迭代,i=k,迭代前有


,此时


,必然成立。

终止:终止时i=-1,那么此时

,与原多项式相同,证明成立

2-4 逆序对

a)列出5个逆序对:(2,1),(3,1),(8,6),(8,1),(6,1)。so easy~~


b)怎样的数组含有最多的逆序对?它们包含多少个逆序对?

从大到小排列,为(n,n-1,n-2,...,1),共(n-1)n/2个。

c)相等。插入排序的思想是,若已经排好序的数为n,待排序的数为第n+1个,需与第n个数比较,若逆序,交换后,再与第n-1个数比较;若不逆序,停止比较。否则直到第1个数与当前数比较完毕。比较的次数和逆序的次数是一样多的。

d) 算法如下所示:

#include "stdafx.h"

int count = 0;

void Merge(int *arr, int head, int mid, int tail)
{
int n1 = mid - head + 1;
int n2 = tail - mid;

int* arr1 = new int[n1 + 1];
int* arr2 = new int[n2 + 1];

int i, j, k;
for (i = 0; i < n1; i++)
arr1[i] = arr[head + i];
for (i = 0; i < n2; i++)
arr2[i] = arr[mid + i + 1];

arr1[n1] = arr2[n2] = 1000000000;

i = j = 0;
k = head;
while (k <= tail) {
if (arr1[i] < arr2[j]) {
arr[k] = arr1[i];
i++;
}
else {
arr[k] = arr2[j];
count++;
j++;
}
k++;
}
delete [] arr1;
delete [] arr2;
}

void MergeSort(int* arr, int head, int tail)
{
if (head < tail)
{
int mid = (head + tail) / 2;
MergeSort(arr, head, mid);
MergeSort(arr, mid + 1, tail);
Merge(arr, head, mid, tail);
}

}

int _tmain(int argc, _TCHAR* argv[])
{
int a[5] = {2,3,8,6,1};
MergeSort(a, 0, 4);
printf("%d", count);
return 0;
}


部分参考/article/8539405.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: