本文属于《图解算法》系列。
一 分而治之
分而治之(devide and conquer)是递归思路是常见的解决方式。
作者以划分土地为例子,介绍这个思路:

换个思路,这是数学上典型的求两个数的最大公约数(GCD)的问题。
这里重申一下D&C的工作原理:
(1) 找出简单的基线条件;
(2) 确定如何缩小问题的规模,使其符合基线条件。
下面是Java分贝用递归跟非递归的方式实现。
public class EuclideanTest {
public static int Recursion(int m,int n){
if (n==0){
return m;
}
return Recursion(n,m%n);
}
public static void main(String[] args) {
int res = Recursion(1680,640);
int res2 = Recursion2(1680,640);
System.out.println("res="+res+":"+res2);
}
public static int Recursion2(int m,int n){
while(n!=0){
int tmp = m%n;
m = n;
n = tmp;
}
return m;
}
}
作者还举了一个数组求和的例子:

public class SumTest {
public static int sum(int[] nums){
if(nums.length==1){
return nums[0];
}else{
int[] tmp = new int[nums.length-1];
for(int i=1;i<=nums.length-1;i++){
tmp[i-1]= nums[i];
}
return nums[0]+ sum(tmp);
}
}
public static void main(String[] args) {
int [] test = {2,4,6};
int res= sum(test);
System.out.println(res);
}
}
二 快速排序
对于一个数组,快速排序的步骤如下:
(1) 选择基准值。
(2) 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。
(3) 对这两个子数组进行快速排序。


好吧,对于Java版本的实现要复杂些,起码数组操作没这么灵活:
public static void quickSort(int[] arrs,int left,int right){
if(left<right){
int temp,i=left,j=right;
temp=arrs[right];//基准值
while(i<j){
//寻找左边第一个大于基准值的下标
while(arrs[i]<=temp&&i<j)i++;
//赋值给right
if(i<j)arrs[j--]=arrs[i];
//寻找右边第一个小于基准值的下标
while(arrs[j]>=temp&&i<j)j--;
//赋值给left
if(i<j)arrs[i++]=arrs[j];
}
arrs[i]=temp;
//递归小的
quickSort(arrs,left,j-1);
//递归大的
quickSort(arrs,j+1,right);
}
}
public static void main(String[] args) {
int [] test = {10,5,2,3};
quickSort(test,0,test.length-1);
System.out.println(JSON.toJSON(test));
}
以测试的【10,5,2,3】为例,基准变量取最右侧的一位,是3
调用过程如下:左侧找到第一个比基准值大的数,赋值给右侧的right,
10,5,2,10
再寻找右侧第一个比基准小的数,赋值给左侧的left
2,5,2,20
此时,一轮下来,i=1,j=2 复合条件,temp=3,继续while循环
重复上面的步骤,左侧有比temp3大的5,处理后变成2,5,5,10
右侧没有复合条件的,跳过。
i=1,j=1.不符合while条件,退出。
arrs[i] =temp;
2,3,5,10
在递归一次,左侧小的【0,0】不符合条件,退出。
右侧【2,3】,left=2,right=3,基准值取出来为10,自己跟一下更清楚。
为了方便理解,在阿里云上找了个图片方便理解。

三 大O运行时间
数据不准确,置为展示时间曲线特性

还有一种名为合并排序(merge sort)的排序算法,其运行时间为O(n log n),比选择排序快 得多!快速排序的情况比较棘手,在最糟情况下,其运行时间为O(n2 )。
之前说的通常会忽略掉大O里面的常量部分,但有时候,常量的影响可能很大,对快速查找和合并查找来说就是如此。快速查找的常量合并查找小,因此如果它们的运行时间都为O(n log n),快速查找的速度将更快。实际上,快速查 找的速度确实更快,因为相对于遇上最糟情况,它遇上平均情况的可能性要大得多。
最糟情况:

最佳情况:
最佳情况:
第一个示例展示的是最糟情况,而第二个示例展示的是最佳情况。在最糟情况下,栈长为 O(n),而在最佳情况下,栈长为O(log n)。现在来看看栈的第一层。你将一个元素用作基准值,并将其他的元素划分到两个子数组中。 这涉及数组中的全部8个元素,因此该操作的时间为O(n)。在调用栈的第一层,涉及全部8个元素, 但实际上,在调用栈的每层都涉及O(n)个元素。

在这个示例中,层数为O(log n)(用技术术语说,调用栈的高度为O(log n)),而每层需要的 时间为O(n)。因此整个算法需要的时间为O(n) * O(log n) = O(n log n)。这就是最佳情况。
在最糟情况下,有O(n)层,因此该算法的运行时间为O(n) * O(n) = O(n2 )。
知道吗?最佳情况也是平均情况。只要你每次都随机地选择一个数组元素作为基准值,快速排序的平均运行时间就将为O(n log n)。快速排序是最快的排序算法之一,也 是D&C典范。
4 小结
D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元 素的数组。
实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(n log n)。
大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(log n)的速度比O(n) 快得多。
1万+

被折叠的 条评论
为什么被折叠?



