无序数组寻找中位数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无序数组寻找中位数相关的知识,希望对你有一定的参考价值。

参考技术A 中位数为一个排序数组的中间元素。假设对于一个排序数组A,数组长度为n,如果n为奇数,即中位数为A[(n-1)/2];如果n为偶数,中位数则是(A[n/2]+A[n/2-1])/2。那么对于一个无序数组,应该如何寻找它的中位数?

一种很容易想到的方法就是对无序数组排序,然后可以直接得到该数组的中位数,时间复杂度为O(nlogn)。

我们可以使用快排思想快速找中位数,即先挑选一个数作为标准,以该元素为支点,将数组划分为两部分。这个问题可以抽象化为寻找第K大的数,快排每排完一轮之后左侧都是比他小的元素,右侧都是比他大的元素,那么支点的index(假设为N-1)即为第N大的数。当N == K,我们就找到了第K大的数;当N > K时, 第K大的数在[0,N-1]范围内;当N < K时,第K大的数在[N+1,n-1] (n为数组长度)范围内,利用递归即可找到第K大的数。

具体到寻找中位数,可分为两种情况:如果数组长度为奇数,即为寻找第(n+1//2)大的数;如果为偶数,则为分别寻找第(n//2+1)和第(n//2)大的数,然后求其平均值。具体程序如下:

我们可以构造一个最小堆,通过维护最小堆,即可得到无序数组的中位数。同样地,这个问题可以延伸至求数组的第K小的数。

无序数组中位数

(1) 最小堆算法

首先将数组的前(n+1)/2个元素建立一个最小堆。

然后,对于下一个元素,和堆顶的元素比较,如果小于等于,丢弃之,接着看下一个元素。如果大于,则用该元素取代堆顶,再调整堆,接着看下一个元素。重复这个步骤,直到数组为空。

当数组都遍历完了,那么,堆顶的元素即是中位数。

 1 #include "stdafx.h"
 2 #include <iostream>
 3 using namespace std;
 4 
 5 const int MAX_SIZE = 100;
 6 
 7 class SmallHeap {
 8 
 9 public:
10     SmallHeap() {
11         size = 0;
12     }
13 
14     void add(int val) {
15         if (size >= MAX_SIZE)
16             return;
17 
18         int s = size++;
19         shiftup(s, val);
20     }
21 
22     int peek() {
23         return queue[0];
24     }
25 
26     int poll() {
27         if (size <= 0)
28             return -1;
29 
30         int ret = queue[0];
31         int s = --size;
32         shiftdown(0, queue[s]);
33         queue[s] = 0;
34         return ret;
35     }
36 
37     void shiftup(int s, int val) {
38         while (s > 0) {
39             int parent = (s - 1) / 2;
40             if (queue[parent] < val) {
41                 break;
42             }
43             queue[s] = queue[parent];
44             s = parent;
45 
46         }
47         queue[s] = val;
48     }
49 
50     void shiftdown(int i, int val) {
51         int half = size / 2;
52         while (i < half) {
53             int child = 2 * i + 1;
54             int right = child + 1;
55             if (right < size && queue[child] > queue[right]) {
56                 child = right;
57             }
58             if (val < queue[child]) {
59                 break;
60             }
61             queue[i] = queue[child];
62             i = child;
63         }
64 
65         queue[i] = val;
66     }
67 
68 private:
69     int queue[MAX_SIZE];
70     int size;
71 
72 };
73 
74 int main()
75 {
76     int a[] = {5, 3, 8, 6, 4};
77     int length = sizeof(a) / sizeof(a[0]);
78     int halfLength = length / 2 + 1;
79 
80     SmallHeap* heap = new SmallHeap();
81     for (int i = 0; i < halfLength; ++i) {
82         heap->add(a[i]);
83     }
84     for (int i = halfLength; i < length; ++i) {
85         if (heap->peek() < a[i])
86         {
87             heap->poll();
88             heap->add(a[i]);
89         }
90     }
91 
92     cout << heap->peek() << endl;
93 
94     int i;
95     cin >> i;
96 
97     return 0;
98 }

 (2) 快排的分而治之

任意挑一个元素,以该元素为支点,将数组分成两部分,左部分是小于等于支点的,右部分是大于支点的。如果你运气好,左部分正好是(n-1)/2个元素,那么支点的那个数就是中位数。否则的话,再找某一边继续排

 1 #include "stdafx.h"
 2 #include <iostream>
 3 using namespace std;
 4 
 5 void swap(int a[], int i, int j) {
 6     int temp = a[i];
 7     a[i] = a[j];
 8     a[j] = temp;
 9 }
10 
11 int partition(int arr[], int low, int high) {
12     int pivot = arr[low];
13     int i = low, j = high;
14     while (i <= j) {
15         while (i <= j && arr[i] <= pivot)i++;
16         while (i <= j && arr[j] >= pivot)j--;
17         swap(arr, i, j);
18     }
19     swap(arr, low, j);
20     return j;
21 }
22  
23 int findMedian(int arr[], int k, int low, int high) {
24     if (k > high - low + 1) return -1;
25     int pos = partition(arr, low, high);
26     if (pos - low < k - 1) {
27         return findMedian(arr, k - pos - 1, pos + 1, high);
28     }
29     else if (pos - low == k - 1) {
30         return arr[pos];
31     }
32     else {
33         return findMedian(arr, k, low, pos - 1);
34     }
35 }
36 
37 int main()
38 {
39     int arr[] = { 3,5,2,3,5,9,1,2,11,12,13 };
40     int length = sizeof(arr) / sizeof(arr[0]);
41     int res = 0;
42     if (length % 2 == 1) {
43         res = findMedian(arr, (length + 1) / 2, 0, length - 1);
44     }
45     else {
46         res = findMedian(arr, length / 2, 0, length - 1);
47     }
48     cout << res << endl;
49 
50     int i;
51     cin >> i;
52 
53     return 0;
54 }

 

以上是关于无序数组寻找中位数的主要内容,如果未能解决你的问题,请参考以下文章

无序数组的中位数

无序数组中位数

无序数组取中位数

怎么在O(N)时间内求一个无序数组的中位数

有序数组寻找中位数以及寻找K大元素

Leetcode寻找两个有序数组的中位数