6-11 求自定類型元素序列的中位數 (25分)
本題要求實現一個函數,求N
個集合元素A[]
的中位數,即序列中第⌊(N+1)/2⌋
大的元素。其中集合元素的類型爲自定義的ElementType
。
函數接口定義:
ElementType Median( ElementType A[], int N );
其中給定集合元素存放在數組A[]中,正整數N是數組元素個數。該函數須返回N個A[]元素的中位數,其值也必須是ElementType類型。
裁判測試程序樣例:
#include <stdio.h>
#define MAXN 10
typedef float ElementType;
ElementType Median( ElementType A[], int N );
int main ()
{
ElementType A[MAXN];
int N, i;
scanf("%d", &N);
for ( i=0; i<N; i++ )
scanf("%f", &A[i]);
printf("%.2f\n", Median(A, N));
return 0;
}
/* 你的代碼將被嵌在這裏 */
- 輸入樣例:
3 12.3 34 -5
- 輸出樣例:
12.30
思路:我試了很多種排序方法。而且也有疑惑,看其他人做的題,都是取序列中第⌊N/2+1⌋大的元素
,而非序列中第⌊(N+1)/2⌋
大的元素,難道題目改錯了嗎?
在C中,序列從小到大排序,第⌊N/2+1⌋
大大對應位序爲N/2的元素,第⌊(N+1)/2⌋
大對應(N+1)/2-1位序的元素,兩者幾乎總是不同,N爲奇數時相等,偶數時N/2 > (N+1)/2-1結果 大1。
從後面的實驗來看,符合第幾大的應該是(N+1)/2-1。
// 最初級的冒泡排序
void SimpleSort(ElementType A[], int N) {
for (int i = 0; i < N - 1; ++i) {
for (int j = i + 1; j < N; ++j) {
if (A[j] < A[i]) { // 從小到大 N/2
ElementType t = A[j];
A[j] = A[i];
A[i] = t;
}
}
}
}
ElementType Median(ElementType A[], int N) {
SimpleSort(A, N);
return A[N/2];
}
改爲從小到大 (N+1)/2-1:
因此,N/2 從大到小,偶數個時1,4測試點出現錯誤,從小到大則不出。
而(N+1)/2-1則相反,從小到大,偶數個時1,4測試點出現錯誤,從大到小則不出。
即偶數個時,該結果位於從大到小的中間偏左,從小到大的中間偏右。
// 冒泡排序
void BubbleSort(ElementType A[], int N) {
for (int i = 0; i < N - 1; ++i) {
for (int j = N - 1; j > i; --j) {
if (A[j] > A[j - 1]) { // 從大到小
ElementType t = A[j];
A[j] = A[j - 1];
A[j - 1] = t;
}
}
}
}
ElementType Median(ElementType A[], int N) {
BubbleSort(A, N);
return A[N / 2];
}
改爲(N+1)/2-1後:
// 插入排序
void InsertionSort(ElementType* A, int N) {
for (int i = 1; i < N; ++i) {
ElementType temp = A[i];
int j;
for (j = i; j > 0 && A[j - 1] > temp; --j) A[j] = A[j - 1]; // 從小到大
A[j] = temp;
}
}
ElementType Median(ElementType A[], int N) {
InsertionSort(A, N);
return A[N / 2];
}
void InsertionSort(ElementType* A, int N) {
for (int i = 1; i < N; ++i) {
ElementType temp = A[i];
int j;
for (j = i; j > 0 && A[j - 1] < temp; --j) A[j] = A[j - 1]; // 從大到小
A[j] = temp;
}
}
int cmp(const void *a, const void *b) {
return (*(ElementType*)b - *(ElementType*)a);
}
ElementType Median(ElementType A[], int N) {
InsertionSort(A, N);
return A[(N + 1) / 2 - 1];
}
// 手寫快排
int Partition(ElementType* A, int lo, int hi) {
if (lo >= hi) return lo; // 只有1個
ElementType t = A[lo];
while (lo < hi) { // 夾出一個位置
while (lo < hi && A[hi] <= t) --hi; // 從大到小排序
A[lo] = A[hi];
while (lo < hi && A[lo] > t) ++lo; // 從大到小排序
A[hi] = A[lo];
}
A[lo] = t; // 從while中退出時,lo等於hi,lo的元素已經使用過了
return lo;
}
void QSort(ElementType* A, int lo, int hi) {
if (lo < hi) {
int mid = Partition(A, lo, hi);
QSort(A, lo, mid);
QSort(A, mid + 1, hi);
}
}
void QuickSort(ElementType* A, int N) {
QSort(A, 0, N - 1);
}
ElementType Median(ElementType A[], int N) {
QuickSort(A, N);
return A[(N + 1) / 2 - 1];
}
// 引用C庫裏的快排
// 不知道爲什麼這裏也會錯,不能理解
#include <stdlib.h>
int cmp(const void *a, const void *b) {
return (*(ElementType*)b - *(ElementType*)a); // 從大到小
}
ElementType Median(ElementType A[], int N) {
qsort(A, N, sizeof(ElementType), cmp); // (N + 1) / 2 - 1
return A[(N + 1) / 2 - 1];
}
// 引用C庫裏的快排
// 不知道爲什麼這裏也會錯,不能理解
#include <stdlib.h>
int cmp(const void *a, const void *b) {
return (*(ElementType*)a - *(ElementType*)b); // 從小到大
}
ElementType Median(ElementType A[], int N) {
qsort(A, N, sizeof(ElementType), cmp); // (N + 1) / 2 - 1
return A[N / 2];
}
真正的答案是用希爾排序,堆排序沒試過:
ElementType Median(ElementType A[], int N) {
// gap是每次排序分組的間隔,每次間隔縮小兩倍(其他縮小辦法也可以)
for (int gap = N / 2; gap > 0; gap /= 2) {
for (int i = gap; i < N; ++i) { // 相當於在同一組內採用直接插入排序
int j = i;
ElementType t = A[i];
for (; j >= gap && t > A[j - gap]; j -= gap) // 從大到小
A[j] = A[j - gap];
A[j] = t;
}
}
return A[(N + 1) / 2 - 1];
}
ElementType Median(ElementType A[], int N) {
for (int gap = N / 2; gap > 0; gap /= 2) {
for (int i = gap; i < N; ++i) {
int j = i - gap;
ElementType t = A[i];
for (; j >= 0 && t < A[j]; j -= gap) // 從小到大
A[j + gap] = A[j];
A[j + gap] = t;
}
}
return A[N / 2];
}