什么是递归?
递归:无限调用自身这个函数,每次调用总会改动一个关键变量,直到这个关键变量达到边界的时候,不再调用。
递归与循环的区别于联系
相同点:
(1)都是通过控制一个变量的边界(或者多个),来改变多个变量为了得到所需要的值,而反复而执行的;
(2)都是按照预先设计好的推断实现某一个值求取;(请注意,在这里循环要更注重过程,而递归偏结果一点)
不同点:
(1)递归通常是逆向思维居多,“递”和“归”不一定容易发现(比较难以理解);而循环从开始条件到结束条件,包括中间循环变量,都需要表达出来(比较简洁明了)。
简单的来说就是:用循环能实现的,递归一般可以实现,但是能用递归实现的,循环不一定能。因为有些题目①只注重循环的结束条件和循环过程,而往往这个结束条件不易表达(也就是说用循环并不好写);②只注重循环的次数而不注重循环的开始条件和结束条件(这个循环更加无从下手了)。
快速排序
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
排序方法:
1、先从数列中选取一个基准数,一般为了方便就选第一个。
2、再对数组进行排序,大的在右边,小的在左边。
3、再分别对左右边重复第一二步,直到各区间只剩下一个数。
来个实例:
以一个数组为例,取第一个数为基准
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
26 | 21 | 45 | 65 | 10 | 78 | 49 | 89 | 13 | 32 |
初始化值,int i=0,j=9,x=a[0];把a[0]赋给x(基准数),相当于给a[0]的位置挖了个坑,把a[0]保存给x以后再用。
先从右即j=9开始循环找出比x小的数,循环到j=8时,a[8]>x,我们就要用a[8]填上a[0]这个坑,a[0]=a[8]; i++;,这时候a[0]的坑填上了但a[8]位置又变成了坑,我们就要从左边进行i循环找到一个比x大的数来填补a[8]这个坑。i++;循环过后,发现a[2]比x大,则a[8]=a[2]; j--;
数组变成了:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
13 | 21 | 45 | 65 | 10 | 78 | 49 | 89 | 45 | 32 |
这时,i=2,j=7,x=26。
然后继续重复上述步骤,直到i==j;再将基准数x填入a[i];然后对左右两个子区间进行递归排序,直到区间大小为1时停止。
我们来实现一下代码:
public static void sort(int a[], int low, int hight) {
int i, j, index;
if (low > hight) {
return;
}
i = low;
j = hight;
index = a[i]; // 用子表的第一个记录做基准
while (i < j) { // 从表的两端交替向中间扫描
while (i < j && a[j] >= index)
j--;
if (i < j)
a[i++] = a[j];// 用比基准小的记录替换低位记录
while (i < j && a[i] < index)
i++;
if (i < j) // 用比基准大的记录替换高位记录
a[j--] = a[i];
}
a[i] = index;// 将基准数值替换回 a[i]
sort(a, low, i - 1); // 对低子表进行递归排序
sort(a, i + 1, hight); // 对高子表进行递归排序
}
快速排序的优化
对于基准位置的选取一般有三种方法:固定切分,随机切分和三取样切分。固定切分的效率并不是太好,随机切分是常用的一种切分,效率比较高,最坏情况下时间复杂度有可能为O(N2).对于三数取中选择基准点是最理想的一种。
三数取中切分:
public static int partition(int []array,int lo,int hi){
//三数取中
int mid=lo+(hi-lo)/2;
if(array[mid]>array[hi]){
swap(array[mid],array[hi]);
}
if(array[lo]>array[hi]){
swap(array[lo],array[hi]);
}
if(array[mid]>array[lo]){
swap(array[mid],array[lo]);
}
int key=array[lo];
while(lo<hi){
while(array[hi]>=key&&hi>lo){
hi--;
}
array[lo]=array[hi];
while(array[lo]<=key&&hi>lo){
lo++;
}
array[hi]=array[lo];
}
array[hi]=key;
return hi;
}
public static void swap(int a,int b){
int temp=a;
a=b;
b=temp;
}
public static void sort(int[] array,int lo ,int hi){
if(lo>=hi){
return ;
}
int index=partition(array,lo,hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
快速排序在序列中元素很少时,效率将比较低,不然插入排序,因此一般在序列中元素很少时使用插入排序,这样可以提高整体效率。
public static void quick(int []array ,int lo,int hi){
if(hi-lo+1<10){
insertSort(array);
}else{
quickSort(array,lo,hi);
}
}