六、排序(3 题)
六、排序(3 题)¶
1. 快速排序¶
int partition(int A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot)
high--;
A[low] = A[high];
while (low < high && A[low] < pivot)
low++;
A[high] = A[low];
}
A[low] = pivot;
return low;
}
void quickSort(int A[], int low, int high) {
if (low >= high)
return;
int pivotPos = partition(A, low, high);
quickSort(A, low, pivotPos - 1);
quickSort(A, pivotPos + 1, high);
}
复杂度: 平均时间 O(n log n),最坏时间 O(n²);平均递归栈空间 O(log n),最坏 O(n)。
校订注:原稿只给出
O(n log n)和O(log n),这是平均情况,不是快速排序的最坏情况。
回源 · PDF 第 27 页 ·
b_27_1;PDF 第 27 页 · b_27_02. 二路归并排序¶
bool mergeRanges(int A[], int low, int mid, int high) {
int length = high - low + 1;
int *temp = (int *)malloc(length * sizeof(int));
if (temp == NULL)
return false;
for (int i = low; i <= high; i++)
temp[i - low] = A[i];
int i = 0;
int j = mid - low + 1;
int leftEnd = mid - low;
int rightEnd = high - low;
int k = low;
while (i <= leftEnd && j <= rightEnd) {
if (temp[i] <= temp[j])
A[k++] = temp[i++];
else
A[k++] = temp[j++];
}
while (i <= leftEnd)
A[k++] = temp[i++];
while (j <= rightEnd)
A[k++] = temp[j++];
free(temp);
return true;
}
bool mergeSort(int A[], int low, int high) {
if (low >= high)
return true;
int mid = low + (high - low) / 2;
if (!mergeSort(A, low, mid))
return false;
if (!mergeSort(A, mid + 1, high))
return false;
return mergeRanges(A, low, mid, high);
}
复杂度: 时间 O(n log n),额外空间 O(n),递归栈 O(log n)。
调用后应检查 mergeSort(A, 0, n - 1) 的返回值;false 表示辅助数组申请失败,不能把当前数组误当成“已完成排序”。
校订注:原稿每次
malloc后没有free,会造成内存泄漏,本版已补上释放,并把内存申请失败逐层传回调用者。
回源 · PDF 第 27 页 ·
b_27_3;PDF 第 27 页 · b_27_23. 将集合划分为元素数最接近、元素和之差最大的两个子集(2016 年统考题)¶
给定由 n (n>=2) 个正整数构成的集合 A={a_k | 0<=k<n},划分为不相交的 A1、A2。要求先使 |n1-n2| 最小,再使 |S1-S2| 最大。
void splitForMaximumSumDifference(int A[], int n,
int A1[], int *n1,
int A2[], int *n2) {
int target = n / 2;
int low = 0;
int high = n - 1;
while (low <= high) {
int pivotPos = partition(A, low, high);
if (pivotPos == target)
break;
if (pivotPos < target)
low = pivotPos + 1;
else
high = pivotPos - 1;
}
*n1 = target;
*n2 = n - target;
for (int i = 0; i < *n1; i++)
A1[i] = A[i];
for (int i = 0; i < *n2; i++)
A2[i] = A[target + i];
}
复杂度: 基于快速选择,平均时间 O(n)、最坏时间 O(n²);除输出数组外额外空间 O(1)。若使用线性时间的确定性选择算法,可保证最坏时间 O(n)。
核心思路: 为使元素个数之差最小,两组大小必须分别为 floor(n/2) 和 ceil(n/2);在大小确定后,把较小的一半元素放入一组、较大的一半放入另一组,可使两组元素和之差最大。快速选择只需把数组划分到这个边界,无需完整排序。
校订注:原 PDF 的集合下标因排版看起来像
0<=a_k<n,结合原题应为0<=k<n;它约束的是下标,不是元素值。原稿用完整快速排序,平均时间O(n log n),不是本题的更优做法。
回源 · PDF 第 27 页 ·
b_27_4;PDF 第 27 页 · b_27_5