快速排序基本算法
private static void quickSort(int[] a, int low, int high) {
// index为关键数据
int index = a[low];
int i = low;
int j = high;
//高指针与低指针重合时退出
while (i < j) {
// 高指针移动
// 没找到,高指针退一
while (i < j && a[j] > index) {
j--;
}
// while循环满足满足前半部分,说明找到了
// 找到就把关键数据填入左坑,同时左指针加一
if (i < j) {
a[i] = a[j];
a[j] = index;
i++;
}
// 低指针移动
// 与高指针移动相同,参照高指针
while (i < j && a[i] < index) {
i++;
}
if (i < j) {
a[j] = a[i];
a[i] = index;
j--;
}
}
//因为每次循环完,都是把左值当做坑,故要从左边开始
if(i-1>low){quickSort(a, low, i-1);}
if(j+1<high){quickSort(a, j+1, high);}
}
}
快排基本算法的变形
快排实际上是确定关键数据、左值针索引数据、右指针索引数据三者的位置
使其中的数据呈现出 x<关键数据<y的关系
1、右指针找大的数,循环条件设置为可以等于,这样找不到就会左右指针重合
2、左指针找小的数,循环条件设置为可以等于,这样找不到就会左右指针重合
3、不论是否找到都可以交换左右指针对应索引的数据,此时左右指针的位置已经确定
4、关键数据总是处于中间位置,又因为索引是从最左侧开始的,故而关键数据要放在左指针的位置
5、重复执行1,2,3,4
快速排序随机数版本
public class Solution {
public void quickSort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int mid = part(arr, l, r);
quickSort(arr, l, mid - 1);
quickSort(arr, mid + 1, r);
}
public int part(int[] arr, int l, int r) {
// 随机数部分,1.产生随机数,2.与l所在的数字进行交换
//这里+1是为了能取到r
int q = new Random().nextInt(r - l + 1) + l;
swap(arr, l, q);
int i = l, j = r, x = arr[l];
while (i < j) {
while (i < j && arr[j] >= x) {
j--;
}
arr[i] = arr[j];
while (i < j && arr[i] <= x) {
i++;
}
arr[j] = arr[i];
}
arr[i] = x;
return i;
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
快排左右指针交换版
两次交换的正确性:
- while循环内的交换(交换1),表示当且仅当,左右指针都找到了才交换。这比较容易理解。
如果任意一侧没有找到就会造成左右指针重合,交换也就没有意义。 - while外的交换(交换2),这表示快速排序把基准数填入左右指针重合处。
此时i=j,那么唯一的要求就是arr[l]=x。仍旧考虑左右指针同时找到和左右至少一个没有找到两种情况。
1).左右同时找到,此时i>l且j<r,此时不会到达while外循环。
2.1).右侧没有找到,此时i=j=l,arr[l]=x;
2.2).左侧没有找到,此时i=j<=r,此时arr[l]=x;
2.3).左右侧都没有找到,与2.1相同。
由以上两点,得,while外的交换也是正确的。
while外的循环在挖坑法里需要交换,这很容易理解。但是交换法为什么还要交换哪?它的基准处不是坑呀,为什么还需要交换?
我们利用前面的while外循环分析:
1).左右同时找到,此时不会退出while循环。
2.1).右侧没有找到,此时i=j=l,此时退出循环,不需要交换;
2.2).左侧没有找到,此时i=j<=r,此时退出循环,并且有arr[l]>arr[r],需要交换;
2.3).左右侧都没有找到,与2.1相同。
也可以说交换法是挖坑法的一种形式。
private static void quickSort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int i = l, j = r, x = arr[l];
while (i < j) {
while (i < j && arr[j] >= x) {
j--;
}
while (i < j && arr[i] <= x) {
i++;
}
//交换1
if (i < j) {
swap(arr, i, j);
}
}
//交换2
swap(arr, l, j);
quickSort(arr, l, i - 1);
quickSort(arr, i + 1, r);
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
快排非递归(借助栈)
class Solution {
//核心代码
public void quickSort(int[] arr) {
int l = 0, r = arr.length - 1, mid = -1;
Stack<Integer> stack = new Stack<>();
stack.push(l);
stack.push(r);
while (!stack.isEmpty()) {
r = stack.pop();
l = stack.pop();
if (l < r) {
mid = part(arr, l, r);
stack.push(l);
stack.push(mid - 1);
stack.push(mid + 1);
stack.push(r);
}
}
}
public int part(int[] arr, int l, int r) {
// 随机数部分,1.产生随机数,2.与l所在的数字进行交换
//这里+1是为了能取到r
int q = new Random().nextInt(r - l + 1) + l;
swap(arr, l, q);
int i = l, j = r, x = arr[l];
while (i < j) {
while (i < j && arr[j] >= x) {
j--;
}
arr[i] = arr[j];
while (i < j && arr[i] <= x) {
i++;
}
arr[j] = arr[i];
}
arr[i] = x;
return i;
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}