赞
踩
我们在求解一些复杂问题的时候,因为问题规模较大,难以理清思路。自然便会想到,把一个复杂、大规模的问题拆解成若干个简单、小规模的问题。
拆解得到的小问题与大问题的本质相同,但是对于小问题,我们更容易看清它的本质,更容易求解。这便是分而治之策略的思想。
基于分而治之思想设计的算法一般有以下3个步骤组成:
分而治之一般步骤:
- 分解原问题:将原问题分解为若干个不相交的子问题
- 解决子问题:递归地求解各个子问题
- 合并问题解:将子问题的解合并为原问题的解
因此,其时间复杂度分为3个部分:划分时间;求解时间;合并时间。
在我们自己设计算法的时候,可以根据问题的特征来设计分治算法。
分治算法能解决的问题的特征:
- 问题规模缩小到一定程度可以很容易解决
- 该问题具有最优子结构性质(即问题可以分解为若干个规模较小的相同问题)
- 子问题之间不交叉(即分解出的子问题之间是相互独立的)
- 分解出的子问题的解可以合并为该问题的解
分而治之的精髓在于分
与合
,针对不同的问题,巧妙地设计分与合的策略,可以取得令人满意的效果。
其中很典型的两个例子便是归并排序与快速排序,一个侧重分,一个侧重合。通过这两种算法的学习,可以更深刻地体会到分而治之思想的妙处。
推荐大家观看北航童咏昕教授的课程,简洁明了,清晰易懂。
归并排序,归指递归,并指合并。
基于分而治之思想设计的算法都要使用到递归,因此必须掌握对递归式的时间复杂度分析,可以参考我之前的一篇博客,介绍了4种分析递归式时间复杂度的方法,简洁易懂易用。
依据童教授的讲解,这里给出我自己实现的归并排序代码(C语言),水平有限,仅供参考学习。
#define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <stdio.h> void MergeSort(int *arr, int low, int high); void Merge(int *arr, int low, int mid, int high); int main() { int i; int arr[] = {70, 25, 23, 55, 31, 20, 22, 13, 62, 81, 50, 74, 65, 84, 80, 66, 64, 42, 12, 40}; MergeSort(arr, 0, 19); for (i = 0; i < 20; i++) printf("%d ", arr[i]); system("pause"); return 0; } void MergeSort(int *arr, int low, int high) { if (low >= high) return; int mid = (low + high) / 2; MergeSort(arr, low, mid); MergeSort(arr, mid + 1, high); Merge(arr, low, mid, high); } void Merge(int *arr, int low, int mid, int high) { int i, j, k; int *assist = (int *)malloc(sizeof(int) * (high + 1)); // 辅助数组,将两部分copy过来 for (k = low; k <= high; k++) assist[k] = arr[k]; k = low; i = low; j = mid + 1; while (i <= mid && j <= high) { if (assist[i] <= assist[j]) arr[k] = assist[i++]; else arr[k] = assist[j++]; k++; } while (i <= mid) arr[k++] = assist[i++]; while (j <= high) arr[k++] = assist[j++]; }
快速排序有多种实现方式,但重要的是理解其中的思想,关于其具体实现方式,大家可以自行查阅别的资料。
依据童教授的讲解,这里给出我自己实现的快速排序代码(C语言),水平有限,仅供参考学习。
#define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <stdio.h> #include <time.h> void QuickSort(int *arr, int low, int high); int QuickSort_Partition(int *arr, int low, int high); void QuickSort_RandomPivot(int *arr, int low, int high); int main() { int i; int arr[] = {70, 25, 23, 55, 31, 20, 22, 13, 62, 81, 50, 74, 65, 84, 80, 66, 64, 42, 12, 40}; QuickSort(arr, 0, 19); for (i = 0; i < 20; i++) printf("%d ", arr[i]); system("pause"); return 0; } void QuickSort(int *arr, int low, int high) { if (low < high) { int pivot = QuickSort_Partition(arr, low, high); QuickSort(arr, low, pivot - 1); QuickSort(arr, pivot + 1, high); } } int QuickSort_Partition(int *arr, int low, int high) { int i, j; // i作为分水岭,比主元小的在i左侧,比主元大的在右侧,i+1为第一个比主元大的 int temp; QuickSort_RandomPivot(arr, low, high); // 随机选取主元 int pivot = high; // 选取主元 i = low - 1; // 初始化i为low-1,j为low for (j = low; j < high; j++) // 遍历所有元素 { if (arr[j] <= arr[pivot]) // 当前元素比主元小,和i+1交换下 { temp = arr[j]; arr[j] = arr[i + 1]; arr[i + 1] = temp; i++; // i右移 } } temp = arr[pivot]; // 把主元放在中间做分界线 arr[pivot] = arr[i + 1]; arr[i + 1] = temp; return i + 1; } void QuickSort_RandomPivot(int *arr, int low, int high) { srand((unsigned)time(NULL)); int num = rand(); int pivot = low + num % (high - low + 1); int temp; temp = arr[high]; arr[high] = arr[pivot]; arr[pivot] = temp; }
基于分而治之的算法有很多,这里列举几个,感兴趣的读者可以自行查阅。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。