快速排序
算法描述:
快速排序又称划分交换排序,首先选择一个"基准"(基准的选择有多种,一般以Array[0]),从数组右边开始j=Array.length-1,j--的寻找小于"基准"数据,从左边i=0,i++的寻找大于"基准"数据,然后让小于"基准"的数据都放在左边,让大于"基准"的数据都放在右边,当i==j时,交换Array[i]与"基准"。这一趟下来数组划分成了两个子序列,这就是分治思想的分,然后递归的对左右两边的子序列,即可完成排序。
快速排序的步骤:
我们以数组int[]a={7,5,3,2,9,10,8,4,6,1};这个数组为例来说明一下快速排序到底是怎么进行的。
第1步:找基准值
所谓的基准值,顾名思义就是以它为基准进行比大小。通常来说,我们选取数组的第一个数为基准值。在数组a里基准值就是7.
第2步:比大小
先从数组的最右边开始往左边找比基准值小的第一个数,然后从数组的最左边开始往右找比基准值大的第一个数。那么为什么要这么找呢?因为现在我们要把数组从小到大排序,所以要找出比基准值小的数放到基准值的左边,找出比基准值的数放在基准值的右边。
那么在数组a里,从左往右找,第一个比7大的数就是9,我们把它的坐标记为i;从右往左找,第一个比7小的数就是1,我们把它的坐标记为j。
第3步:交换
找到之后,如果这个时候i<j,那么就交换这两个数,因为i=4,j=9,符合条件,将9和1进行交换。现在数组变成了int[]a={7,5,3,2,1,10,8,4,6,9};
如果j>=i,那么不做处理
为什么还要判断i和j的大小呢?就像第二步说的,我们要找出比基准值小的数放到基准值的左边,找出比基准值的数放在基准值的右边。所以如果i<j的话,交换就达到目的了,如果i>=j,比基准值小的数就在基准值的左边,比基准值大的数已经在基准值的右边了,这时候就没必要交换了。
第4步:继续查找
在i<j的前提下,继续向右查找比基准值大的数,往左找比基准值小的数,然后交换。
在我们的例子中,10和6、8和4都符合交换的条件,所以数组就变成了
int[]a={7,5,3,2,1,6,4,8,10,9};这时候i=6,j=7
第5步:交换基准值到合适的位置
当查找继续进行,这时候i=j=6,已经不满足继续查找和交换的条件了,那么我们应该怎么办呢?答案就是把a[6]和基准值交换,因为i=j的时候说明已经到了一个分界线的位置,分界线左边的数比基准值小,分界线右边的数比基准值大,而这个分界线就是我们的基准值,所以把它和a[i]交换,这时候数组就变成:
int[]a={4,5,3,2,1,6,7,8,10,9};
第6步:重复
对基准值左右两边的两个子数组重复前面五个步骤直至排序完成。
时间复杂度:O(n*lgn)
空间复杂度:O(1)
java实现:
public static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int index = partion(arr, left, right); //查找基准分割点
quickSort(arr, left, index - 1); //左边序列递归排序
quickSort(arr, index + 1, right); //右边序列递归排序
}
}
public static int partion(int[] arr, int left, int right) {
int pivot = arr[left]; //第一个作为基准点
int i = left;
int j = right;
while (i < j) {
while (pivot <= arr[j] && i < j) { //从右边查找小于基准的数据
j--;
}
while (pivot >= arr[i] && i < j) { //从左边查找大于基准的数据
i++;
}
if (i < j) { //左右交换
swap(arr, i, j);
}
}
arr[left] = arr[i]; //基准划分点
arr[i] = pivot;
return i;
}
public static void swap(int[] arr, int left, int right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
GitHub地址:
https://github.com/xckNull/Algorithms-introduction.git