基本思想
快排是对冒泡排序的一种改进,是C.R.A.Hoare于1962年提出的一种划分交换排序,采用了分治的策略,通常称为分治法
基本思想:
1、选取一个值为基准值point
2、将小于或等于point的值放于左边
3、将大于point的值放于右边
4、分别对左右子序列重复前三个步骤,直到各子序列只有一个数
上述图实例为快速的第一轮过程,显然是挖坑填数+分治法来实现整个快速排序过程,以序列为一数组,以i=0指向数组第一个数字,以j=5来指向数组最后一个数字,进行快排。
第一次取下标为0的数作为基准值:18
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
18 | 98 | 10 | 16 | 2 | 7 |
开始时,i=0,j=5,temp=num[i]=18
此时已经将num[0]的值保存到temp中,可理解为将num[0]这里挖坑,可以将其他数据填充到这里。从j=5开始向前寻找小于或等于temp的数字,j=5时,7<18,符合条件,将num[5]的值挖出填入坑num[0]中,即num[0]=num[5];i++;填完坑之后,又出现了新坑num[5],则需要再一次找数字来填这个坑,这次从i开始向后找大于temp的数字,当i=1时,即num[1]=98大于temp,符合条件,挖出num[1]的值填入坑num[5]中,即num[5]=num[1];j++;这样又出现了新坑num[1]…以此类推,当j等于i时,将基准值赋给num[i],分别对num[i]左右子序列递归调用快排方法,递归的结束条件为各子序列只有一个数字
代码实现
1、以上思想中挖坑填数+分治法结合,Java实现如下
public void quick_sort(int num[], int l, int r)
{
if (l < r)
{
int i = l, j = r, temp = num[l];
while (i < j)
{
// 从右向左找第一个小于或等于x的数来填坑num[i]
while(i < j && num[j] > temp)
j--;
if(i < j)
num[i++] = num[j]; //将num[j]填到num[i]中,num[j]形成新坑
// 从左向后找第一个大于x的数
while(i < j && num[i] <= temp)
i++;
if(i < j)
num[j--] = num[i]; //将num[i]填到num[j]中,num[i]形成新坑
}
num[i] = temp;
quick_sort(s, l, i - 1); // 左边子序列递归调用
quick_sort(s, i + 1, r); //右边子序列递归调用
}
}
2、Golang的快排结合切片实现(用填坑法+分治Go重写1的Java代码超出时间限制)
注:continue语句执行是跳过本次循环(不管后面未执行的语句)继续下一次循环
func quick_sort(nums []int){
i , j := 0 , len(nums) - 1
for i < j {
if nums[j] > nums[0] {
j--
continue
}
if nums[i] <= nums[0] {
i++
continue
}
nums[i],nums[j] = nums[j] ,nums[i]
}
nums[0],nums[i] = nums[i],nums[0]
if len(nums[:j]) > 1 {
sortArray(nums[:j]) //左边子序列递归调用
}
if len(nums[j+1:]) >1 {
sortArray(nums[j+1:]) //右边子序列递归调用
}
}
3、Golang slice方法,很好体现出分治思想
func sortArray(nums []int) []int {
if len(nums) <= 1 {
return nums
}
//nums[0]作为基准值
key := nums[0]
var left, right []int
for _, e := range nums[1:] {
if e <= key {
left = append(left, e)
} else {
right = append(right, e)
}
}
return append(append(sortArray(left), key), sortArray(right)...)
}