⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄

📁 分而治之算法描述
💻
📖 第 1 页 / 共 5 页
字号:
Y[i].p = i;

Y[i].x = X[i].x;

Y[i].y = X[i].y;

}

M e r g e S o r t ( Y,n); // 按y坐标排序

// 创建临时数组

Point2 *Z = new Point2 [n];

// 寻找最近点对

c l o s e ( X , Y, Z , 0 , n - 1 , a , b , d ) ;

// 删除数组并返回

delete [] Y;

delete [] Z;

return true;

}

程序1 4 - 11 计算最近点对

void close(Point1 X[], Point2 Y[], Point2 Z[], int l, int r, Point1& a, Point1& b, float& d)

{//X[l:r] 按x坐标排序

//Y[l:r] 按y坐标排序

if (r-l == 1) {// 两个点

a = X[l];

b = X[r];

d = dist(X[l], X[r]);

r e t u r n ; }

if (r-l == 2) {// 三个点

// 计算所有点对之间的距离

float d1 = dist(X[l], X[l+1]);

float d2 = dist(X[l+1], X[r]);

float d3 = dist(X[l], X[r]);

// 寻找最近点对

if (d1 <= d2 && d1 <= d3) {

a = X[l];

b = X[l+1];

d = d1;

r e t u r n ; }

if (d2 <= d3) {a = X[l+1];

b = X[r];

d = d2;}

else {a = X[l];

b = X[r];

d = d3;}

r e t u r n ; }

/ /多于三个点,划分为两部分

int m = (l+r)/2; // X[l:m] 在A中,余下的在B中

// 在Z[l:m] 和Z [ m + 1 : r ]中创建按y排序的表

int f = l, // Z[l:m]的游标

g = m+1; // Z[m+1:r]的游标

for (int i = l; i <= r; i++)

if (Y[i].p > m) Z[g++] = Y[i];

else Z[f++] = Y[i];

// 对以上两个部分进行求解

c l o s e ( X , Z , Y, l , m , a , b , d ) ;

float dr;

Point1 ar, br;

c l o s e ( X , Z , Y, m + 1 , r, a r, b r, d r ) ;

// (a,b) 是两者中较近的点对

if (dr < d) {a = ar;

b = br;

d = dr;}

M e r g e ( Z , Y,l,m,r);// 重构Y

/ /距离小于d的点放入Z

int k = l; // Z的游标

for (i = l; i <= r; i++)

if (fabs(Y[m].x - Y[i].x) < d) Z[k++] = Y[i];

// 通过检查Z [ l : k - 1 ]中的所有点对,寻找较近的点对

for (i = l; i < k; i++){

for (int j = i+1; j < k && Z[j].y - Z[i].y < d;

j + + ) {

float dp = dist(Z[i], Z[j]);

if (dp < d) {// 较近的点对

d = dp;

a = X[Z[i].p];

b = X[Z[j].p];}

}

}

}

函数c l o s e(见程序1 4 - 11)用来确定X[1:r] 中的最近点对。假定这些点按x 坐标排序。在Y [ 1 : r ]中对这些点按y 坐标排序。Z[ 1 : r ]用来存放中间结果。找到最近点对以后,将在a, b中返回最近点对,在d 中返回距离,数组Y被恢复为输入状态。函数并未修改数组X。

首先考察“小问题”,即少于四个点的点集。因为分割过程不会产生少于两点的数组,因此只需要处理两点和三点的情形。对于这两种情形,可以尝试所有的可能性。当点数超过三个时,通过计算m = ( 1 + r ) / 2把点集分为两组A和B,X [ 1 : m ]属于A,X [ m + 1 : r ]属于B。通过从左至右扫描Y中的点以及确定哪些点属于A,哪些点属于B,可以创建分别与A组和B组对应的,按y 坐标排序的Z [ 1 : m ]和Z [ m + 1 : r ]。此时Y和Z的角色互相交换,依次执行两个递归调用来获取A和B中的最近点对。在两次递归调用返回后,必须保证Z不发生改变,但对Y则无此要求。不过,仅Y [ l : r ]可能会发生改变。通过合并操作(见程序1 4 - 5)可以以Z [ 1 : r ]重构Y [ 1 : r ]。

为实现图1 4 - 1 6的策略,首先扫描Y [ 1 : r ],并收集距分割线小于的点,将这些点存放在Z [ 1 : k - 1 ]中。可按如下两种方式来把RA中点p 与p 的比较区内的所有点进行配对:1) 与RB 中y 坐标≥p.y 的点配对;2) 与y 坐标≤p.y 的点配对。这可以通过将每个点Z [ i ](1≤i < k,不管该点是在RA

还是在RB中)与Z[j] 配对来实现,其中i<j 且Z [ j ] . y - Z [ i ] . y< 。对每一个Z [ i ],在2 × 区域内所检查的点如图1 4 - 1 7所示。由于在每个2 × 子区域内的点至少相距。因此每一个子区域中的点数不会超过四个,所以与Z [ i ]配对的点Z [ j ]最多有七个。

2. 复杂性分析

令t (n) 代表处理n 个点时,函数close 所需要的时间。当n<4时,t (n) 等于某个常数d。当n≥4时,需花费(n) 时间来完成以下工作:将点集划分为两个部分,两次递归调用后重构Y,淘汰距分割线很远的点,寻找更好的第三类点对。两次递归调用需分别耗时t (「n /2ù」和t (?n /2?).

这个递归式与归并排序的递归式完全一样,其结果为t (n) = (nl o gn)。另外,函数c l o s e s t还需耗时(nl o gn)来完成如下额外工作:对X进行排序,创建Y和Z,对Y进行排序。因此分而治之最近点对求解算法的时间复杂性为(nl o gn)。

练习

8. 编写一个完整的残缺棋盘问题的求解程序,提供以下模块:欢迎用户使用本程序、输入棋盘大小和残缺方格的位置、输出覆盖后的棋盘。输出棋盘时要着色,共享同一边界的覆盖应着不同的颜色。由于棋盘是平面图,所以最多只需用四种颜色便可为整个棋盘着色。在本练习中,应尽量使用较少的颜色。

9. 用迭代方法求解公式(1 4 - 7)的递归式。

10. 编写一个归并排序程序,要求用链表来存储元素。输出结果为排序后的链表。把函数做为C h a i n类(见程序3 - 8)的一个成员函数。

11. 编写函数N a t u r a l M e rgeSort 来实现自然归并排序。其中输入和输出的规定与程序1 4 - 3相同。

12. 编写一个自然归并排序函数,用来对链表元素进行排序。把函数设计为C h a i n类的一个成员(见程序3 - 8 )。

13. 用一个w h i l e循环来替换程序1 4 - 6中的最后一个递归调用q u i c k S o r t。比较修改后的函数与程序1 4 - 6的平均运行时间。

14. 重写程序1 4 - 6,使用堆栈来模拟递归。堆栈中只需保存l e f t和r i g h t中较小者的边界。

1) 证明所需要的栈空间大小为O ( l o gn)。

2) 比较程序1 4 - 6和新代码的平均运行时间。

15. 证明在最坏情况下Q u i c k S o r t的运行时间为( n2)。

16. 假定在划分l e f t、middle 和right 时按照如下方式来进行:若n为奇数,则l e f t与r i g h t的大小相同;若n为偶数,则l e f t比r i g h t多一个元素。证明在这种假设条件下,程序1 4 - 6的时间复杂性为(nl o gn)。

17. 证明,并利用该结果证明。

18. 试比较使用“中间的中间”规则与不使用该规则时,程序1 4 - 6的最坏复杂性和平均复杂性。取n=10, 20, ., 100, 200, 300, 400, 500, 1000及适当的测试数据来进行比较。

19. 采用随机产生的数作为支点元素完成练习1 8。

20. 在1 4 . 2 . 3节快速排序结束时,我们曾建议将快速排序与插入排序进行结合,结合后的算法实质上仍是快速排序,只是当排序部分小于等于C h a n g e O v e r = n B r e a k时执行插入排序。能否通过改变C h a n g e O v e r而得到更快的算法?为什么?试试不同的C h a n g e O v e r。确定能提供最佳平均性能的C h a n g e O v e r。

21. 设计一个在最差情况下性能最好的排序算法。

1) 比较插入排序、冒泡排序、选择排序、堆排序、归并排序和快速排序在最坏情况下的运行时间。导致插入排序、冒泡排序、选择排序和快速排序出现最坏复杂性的输入数据很容易产生。试编写一个程序,用来产生导致归并排序出现最坏复杂性的输入数据。这个程序本质上是将n 个排好序的元素“反归并”。对于堆排序,用随机产生的输入序列来估算最坏情况下的时间复杂性。

2) 利用1) 中的结果设计一个混合排序函数,使其在最坏情况下具有最佳性能。比如混合函数可以只包含归并排序和插入排序。

3) 测试混合排序函数在最坏情况下的运行时间,并与原排序函数进行比较。

4) 用一个简单的图表来列出七种排序函数在最差情况下的运行时间。

22. 当n 是2幂时,用迭代方法求解公式1 4 - 8。

*23. 证明定理1 4 - 2。

*24. 用归纳法证明,对于公式(1 4 - 11),当n≥1时有t (n)≤7 2c n。

25. 程序1 4 - 7所需要的递归栈空间为O ( n )。当用一个w h i l e或f o r循环来代替递归调用时,可以完全消除这种递归栈空间。根据这种思想重写程序1 4 - 7。比较这两种选择排序函数的运行时间。

26. 重写程序1 4 - 7,用随机数产生器来选择支点元素。试比较这两种代码的平均性能。

27. 重写程序1 4 - 7,使用“中间的中间”规则,其中r = 9。

28. 为了加快程序1 4 - 11的执行速度,可以不执行距离计算公式中的开方运算,而直接用距离的平方来代替距离,所得结果是一样的。为此,程序1 4 - 11必须做哪些改变? 试通过实验来比较这两种版本的性能。

29. 重写程序1 4 - 11,把P o i n t 1作为模板类,其中I D域的类型由用户来决定。

30. 当所有点都在一条直线上时,编写一个更快的算法来寻找最近的点对。例如,假设所有点都在一条水平线上。如果这些点根据x 坐标排序,则最近点对中的两个点必相邻。虽然使用M e rg e S o r t(见程序1 4 - 3)来实现这种策略时,算法的复杂性仍然是O(nl o gn),但这种算法的额外开销要比程序1 4 - 1 0小得多,因此会运行得更快。

31. 考察最近点对问题。假设初始时不是根据x 坐标来排序,而是使用Selcet (见程序1 4 - 7 )来寻找中点xi ,以便将点集划分为A组和B组。

1) 给出按这种思想实现的最近点

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -