# 设我们交换元素 A [i] 和 A [i+k], 它们最初是无序的。证明去掉的逆序最少为 1 个最多为 2k-1 个
若 N=1, 则只存在一个逆序数。
若 N=K, 则 A [i] 和 A [i+k] 各存在 K-1 的逆序数,且有一个重复,即 (A [i],A [i+k]), 总共 2K-1 个。
所以去掉的逆序最少为 1 个最多为 2k-1 个。
# 下述两种对图 7 一 4 所编写的希尔排序例程的修改影响最坏情形的运行时间吗?
# a. 如果 lncrement 是偶数,则在第 2 行前从减 1。
不会,希尔排序的一个重要特性是增量之间最好不要有公因子,即增量之间互素,原始增量排序 () 减一依旧有公因子,如 1,3,9,15..., 所以运行时间不变。
# b. 如果 lncrernent 是偶数,则在第 2 行前往 lncrement 加 1。
会,新的增量排序互素,如 1,3,5,7,9...,所以运行时间较原始增量排序小。
# 不使用递归如何实现归并排序?
一开始我想到的用栈模拟递归过程,但答案说的是从头开始由长度为 1 的顺串逐渐合并,不得不说这个思路真的牛逼,最起码我没想到。
void Mergesort( ElementType A[ ], int N ) | |
{ | |
ElementType *TmpArray; | |
int SubListSize, Part1Start, Part2Start, Part2End; | |
TmpArray = malloc( sizeof( ElementType ) * N ); | |
for( SubListSize = 1; SubListSize < N; SubListSize *= 2 ) | |
{ | |
Part1Start = 0; | |
while( Part1Start + SubListSize < N - 1 ) | |
{ | |
Part2Start = Part1Start + SubListSize; | |
Part2End = min( N, Part2Start + SubListSize - 1 ); | |
Merge( A, TmpArray, Part1Start, Part2Start, Part2End ); | |
Part1Start = Part2End + 1; | |
} | |
} | |
} |
# 对于本章中快速排序的实现方法,当所有的关键都相等时它的运行时间多少?
O()
# 假设我们改变分割策略使得当找到一个与枢纽元相同的关键字 i 和 j 都不停止。当所有关键字都相等时,为了保证快速排序正常工作,需要对程序做哪些修改。
i 和 j 加一个哨兵,哨兵值等于关键字值,当 i 和 j 等于关键字值时,触发哨兵自动停止。类似想 i 停止而 j 不停止,就只对 i 加哨兵即可。
说是这么说,可我没实际写过,多少难有可信度。
# 设我们选择中间的关键字作为枢纽元,这是否使得快速排序将不可能需要二次时间?
若每次中间的关键字恰好是排序后的第一位或最后一位,那就是最坏情形 O (),每次都要比较 N 次。
# 构造 20 个元素的一个排列使得对于三数中值分割且截止为 3 的快速排序,该排列尽可能地差。
同上题只要每次中间的关键字恰好是排序后的第一位或最后一位,那这个情形就是最坏情形,比如 20, 3, 5, 7, 9, 11, 13, 15, 17, 19, 4, 10, 2, 12, 6, 14, 1, 16, 8, 18。
# 编写一个程序实现选择算法
我没理解这个题目的意思,书上不是有例程吗?
/*Place the kth smallest element in the kth position.*/ | |
/*Beacause arrays start at 0, this will be index k-1.*/ | |
void QSelect(int A[],int k,int Left,int Right) | |
{ | |
int i,j; | |
int Pivot; | |
if(Left+Cutoff<=Right) | |
{ | |
Pivot = Median3(A,Left,Right); | |
i = Left;j = Right-1; | |
for(;;) | |
{ | |
while(A[++i]<Pivot){} | |
while(A[--j]>Pivot){} | |
if(i<j) | |
Swap(&A[i],&A[j]); | |
else | |
break; | |
} | |
Swap(&A[i],&A[Right-1]);//Restore Pivot | |
if(k<=i) | |
QSelect(A,k,Left,i-1); | |
else if(k>i+1) | |
QSelect(A,k,i+1,Right); | |
} | |
else//Do an Insertion Sort on the subarray | |
InsertSort(A+Left,Right-Left+1); | |
} |
# 求解下列递推关系:。
两边乘于 N,有
让,有
两式相减,得
化简得
使用叠缩求和并忽略不必要得常数项 c
......
......
最终求得
所以。
# 设给定 N 个排过序的元素,后面跟有 个随机顺序得元素。如果 是下列情况,那么如何将全部排序?
#
先分割一定次数,后比较插入,毕竟只有一个元素。
#
使用堆排序较好。
#
使用归并排序较好。
# 得多大使得全部数据仍然能够以 O (N) 时间排序
。
假设使用归并排序。
对于,有
则对于 得最后一趟,有
# 证明:在 N 个元素排过序的表中找出一个元素 X 的任何算法都需要 次比较。
使用书中引理 7.2:具有 L 片树叶得二叉树得深度至少是
可知在 N 个元素排过序的表中找出一个元素 X 的任何算法都需要 次比较。
用定理打败定理.jpg
# 利用 Stirling 公式 给出 得精确估计。
忽略, 则
# 证明,使用桶式排序把具有范围 内的整数关键字的 N 个元素排序需要时间 O (M 十 N)。
O (N) 次元素插入,O (M) 用于检查所放桶得位置,总计 O (M 十 N)。
# 设有 N 个元素的数组只包含两个不同的关键字 true 和 false。给出一个 O (N) 得算法,重新排列这些元素使得所有的元素都排在 true 的元素的前面。你只能使用常数附加空间。
假设存在第 N+1 个元素 Maybe,该元素, 把该元素作为枢纽元进行快速排序,只需要一轮,即 O (N)。
同理,对于含有三个值 false、Maybe、true 的排序,同样假设存在第 N+1 个元素 ProbablyFalse,把该元素作为枢纽元进行快速排序,只需要两轮,第一轮,第二轮, 即 O (2N)。
# 给出一个算法用 7 次比较将 5 个元素排序
假设实际结果是。
首先比较、, 得到, 再比较、 得到, 一共两次。
让 与、 得到, 一共一次。
让 与、、 得 一共三次。
最后让 与、、、 得到,一共一次,总和一共七次,这算是比较理想得情况了。
# 写出一个有效的希尔排序算法并比较当使用下列增量序列时的性能
# a. 希尔的原始序列
// Incremental sort is N/2 | |
void ShellSort(int A[],int N) | |
{ | |
int tmp; | |
int i,j; | |
for(int Increment=N/2;Increment> 0; Increment /=2) | |
{ | |
for(i = Increment;i<N;i++) | |
{ | |
tmp = A[i]; | |
for(j=i;j>Increment;j-=Increment) | |
{ | |
if(tmp<A[j-Increment]) | |
A[j] = A[j-Increment]; | |
else | |
break; | |
A[j] = tmp; | |
} | |
} | |
} | |
} |
# b.Hibbard 的增量
#define Increment_len (10) | |
// generate Hibbard incremental sequence and fixed quantity is 10 | |
static int* Hibbard() | |
{ | |
int* H = malloc(Increment_len*sizeof(int)); | |
if(H!= NULL) | |
{ | |
H[0] = 1; | |
for(int i=1; i<Increment_len; i++) | |
H[i] = 2*H[i-1]+1; | |
} | |
else | |
return NULL; | |
return H; | |
} | |
// Use Hibbard incremental sequence to sort | |
void ShellSort_Hibbard(int A[],int N) | |
{ | |
int tmp; | |
int i,j; | |
int* Increment = Hibbard(); | |
for(int k=9;k>=0;k--) | |
{ | |
for(i = Increment[k];i<N;i++) | |
{ | |
tmp = A[i]; | |
for(j = i;j>Increment[k];j-=Increment[k]) | |
{ | |
if(tmp<A[j-Increment[k]]) | |
A[j] = A[j-Increment[k]]; | |
else | |
break; | |
A[j] = tmp; | |
} | |
} | |
} | |
free(Increment); | |
} |
# c.Knuth 的增量:
// generate Knuth incremental sequence and fixed quantity is 10 | |
static int* Knuth() | |
{ | |
int* H = malloc(Increment_len*sizeof(int)); | |
if(H!= NULL) | |
{ | |
H[0] = 1; | |
for(int i=1; i<Increment_len; i++) | |
H[i] = 3*H[i-1] + 1; | |
} | |
else | |
return NULL; | |
return H; | |
} | |
// Use Knuth incremental sequence to sort | |
void ShellSort_Knuth(int A[],int N) | |
{ | |
int tmp; | |
int i,j; | |
int* Increment = Knuth(); | |
for(int k=9;k>=0;k--) | |
{ | |
for(i = Increment[k];i<N;i++) | |
{ | |
tmp = A[i]; | |
for(j = i;j>Increment[k];j-=Increment[k]) | |
{ | |
if(tmp<A[j-Increment[k]]) | |
A[j] = A[j-Increment[k]]; | |
else | |
break; | |
A[j] = tmp; | |
} | |
} | |
} | |
free(Increment); | |
} |
# d.Gonnet 的增量:, 而,(若 则)
// generate Gonnet incremental sequence and fixed quantity is 10 | |
static int* Gonnet(N) | |
{ | |
int* H = malloc(Increment_len*sizeof(int)); | |
if(H!= NULL) | |
{ | |
H[9] = N/2.2; | |
for(int i=8; i>=0; i--) | |
H[i] = H[9]/2.2; | |
} | |
else | |
return NULL; | |
return H; | |
} | |
// Use Gonnet incremental sequence to sort | |
void ShellSort_Gonnet(int A[],int N) | |
{ | |
int tmp; | |
int i,j; | |
int* Increment = Gonnet(N); | |
for(int k=9;k>=0;k--) | |
{ | |
for(i = Increment[k];i<N;i++) | |
{ | |
tmp = A[i]; | |
for(j = i;j>Increment[k];j-=Increment[k]) | |
{ | |
if(tmp<A[j-Increment[k]]) | |
A[j] = A[j-Increment[k]]; | |
else | |
break; | |
A[j] = tmp; | |
} | |
} | |
} | |
free(Increment); | |
} |
# e.Sedgewick 的增量
// generate Sedgewick incremental sequence and fixed quantity is 10 | |
// Sedgewick incremental sequence is hi=max(9∗4^j−9∗2^j+1,4^j−3∗2^j+1) | |
static int* Sedgewick() | |
{ | |
int* H = malloc(Increment_len*sizeof(int)); | |
if(H!= NULL) | |
{ | |
int a,b; | |
H[0] = 1; | |
for(int i=1;i<Increment_len;i++) | |
{ | |
a = 9*pow(4,i)-9*pow(2,i)+1; | |
b = pow(4,i)-3*pow(2,i)+1; | |
H[i] = a>b?a:b; | |
} | |
} | |
else | |
return NULL; | |
return H; | |
} | |
// Use Sedgewick incremental sequence to sort | |
void ShellSort_Sedgewick(int A[],int N) | |
{ | |
int tmp; | |
int i,j; | |
int* Increment = Sedgewick(); | |
for(int k=9;k>=0;k--) | |
{ | |
for(i = Increment[k];i<N;i++) | |
{ | |
tmp = A[i]; | |
for(j = i;j>Increment[k];j-=Increment[k]) | |
{ | |
if(tmp<A[j-Increment[k]]) | |
A[j] = A[j-Increment[k]]; | |
else | |
break; | |
A[j] = tmp; | |
} | |
} | |
} | |
free(Increment); | |
} |
# 实现优化的快速排序算法
我寻思着想优化只能选最优得枢纽元和优先小区间排序优化了,但我不想写,就贴个例程得了。
// the mian function is Qsort() and Median3(). | |
void QuickSort(int A[],int N) | |
{ | |
Qsort(A,0,N-1); | |
} | |
// returns median of left, Center, and Right | |
// order these and hide the pivot | |
static int Median3(int A[],int Left,int Right) | |
{ | |
int Center = (Left + Right) / 2; | |
if(A[Left]>A[Center]) | |
Swap(&A[Left],&A[Center]); | |
if(A[Left]>A[Right]) | |
Swap(&A[Left],&A[Right]); | |
if(A[Center]>A[Right]) | |
Swap(&A[Center],&A[Right]); | |
// Invariant: A[Left] <= A[Center] <= A[Right] | |
Swap(&A[Center],&A[Right-1]);//hide pivot | |
return A[Right-1];//return pivot | |
} | |
#define Cutoff (3) | |
static void Qsort(int A[], int Left, int Right) | |
{ | |
int i,j; | |
int Pivot; | |
if(Left+Cutoff<=Right) | |
{ | |
Pivot = Median3(A, Left, Right); | |
i = Left;j = Right-1; | |
for(;;) | |
{ | |
while(A[++i]<Pivot){} | |
while(A[--j]>Pivot){} | |
if(i<j) | |
Swap(&A[i], &A[j]); | |
else | |
break; | |
} | |
Swap(&A[i], &A[Right-1]);//restore pivot | |
Qsort(A,Left,i-1); | |
Qsort(A,i+1,Right); | |
} | |
else //Do an insertion sort on the subarray | |
InsertSort(A+Left,Right-Left+1); | |
} |
# 编写一个例程读人两个用字母表示的文件并将它们合并到一起,形成第三个也是用字母表示的文件
就是满大街飞的合并文件的做法
#include <stdio.h> | |
#include <stdlib.h> | |
int main() | |
{ | |
// Open two files to be merged | |
FILE *fp1 = fopen("file1.txt", "rb"); | |
FILE *fp2 = fopen("file2.txt", "rb"); | |
// Open file to store the result | |
FILE *fp3 = fopen("file3.txt", "wb"); | |
char c; | |
if (fp1 == NULL || fp2 == NULL || fp3 == NULL) | |
{ | |
puts("Could not open files"); | |
exit(0); | |
} | |
// Copy contents of first file to file3.txt | |
while ((c = fgetc(fp1)) != EOF) | |
fputc(c, fp3); | |
// Copy contents of second file to file3.txt | |
while ((c = fgetc(fp2)) != EOF) | |
fputc(c, fp3); | |
printf("Merged file1.txt and file2.txt into file3.txt"); | |
fclose(fp1); | |
fclose(fp2); | |
fclose(fp3); | |
return 0; | |
} |
# 证明:任何基于比较的排序算法都需要平均 次比较。
比较次数可以转换为求叶子总深度可求得。
假设总共有 L 片树叶,左子树叶子数量为, 右子树叶子数量为。
则左子树树叶叶子总深度, 右子树树叶叶子总深度。
那么总的树叶叶子总深度为。
由不等式定理可以求得
。
所以任何基于比较的排序算法都需要平均 次比较,证毕。
# 提出一种算法,只用两盘磁带对一个大型文件进行排序。
将大型文件按一定规律在两个磁盘分成若干顺串,其中第一个磁盘为、、、, 第二个磁盘、、、,最后剩余得放在最后指向得磁盘。
从第二个磁盘开始与第一个磁盘进行归并排序,并放在第一磁盘中;
之后第一个磁盘开始与第二个磁盘进行归并排序,并放在第二磁盘中,循坏上面两步直到合并完全。