1.根据分治想到归并排序,根据归并排序想到快速排序
我们要对一个无序的数组排序,用分治的思想来思考解决方案就是,将这个无序的数组分成两个子数组,分别排序,直观想到的结局方案就是归并排序的算法,基于两个排好序的子数组,比较着放入到大数组中。
但是归并排序核心的问题就是,我们能否不用去比较着插入呢?如果要两个子数组有序就得到整个数组有序,那么一定可以推出小的那个子数组最大的数小于等于大的子数组最小的那个元素。
2.快速排序的核心要素(为什么成立)
继续延展第一个部分我们得出的思路,我们怎么来描述小的数组全部小于大的数组呢?我们考虑转化为小的数组的数全部小于等于一个数,而大的数组全部大于等于一个数。好了,满足了这个条件了,这又如何呢?我们回到我们的数组模型,想一想,就发现,如果这个中间数恰好在应该在的位置上,那么这个数组已经完成了排序。(实际上,如果左边的数全小于中间数,右边的数全大于中间数,那么就能推出这个数在合适的位置上了)。
总结:左边数组有序+左边数组全小于中间数+右边数组有序+右边数组全大于中间数 => 整体数组有序。
3.快速排序的实现思路(步骤是什么)
因为是分治处理子数组,所以应该是递归的,我们选出中间数,把比它小的全放在它左边,把比它大的全放在它右边,然后,对左边排序,对右边排序。我们对排序递归,那么发现,还需要实现一个操作,就是加粗部分,中间元素的选取应该是任意的,我们没必要去为选取它调用算法,就选递归的数组的第一个就好了。那么问题来了,我们怎么把把数放到相应的区域内呢,我第一的想法是循环扫描比较,往两边放,这样的话,会产生及其大量的移动,有没有好的办法呢?我们要解决的是大量的移动,那么解决办法就是直接交换入坑。那么谁和谁交换呢,自然是左边区域却大,右边区域却小的,我们在数组两头分别设置两个指针,向中间扫描,当且仅当小的指针遇到比中间数大的,大的指针遇到比中间数小的,交换。
4.快速排序的程序实现(步骤转化为代码)
public class Quick {
public static void sort(int[] a, int low, int high)
{
if(low >= high) //递归先给出结束条件
return;
int middle = handle(a, low, high); //我们放好两个区域的数,同时需要返回一个中间数的角标用以递归排序
sort(a, low, middle-1);
sort(a, middle+1, high);
}
private static int handle(int[] a, int low, int high)
{
int middle = a[low];
int i = low + 1;//要把中间元素错开
int j = high;
while(true)
{
while(i!=high && a[i] < middle){ i++; }
while(j!=low && a[j] > middle){ j--; }
if(i >= j)
break; //这个判断需要在交换之前,找到之后。
int t = a[i];
a[i] = a[j];
a[j] = t;
}
int t = a[low];
a[low] = a[j];
a[j] = t;
return j;
}
public static void main(String[] args)
{
int[] arr = {3,4,5,8,7,6,9,0,1,2};
Quick.sort(arr, 0, arr.length-1);
for(int i=0; i<10 ; i++)
{
System.out.println(arr[i]);
}
}
}