2-1 (在归并排序中对小数组采用插入排序)
虽然归并排序的最坏情况运行时间为Θ(nlgn),而插入排序的最坏情况运行时间为Θ(n2),但是插入排序中的常量因子可能使得它在n较小时,在许多机器上实际运行得更快。因此,在归并排序中当子问题变得足够小时,采用插入排序来使递归的叶变粗是有意义的。考虑对归并排序的一种修改,
39其中使用插入排序来排序长度为k的n/k个子表,然后使用标准的合并机制来合并这些子表,这里k是一个待定的值。
a.证明:插入排序最坏情况可以在Θ(nk)时间内排序每个长度为k的n/k个子表。
b.表明在最坏情况下如何在Θ(nlg(n/k))时间内合并这些子表。
c.假定修改后的算法的最坏情况运行时间为Θ(nk+nlg(n/k)),要使修改后的算法与标准的归并排序具有相同的运行时间,作为n的一个函数,借助Θ记号,k的最大值是什么?
d.在实践中,我们应该如何选择k?
******************************************************
1)每一个大小为k的子序列最坏情况可以在O(k^2)内完成排序,则n/k个子序列可以在n/k * O(k^2) = Θ(k^2*n/k) = O(nk)时间内完成排序。
2) 该分治算法一共要分的层数为lg(n/k)次,而每一层,合并算法的时间复杂度为Θ(n),所以总个的时间复杂度为Θ(nlg(n/k))
3) Θ(n*k+n*lg(n/k)) = Θ(n*lgn)
=>Θ(k+lg(n/k)) = Θ(lgn)
所以 k < lgn , k 的最大值应该是 lgn.
4)、实践中k应该尽量选择为插入排序比合并排序快的最大的列表长度。
伪代码:
/**
*参数:数组a, 起始位置,结束位置。
**/
void mergeSort(int a[], int p, int r)
{
if (r-p <= 6) { //这里的r-p +1 < = 7, 即r-p<= 6, k值为7。
return insertSort(a, p, r);
} else {
int q = (p + r)/2;
mergeSort(a, p, q);
mergeSort(a, q+1, r);
merge(a, p, q, r);
}
}
/**插入排序**/
void insertSort(int a[], int p, int r)
{
int i, j, key;
for (j = p+1; j <= r; j++) {
key = a[j];
i = j - 1;
while (i>=p && a[i]>key) {
a[i+1] = a[i];
i--;
}
a[i+1] = key;
}
}
//下面是插入排序的变形。
void insertsort(int a[], int p, int r){
int i, j;
for (i=p; i<r; i++)
for (j=i+1; j>p && a[j-1]>a[j]; j--)
swap(a, j, j-1); //交换数组a中的j和j-1位置处的值。
}
============================================================================================================
2-2:冒泡排序算法的正确性
冒泡排序(bubblesort)算法是一种流行的排序算法,它重复地交换相邻两个反序元素。
BUBBLESORT(A)
1 for i=1 to length[A] -1
2 for j=length[A] downto i+1
3 if A[j]< A[j-1]
4 exchange A[j]←→ A[j-1]
a) 设A’表示BULLESORT(A)的输出,为了证明BUBBLESORT是正确的,需要证明它能够终止,并且有: A’[1]<=A[2]<=..<=A’[n]
其中n=length[A]。为了证明BUBBLESORT的确能实现排序的效果,还需要证明什么?
下面两个部分将证明不等式(2.3)。
b) 对第2~4行中的for循环,给出一个准确的循环不变式,并证明该循环不变式是成立的。在证明中采用本章中给出的循环不变式证明结构。
c) 利用在b)部分证明的循环不变式的终止条件,为第1~4行中的for循环给出一个循环不变式,它可以用来证明不等式(2.3)。你的证明因采用本章中给出的循环不变式的证明结构。
d) 冒泡排序算法的最坏情况运行时间是什么?比较它与插入排序的运行时间。
******************************************************
http://amazingidiot.iteye.com/blog/1127800
http://ufownl.blog.163.com/blog/static/1250122200832410828987/
http://blog.sina.com.cn/s/blog_769a9def0100tx9b.html
a). A’中的元素全部来自于A中变换后的元素。
b)、c) 、 答案看 http://www.cnblogs.com/ghj1976/archive/2013/03/06/2946078.html 最后一个例子的分析
d)、最坏情况为所有数据都是从大到小排序的,这时候时间花费为Θ(n^2)。最好情况是已经排好序的,仍然是Θ(n^2),因为仍然需要双重循环。
但是在最佳情况下,冒泡排序比插入排序略长。
============================================================================================================
2-3:霍纳规则的正确性
http://blog.csdn.net/cppgp/article/details/7161701
公式的简单推理:
a0+a1*x+a2*x^2+a3*x^3+a4*x^4+…+ak*x^k+…+an*x^n
计算机的循环计算。
1 y = 0 时间花费 1
2 for i=n down to 0 n+1
3 y = ai + x*y n
总时间花费 2n+2
这样循环计算出来的y就是上面汇总的值。
a)、Θ(n), 推理过程看上面。
b)、伪代码实现的朴素的多项式求值算法。
下面是一个取巧的算法,时间消耗是 3n, 在n >2 时 时间消耗大于 2n+2
void Ploynomial() 时间消耗 = 3n
{
int t; 1
sum = a[0]; 1
for (i = 1; i < n; i++) n
{
sum += a[i]*x; n-1
x = x*x; n-1
}
}
c)、
初始化: 有 y = 0, i = n , 这样 计算 下面公式的右边 为 0 , 所以初试化满足循环不变式。
保持:假设当第i=j满足时,考察i=j-1。
终止: 当循环结束时候,有 i= -1,
------------
由于0从0到n-(i+1),因此有:
y = Σ ak+i+1 * x^k
= ak+i+1 + ak+i+2 * x + ... + an * x^(n-(i+1))
霍纳规则代码段循环不变式证明如下:
初始:
i=n,y[n] = 0,迭代开始时,循环后有y[n] = a[n]。
保持:
对于任意 0 ≤ i ≤ n,循环后有:
y[i] = a[i] + y[i+1] * x = a[i] + (a[i+1] * x + a[i+2] * x + ... + a[n] * x^(n-(i+1))) * x
= a[i] + a[i+1] * x + a[i+2] * x^2 + ... + a[n] * x^(n-i)
终止:
i小于0时终止,此时有 y[0] = a[0] + a[1] * x + a[2] * x^2 + a[n] * x^n
证明和y = Σ a[k+i+1] * x^k的关系:
k 从0到n-(i+1),等价于 0 ≤ k ≤ n-(i+1)。因此
y = Σ a[k+i+1] * x^k
= a[i+1] + a[i+2] * x + ... + a[n-(i+1)+i+1] * x^(n-i)
= a[i+1] + a[i+2] * x + ... + a[n] * x^(n-i)
由于i+1循环之后和i循环之前的值相等,用y'[i]表示i循环之前的值,则有:
y'[i] = y[i+1]
霍纳规则循环不变式的结果表明:
y[i] = a[i] + a[i+1] * x + a[i+2] * x^2 + ... + a[n] * x^(n-i)
因此有:
y'[i] = y[i+1] = a[i+1] + a[i+2] * x + ... + a[n] * x^(n-(i+1))
令k=n-(i+1),则n=k+i+1,所以:
y'[i] = a[i+1] + a[i+2] * x + ... + a[k+i+1] * x^(k+i+1-(i+1))
= a[i+1] + a[i+2] * x + ... + a[k+i+1] * x^k
用y表示y'[i],则有:
y = a[i+1] + a[i+2] * x + ... + a[k+i+1] * x^k
= Σ a[k+i+1] * x^k
其中 k从0到n-(i+1)
证毕。
============================================================================================================
2.4 逆序对
设A[1..n]是一个包含n个不同数的数组。如果i<j且A[i]>A[j],则(i,j)就称为A中的一个逆序对(inversion)。
a)列出数组〈2,3,8,6,1〉的5个逆序。
b)如果数组的元素取自集合{1, 2, ..., n},那么,怎样的数组含有最多的逆序对?它包含多少个逆序对?
c)插入排序的运行时间与输入数组中逆序对的数量之间有怎样的关系?说明你的理由。
d)给出一个算法,它能用Θ(nlgn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的数目。(提示:修改合并排序)
a) (2,1) (3,1) (8,1) (6,1),(8,6)
b) 数组从大到小有序排列时,逆序对最多,为n(n-1)/2个。
c)
逆序对增加时,插入排序时间增加。
没有逆序对时,插入排序时间最少,为Θ(n)。
逆序对最多时,插入排序时间最多,为Θ(n^2)。
d) 归并算法, 每次移动牌,次数加1, 合计的次数就是逆序对的个数。
在归并排序合并步骤时,每移动一个右边的数,查看他左边数组还剩多少数,然后将其总数相加,就可以得到逆序对的数目了。
http://www.cnblogs.com/liao-xiao-chao/articles/2351925.html
============================================================================================================
参考资料:
http://qiemengdao.iteye.com/blog/1328678
http://www.cnblogs.com/sinoxavier/archive/2012/11/23/2785082.html