快速排序算法虽然看起来简单,但是要手写实现一个没有bug的版本,对不是每天都在做数据结构和算法的工程师来说还是有点困难的,这里给出了4种两边查找的排序算法的实现,亲测过!实现主要是细节的注意,最关键是下标的处理上:
/**
* 排序方法一(基数固定在第一个位置):
* 设置左右两个扫描指针,左边是从第一个位置再往前一个位置(即排除基数的位置)开始,右边从最后一个位置再往后一个位置开始
* 先从右往找比基数小的位置,再从左往右找比基数大的位置,或反过来也可以,如果两个方向的位置重合,或者交叉了,
* 停掉这次循环,这时可以确定左边位置(不包括不前位置和第一个基数位置即(1, i))往左都比基数小,右边往右位置(不包括当前位置即(i, end])都比基数要大,如果没有出现交叉,那就要交换这两个位置的数据,然后进入下次循环
* 如果在一次划分过程结束,将基数位置和左边指针扫描的位置进行交换,这样就得到了左边指针左边一直往左(即[0, i))都是小于基数的,右边指针一直往右(即[i, end])都是大于基数的,完成一次划分
* 然后对[start, i - 1]进行划分,对[i + 1, end]进行划分
* @param arr
* @param start
* @param end
*/
public void sort(int[] arr, int start, int end) {
if (start < end) {
// System.out.println("start=" + start + ",end=" + end);
int i = start;
int j = end + 1;
int middle = (start + end) / 2;
int t = arr[start];
while (true) {
while (i < end && arr[++i] <= t) {
}
while (j > 0 && arr[--j] >= t) {
}
if (i >= j) {
break;
}
swap(arr, i, j);
}
// System.out.println("i=" + i + ",j=" + j);
swap(arr, j, start);
sort(arr, start, j - 1);
sort(arr, j + 1, end);
}
}
public void swap(int arr[], int f, int t) {
System.out.println("swap:f=" + f + ",t=" + t + "[" + arr[f] + "]=>[" + arr[t] + "]");
int temp = arr[f];
arr[f] = arr[t];
arr[t] = temp;
}
/**
* 排序方法二(根据情况调整基数位置):
* 设置左右两个扫描指针,左边是从第一个位置开始,右边从最后一个位置开始
* 先从右边扫描第一个小于基数的位置,如果两边扫描没有相遇,交换基数位置和右边扫描到的位置,此时基数放到右边去了
* 然后左边从第一个位置再往前一个位置开始找比基数大的位置(要往前一个位置是因为基数占了第一个位置或者从右边交换了一个比基数小的位置在第一个位置了)
* 如果两边指针还没有相遇,交换右边位置和左边位置(即新的基数位置和左边比基数大的位置),此时基数放回左边了
* 然后把右边位置往左边位置移动一下进行下次的循环
* 直到两个扫描指针相遇,结束一次划分操作
* 然后对左边和右边分别再进行划分
*
* 这个方法一次划分的结果扫描指什是相遇的即i = j,基数总是在i位置即相遇的位置上,一次划分结果就是分成[0, i)小于基数的范围和(i, end]大于基数的范围
* @param arr
* @param start
* @param end
*/
public void sort2(int[] arr, int start, int end) {
if (start < end) {
int i = start;
int j = end;
int t = arr[start];
while (i < j) {
while (i < j && arr[j] >= t) {
j--;
}
if (i < j) {
swap(arr, i++, j);
}
while (i < j && arr[i] <= t) {
i++;
}
if (i < j) {
swap(arr, j--, i);
}
}
// System.out.println("i=" + i + ",j=" + j);
// arr[i] = t;
sort2(arr, start, i - 1);
sort2(arr, i + 1, end);
}
}
/**
* 排序算法三(基于排序算法二,在找到位置时不是交换而是先赋值):
* 这个方法跟方法二有个地方不一样的是,要在划分循环结束时,将结束位置赋值为基数
* @param arr
* @param start
* @param end
*/
public void sort3(int[] arr, int start, int end) {
if (start < end) {
int i = start;
int j = end;
int t = arr[start];
while (i < j) {
while (i < j && arr[j] >= t) {
j--;
}
if (i < j) {
//swap(arr, i++, j);
arr[i++] = arr[j];
}
while (i < j && arr[i] <= t) {
i++;
}
if (i < j) {
// swap(arr, j--, i);
arr[j--] = arr[i];
}
}
arr[i] = t;
// System.out.println("i=" + i + ",j=" + j);
// arr[i] = t;
sort3(arr, start, i - 1);
sort3(arr, i + 1, end);
}
}
/**
* 排序方法四:
* 方法一划分的两边扫描先从左往右还是从右往左都是一样的,但方法二和方法三一定要先从右往左,否则交换位置就错乱了
* 方法四就是为了解决这个问题的解决方案,方法是跟方法一类似先两边扫描到位置再说
* 如果两边扫描结果没相遇,就交换两个位置,然后两边指值分别都换原来方向移一个位置
* 如果指针相遇,那么如果相遇位置的值和基数比小的话,就要将这个位置和基数位置交换,这样才保证结束位置往左都是小于等于基数,结束位置往右都是大于等于基数
* 这时一次划分完成,但i和j位置可能不是刚好相等,i可能比j大了一个位置,这时基数不在i或j位置,所以划分的位置也会不太一样。
* @param arr
* @param start
* @param end
*/
public void sort4(int[] arr, int start, int end) {
if (start < end) {
int i = start;
int j = end;
int t = arr[start];
while (i < j) {
while (i < j && arr[i] <= t) {
i++;
}
while (i < j && arr[j] >= t) {
j--;
}
if (i < j) {
swap(arr, i, j);
i++;
j--;
}
}
if (i == j && arr[start] > arr[i]) {
swap(arr, start, i);
}
sort4(arr, start, i - 1);
sort4(arr, i, end);
}
}