无序数组寻找中位数
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 }
。
以上是关于无序数组寻找中位数的主要内容,如果未能解决你的问题,请参考以下文章