摘自百度百科:
快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];(第一个位置空闲出来,需要赋值)
3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换; (不需要互换,因为下一次操作会覆盖A[j]。 将A[j]值赋给A[i], 这时第j个位置空闲出来,需要赋值)
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换; (不需要互换,因为下一次操作会覆盖A[i]。将A[i]值赋给A[j], 这时第i个位置空闲出来,需要赋值)
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。(这时第i个位置是空闲的,如果继续循环则执行第3步骤给它赋值;如果跳出循环则赋值为key )
说通俗一点, 快排的思想就是将大问题分解成小问题,解决完所有小问题后大问题也就解决了,即“分而治之”的算法思想。以上的5个步骤理解理解为填空题, 从第一个位置开始填空, 每当循环赋值后位置便空闲出来等待赋值,循环的从后向前或从前向后遍历; 然后再递归排序前半部分和后半部分。
对照例子说明快排的一趟排序(A[0]~A[9]表示数组元素,红色表示空闲位置,绿色表示刚刚被替换):
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 数值 | 11 | 54 | 85 | 57 | 39 | 80 | 70 | 45 | 82 |
执行算法第3步骤,即从数组j位置向前搜索,第一个满足条件的是下标8、数值45。
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 数值 | 45 | 11 | 54 | 85 | 57 | 39 | 80 | 70 | 82 |
执行算法第4个步骤, 从从数组i位置想后搜索,找到下标3、数组85, 将A[3]赋值给A[8]。A[3]变为无效位置,等待赋值。
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 数值 | 45 | 11 | 54 | 57 | 39 | 80 | 70 | 82 |
执行算法第3步骤,从j即第8个位置向前遍历, 下标5、值39符合要求(即小于key值60)
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 数值 | 45 | 11 | 54 | 39 | 57 | 80 | 70 | 82 |
执行算法第4步骤, 从下标i即第3个位置向后遍历,因为i<j条件限制, i自动都第5个位置时跳出循环。
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 数值 | 45 | 11 | 54 | 39 | 57 | 80 | 70 | 82 |
然后再递归排序key位置的左边和右边就行了。
完整Java代码:
private int partition(int[] array, int begin, int end) {
int i = begin; //从前向后遍历
int j = end; //从后向前遍历
int buf = array[begin]; //缓存数据, 可以理解成begin位置空出来了,等待赋值。
while (i < j) { //循环填充空闲出来的唯一位置
while (i<j && array[j] >= buf) { //从后向前找到第一个小于buf变量的位置
j--;
}
array[i] = array[j]; //将第j位置的值赋给第i个位置。这时第j个位置可以理解为空出来了,等待赋值。
while (i<j && array[i] <= buf) {
i++;
}
array[j] = array[i]; //给空出来的第j位置赋值, 这时i位置空闲出来了。
}
array[i] = buf; //i和j相等
return i;
}
private void quickSort(int[] array, int beigin, int end) {
if (beigin < end) {
int pos = partition(array, beigin, end);
quickSort(array, beigin, pos-1); //递归排序前半部分
quickSort(array, pos+1, end); //递归排序后半部分
}
}
int array[] = {60, 11, 54, 85, 57, 39, 80, 70, 45, 82};
quickSort(array, 0, array.length-1);其实在日程编码中, 我们更可能会用Comparator, 例如:
arrays.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
if (o1 < o2) {
return -1;
} else if (o1 == o2) {
return 0;
} else {
return 1;
}
}
});Swift3.0语言版本(注意inout和&的用法, 语法建议不要修改函数参数):
func partition(array: inout Array<Int>, low: Int, high: Int) -> Int {
var i = low
var j = high
let buf = array[low]
while i < j {
while i < j && array[j] >= buf {
j -= 1
}
array[i] = array[j]
while i < j && array[i] <= buf {
i += 1
}
}
array[i] = buf
return i
}
func quickSort(list: inout Array<Int>, low: Int, high: Int) {
if low < high {
let pos = partition(array: &list, low: low, high: high)
quickSort(list: &list, low: low, high: pos-1)
quickSort(list: &list, low: pos+1, high: high)
}
}
var array1 = [3, 3, 1, 32, 34, 42, 11, 23, 26]
quickSort(list: &array1, low: 0, high: array1.count-1) //array1数组改变为有序数组
var ret = array1.sorted(by: <) //array1数组不变,返回有序数组ret
JavaScript写法:
var arr = [3, 1, 39, 21, 36, 37, 93, 56]
quickSort(arr, 0, arr.length-1) //测试
function quickSort(array, low, high) {
if (low < high) {
var pos = partition(array, low, high)
quickSort(array, low, pos-1)
quickSort(array, pos+1, high)
}
}
function partition(array, low, high) {
var i = low
var j = high
var buf = array[low]
while (i < j) {
while (i<j && array[j] >= buf) {
j--
}
array[i] = array[j]
while (i<j && array[i] <= buf) {
i++
}
array[j] = array[i]
}
array[i] = buf
return i
} 对比Java,JavaScript和Swift3.0数组的定义方式不同, Java使用{}、Swift和JS使用[]。 其实Swift3.0排序是最方便的,调用sort方法就可以排序了,即:var ret = array.sorted(by: <)。
参考:https://juejin.im/post/5a08cc646fb9a045030f9174

562

被折叠的 条评论
为什么被折叠?



