只能解决一种问题的算法,用处必然是有限的.所以今天介绍一种解决问题的思路,一个可以让我们使用的工具.
Divide and Conquer 分而治之
先来说明一下D&C的工作原理:
(1) 找出简单的基线条件
(2) 确定如何缩小问题的规模,使其符合基线条件.
10 | 20 | 30 |
---|
比如我们要把上面几个数字相加,你首先想到的是什么?
我开始的时候,想到的是循环,我想你们应该也有和我想得一样的吧.
那好,今天我们换个思路,用我们的D&C的思想来解决.
我们在计算一个整形数组的和的时候,最简单的情况是什么?
1.数组为空
2.数组只有一个元素
大多数情况下,我们要计算数组的和,那这个数组一般情况下是不会为空的.所以,我们的最理想的情况就是数组只有一个元素.基线条件已确定.
接下来,该如何将它分解到基线情况呢?
1.把第一个元素拆出来: 10 + sum(20+30)
2.把第二个元素拆出来: 10 + sum(20 + sum(30))
一层一层递进,就可以求出数组所有元素的和.
好,下面看一下python代码:
def sum(L):
#递归的退出条件
if l == []:
return 0
return l[0] + sum(l[1:])
快速排序是一种常用的排序算法,比选择排序快的多.
对一个数组进行排序,那什么情况是基线呢?其实和数组求和一样.如果这个数组是空,或者数组的只有一个元素,那么排序就很简单了,就是它自己.
7 | 2 | 10 |
---|
应用快速排序时,得先选择一个元素作为基准值(pivot),一般情况下,我们选择第一个元素作为基准值.
对于上面的数组,选择7作为基准值.
然后,向后遍历,遇到一个小于基准值的元素,放到基准值的左边.遇到一个大于基准值的元素,放到基准值的右边.这被称为分区(partitioning).
看一下现在的情况:
- 一个由所有小于基准值的数字组成的字数组;
- 基准值;
- 一个由所有大于基准值的数组组成的字数组.
现在,对于这个只有三个元素的数组,已经排序完毕.
12 | 8 | 2 | 15 |
---|
首先,12作为基准值,进行排序,第一轮完毕之后,是这个样子的:
8 | 2 | 12 | 15 |
---|
现在,排列情况是:
<基准值 | 基准值 | >基准值 |
---|
大于基准值的部分,只有一个元素,不需要排序了.
小于基准值的部分,两个元素,同样,我们按照上面的方法,递归调用,进行排序.最后,得到了:
2 | 8 | 12 | 15 |
---|
那,对于5个元素,6个元素,上千,上万的元素呢?同样啊,进行递归的调用,就可以排序了.
快速排序的python代码:
def quick_sort(arr):
if len(arr) < 2:
return arr
else:
pivot = arr[0]
less = [i for i in arr[1:] if i < pivot]
greater = [j for j in arr[1:] if j > pivot]
return quick_sort(less) + [pivot] + quick_sort(greater)
print(quick_sort([2,8,12,15,32,4,56,12]))
输出结果:
[2, 4, 8, 12, 15, 32, 56]
快速排序的时间复杂度为O(nlogn)
注意:递归的退出条件一定要给出
#include
#define N 5
int arr[N] = {23,65,156,545,32};
void quick_sort(int arr[],int low,int high)
{
//base value
int pivot = arr[low];
int i = low;
int j = high;
if(i > j)
return;
while(i < j)
{
while((i < j) && (arr[j] >= pivot))
j--;
if(i < j)
arr[i] = arr[j];
while((i < j) && (arr[i] <= pivot))
i++;
if(i < j)
arr[j] = arr[i];
}
arr[i] = pivot;
quick_sort(arr,low,i-1);
quick_sort(arr,j+1,high);
}
void main()
{
int i;
quick_sort(arr,0,4);
for(i = 0;i < 5;i++)
{
printf("%d \n",arr[i]);
}
}
c语言的代码相对于python代码有点复杂.c代码中定义了两个指针,来指向正在遍历的元素,一个指向小于pivot的,一个指向大于pivot的.