快速排序
前言:在博客写这些文章的目的用于记录所学,怕以后忘了,如果哪里写的不对欢迎指正,谢谢!!
学习目标:掌握快速排序算法的原理和思想
一、前提知识
排序算法概念、时间复杂度。可前往此网址 排序算法学习01_算法基础介绍阅读
二、快速排序介绍
快速排序(英语:Quicksort),属于交换排序的一种。又称分区交换排序(partition-exchange sort),我们一般简称为快排
三、快速排序工作原理
首先了解一下分治法:分治法将问题分解为规模更小的子问题,然后将这些子问题逐个击破,再将已解决的子问题合并,得出母问题的解
快速排序使用分治法策略,首先它要排序多次,但每次排序时并不真正的对每个数进行排序,而是从序列里找一个基准值将序列分成两个部分(子序列),此时这个基准值需所处在序列“中间”,一边序列放置比基准值小的元素,另一边序列则要比基准值大。
然后把比基准值小\大的部分的序列里再找一个基准值又分成两个部分,重复此逻辑。最终将得到一个排序好的结果
四、快速排序算法设计思路
设计一个快速排序算法,需要思考以下这几点:
- 基准值选择和移动(前面说过,分成俩部分后,比基准值小的在一边,大的在一边)
- 如何让比基准值小的部分和大的部分在基准值两边分开放置
-
接下来就是重点了,当采用以下方案时,基准值的选择有两种:第一种:序列最左边元素作为基准值。第二种序列最右边作为基准值
- (还有一种方案是基准值直接选择中间位置的,那就是另一种思路了,这里就不讲了)
-
首先,我们就要再引入两个变量:
- 一个为startIndex,该变量用于从序列最左边往右扫描。当要做升序时,扫描时遇到比基准值大,停止
- 一个为endIndex,该变量用于从序列最右边往左扫描。扫描时遇到比基准值小,停止
- 当两次扫描停止时,交换元素
- 当两个指针相遇时,该位置索引上的值与基准值互换
-
指针相遇后,则代表某个子序列排序完毕
- 我们此时要做的操作是:从基准值另一边的子序列进行排序,基准值重新选取。
-
当子序列元素为1时
- 当进入排序时,发现startIndex和endIndex重合就能结束了,也就是只有一个元素,就可结束了,去判断其他序列
-
当所有子序列都排序完毕,最终结果也就出来了
- 从上面思路我们也可以了解到,排序时,都是从大的序列排序,再慢慢到小的序列排序,左边序列排序完毕,排右边,这种分治思想,使用递归可以完美解决
五、过程详解
现在选择第一种,选取序列最左边元素作为基准值,此时进行图解如下
-
开始扫描,当选取最左边元素作为基准值时,那么需从最右边开始扫描,取右则相反
-
endIndex开始扫:扫到0,比3小,直接停止
-
startIndex开始扫:扫2不是,扫到11比3大,停止
-
两者扫描停止,互换元素,如下图:
-
-
指针还没相遇,则继续扫,依然是endIndex先开始扫
-
第二次扫
-
第三次扫
-
第四次,指针相遇,与基准值互换
-
-
至此,我们第一轮排序结束,基准值左边的都是小的,右边都是大的
-
接下来则排序基准值左边的子序列,你也可以先判断右边的
-
执行逻辑都是一样的
-
直到什么时候结束捏
- 当某个子序列结束后, 想再往某个基准值的一边排序时, 传值过来发现startIndex和endIndex重合,则表明这个子序列排序完毕
六、代码实现
对该数列进行递增排序 {3,2,11,5,1,7,9,4,-1,-8,0}
package com.migu.sortingAlgorithm;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int array[] = {3,2,11,5,1,7,9,4,-1,-8,0};
//从两端开始往里找
quickSort(array,0,array.length-1);
}
//采用分治思想
public static void quickSort(int [] array,int startIndex,int endIndex){
if(startIndex >= endIndex) return; // 当传入左右索引相遇时,则表明某个序列以排序完毕
int pivotIndex = sort(array, startIndex, endIndex); // sort方法,用于实现排序.并返回一个基准值,用于判断其他范围的序列
quickSort(array,startIndex,pivotIndex-1); // 这是基准值左边的序列
quickSort(array,pivotIndex+1,endIndex); // 这是右边的序列
}
//排序实现
private static int sort(int[] array,int startIndex,int endIndex){
// 定义四个变量: left:某个序列最左边索引,right:某个序列最右边索引。作用就是找大小
// pivot:基准值 temp:辅助交换
int left,right,pivot,temp;
pivot = array[startIndex]; // 该算法需要选某个序列最左/右边的元素为基准值
left = startIndex; // 从某个序列最左边,往右走
right = endIndex; // 从某个序列最右边,往左走
while (true){
// 基准值在左边,那么从右边开始找,当left和right重合时停止扫描
while (array[right] >= pivot && left < right) right--; // 找到小于基准值退出
while (array[left] <= pivot && left < right) left++; // 找到大于基准值退出
//得到一大一小进行交换
temp = array[left];
array[left] = array[right];
array[right] = temp;
if(left >= right) break; // 指针相遇退出循环
}
// 指针相遇,相遇位置的元素和基准值交换
temp = array[startIndex];
array[startIndex] = array[left];
array[left] = temp;
// 返回基准值此时所在的索引,以便排序其他子序列
return left;
}
}
七、时间复杂度
在平均状态下:该算法排序n个项目复杂度为O(n log n),极端情况下可能会想冒泡排序一样为O(n^2),一般不可能发生
八、总结
快速排序算法,采用分治法,实现跳跃式的'冒泡排序来对数据进行排序。
理清基准值两边元素是如何放置的,即可轻松掌握快速排序