intgetMedianIdx(int arr[], int left, int right) { int mid = (left + right) / 2;
int min = (arr[left] < arr[mid]) ? left : mid; min = (arr[min] < arr[right]) ? min : right;
int max = (arr[left] > arr[mid]) ? left : mid; max = (arr[max] > arr[right]) ? max : right;
if (min == left) { return max == right ? mid : right; } elseif (min == right) { return max == left ? mid : left; } else { return max == right ? left : right; } }
进阶 2:何时停止进行交换
上文的划分代码中,当左指针等于基准元素的时候并没有停止(while (i <= j && arr[i] <= pivot) {i++;}),当右指针等于基准元素的时候却停止了(while (j >= i && arr[j] > pivot) {j--;})。那么左、右指针是否应该停止呢?
假设,未排序的初始数组的所有元素都相同,那么试想一下左、右指针指向的元素等于基准元素时的情况:
左、右指针都不停止:那么先执行 while 循环的那个指针会一直移动,直到与另一个指针相遇(另一个指针一步也没移动)。这一轮移动划分的时间复杂度为 O(n),最后基准元素的位置还是在某一侧。下一次移动还是同样的情况,每次划分的数组的长度都是减少 1(另一个数组长度为 0)。整个排序过程的时间复杂度为 O(n^2)。
voidswap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; }
// 获取 arr[left], arr[mid], arr[right] 三数中值的索引 intgetMedianIdx(int arr[], int left, int right) { int mid = (left + right) / 2;
int min = (arr[left] < arr[mid]) ? left : mid; min = (arr[min] < arr[right]) ? min : right;
int max = (arr[left] > arr[mid]) ? left : mid; max = (arr[max] > arr[right]) ? max : right;
if (min == left) { return max == right ? mid : right; } elseif (min == right) { return max == left ? mid : left; } else { return max == right ? left : right; } }
intpartition(int arr[], int left, int right) { // 将选中的基准元素与数组末尾元素交换, 并指定基准元素为「数组末尾元素」 int pivotIdx = getMedianIdx(arr, left, right); swap(&arr[right], &arr[pivotIdx]); int pivot = arr[right];
int i = left, j = right - 1; while (true) { while (i <= j && arr[i] < pivot) { i++; } while (j >= i && arr[j] > pivot) { j--; } if (i < j) { swap(&arr[i++], &arr[j--]); } else { break; } } swap(&arr[right], &arr[i]);
return i; }
voidquickSort(int arr[], int left, int right) { if (left < right) { int pivot = partition(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot + 1, right); } }