快排遇到的坑
https://blog.csdn.net/liuzuoping/article/details/103387250?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162071138516780264087350%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=162071138516780264087350&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-103387250.first_rank_v2_pc_rank_v29&utm_term=java+%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95&spm=1018.2226.3001.4187
↑在这位兄台的文章下学习了一下各种排序的写法,感觉总的来说做得很好,有动图搭配理解,不过在快排的地方遇到了一个以前也讨论了很久的坑,今天又遇到,就记录一下,先看下他的代码
public static void sort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int startIndex, int endIndex) {
if (endIndex <= startIndex) {
return;
}
//切分
int pivotIndex = partition(arr, startIndex, endIndex);
sort(arr, startIndex, pivotIndex-1);
sort(arr, pivotIndex+1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
int left = startIndex;
int right = endIndex;
int pivot = arr[startIndex];//取第一个元素为基准值
while (true) {
//从左往右扫描
while (arr[left] <= pivot) {
left++;
if (left == right) {
break;
}
}
//从右往左扫描
while (pivot < arr[right]) {
right--;
if (left == right) {
break;
}
}
//左右指针相遇
if (left >= right) {
break;
}
//交换左右数据
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
//将基准值插入序列
int temp = arr[startIndex];
arr[startIndex] = arr[right];
arr[right] = temp;
return right;
}
乍一看感觉也没什么问题,自己运行一次以后,发现结果不对
第一行为原数组,第二行为执行结果
在纸上模拟了一下,发现了问题所在:原来的代码是先从左开始扫描,再从右开始扫描,这样可能会导致一种结果:左边的扫描遇到了一个比基准值大的数字,但是右边恰好也没有可以替换的数字了,满足了left == right,最后导致了基准数与之交换
比如:3 1 2 4 这个数组需要从小到大排序,如果先从左扫描,1和2都比3小,左指针会直接到达4的位置,满足了left == right的条件,因此第一次排序后,结果是4 1 2 3,后续的排序也不会正确了。但是如果先从右开始扫描,一定找到的是一个小于等于基准值3的数,与其交换后,仍然是满足左边的数小于等于基准值的。
结论:在类似的代码逻辑下,从左开始扫描可能会将大于基准值的数移动到左侧,导致最后结果不对。
最后贴一个修改后的代码,文章中有任何不合理的地方,欢迎指正
public static void sort(int[] arr) {
sort(arr, 0, arr.length - 1);
}
private static void sort(int[] arr, int startIndex, int endIndex) {
if (endIndex <= startIndex) {
return;
}
//切分
int pivotIndex = partition(arr, startIndex, endIndex);
sort(arr, startIndex, pivotIndex - 1);
sort(arr, pivotIndex + 1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
int left = startIndex;
int right = endIndex;
int pivot = arr[startIndex];//取第一个元素为基准值
while (true) {
//从右往左扫描
while (pivot <= arr[right]) {
right--;
if (left == right) {
break;
}
}
//从左往右扫描
while (arr[left] <= pivot) {
left++;
if (left == right) {
break;
}
}
//左右指针相遇
if (left >= right) {
break;
}
//交换左右数据
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
//将基准值插入序列
int temp = arr[startIndex];
arr[startIndex] = arr[right];
arr[right] = temp;
return right;
}
运行结果:
第一行为原数组,第二行为结果