给定一个数组,数组内每一个元素的位置和其最终排序好的位置 的 距离相差在 k 以内,怎么有效的对其排序?要求时间复杂度为 O(n log k)
例如:当k=2时,如果一个元素的位置为 7,那么再最终的排序数组中该元素的位置在 5-9 之间。
方法一 插入排序
比较每个排序算法的特点,会发现插入排序对于这种情况可能会比较好,因为插入元素时,比较的次数不会超过k。
01 | /* 插入排序 */ |
02 | void insertionSort( int A[], int size) |
03 | { |
04 | int i, key, j; |
05 | for (i = 1; i < size; i++) |
06 | { |
07 | key = A[i]; |
08 | j = i-1; |
09 |
10 | /* 插入一个元素,对于大的后移。最多比较k次 */ |
11 | while (j >= 0 && A[j] > key) |
12 | { |
13 | A[j+1] = A[j]; |
14 | j = j-1; |
15 | } |
16 | A[j+1] = key; |
17 | } |
18 | } |
最多需要k次比较和k个元素后移,因此复杂度为 O(nk)
方法二为堆排序
依然没有达到要求 O(n log k) ,可以使用 堆来继续优化。详细步骤如下:
1 | 1) 用最开始的k+1个元素 创建一个大小为k+1的小顶堆。时间为 O(k) |
2 | 2) 依次从小顶堆中取出最小元素放到结果数组中,然后从剩下的待排序数组中添加新的元素到堆中 |
从堆中取出一个元素并加入新的元素,为花费 O(Log k )的时间。所以总共的时间复杂度为 O(k) + O((n-k)*logK)
代码实现:
001 | #include<iostream> |
002 | using namespace std; |
003 | void swap( int *x, int *y); |
004 |
005 | //小顶堆类 |
006 | class MinHeap |
007 | { |
008 | int *harr; // 堆中的元素数组 |
009 | int heap_size; // 堆的大小 |
010 | public : |
011 | // 构造函数 |
012 | MinHeap( int a[], int size); |
013 |
014 | // 从给定的下标开始调整堆 |
015 | void MinHeapify( int ); |
016 |
017 | //i的左孩子的下标 |
018 | int left( int i) { return (2*i + 1); } |
019 |
020 | // i的右孩子的下标 |
021 | int right( int i) { return (2*i + 2); } |
022 |
023 | //取走最小值(或者说是 root), 并添加新的元素x |
024 | int replaceMin( int x); |
025 |
026 | // 取最小值 |
027 | int extractMin(); |
028 | }; |
029 |
030 | // k为每个元素的当前位置和排序数组位置相差的范围 |
031 | int sortK( int arr[], int n, int k) |
032 | { |
033 | // 用前 (k+1) 个元素创建最小堆 |
034 | int *harr = new int [k+1]; |
035 | for ( int i = 0; i<=k && i<n; i++) // i < n 防止 k>n的情况 |
036 | harr[i] = arr[i]; |
037 | MinHeap hp(harr, k+1); |
038 |
039 | for ( int i = k+1, ti = 0; ti < n; i++, ti++) |
040 | { |
041 | //如果有剩余元素,就替换掉堆顶 |
042 | if (i < n) |
043 | arr[ti] = hp.replaceMin(arr[i]); |
044 | // 否则就依次取走堆顶的元素, |
045 | else |
046 | arr[ti] = hp.extractMin(); |
047 | } |
048 | } |
049 |
050 | // 以下是标准的小顶堆的实现 |
051 | MinHeap::MinHeap( int a[], int size) |
052 | { |
053 | heap_size = size; |
054 | harr = a; |
055 | int i = (heap_size - 1)/2; |
056 | while (i >= 0) |
057 | { |
058 | MinHeapify(i); //构建初始堆 |
059 | i--; |
060 | } |
061 | } |
062 |
063 | //从小顶堆中移除堆顶元素 |
064 | int MinHeap::extractMin() |
065 | { |
066 | int root = harr[0]; |
067 | if (heap_size > 1) |
068 | { |
069 | harr[0] = harr[heap_size-1]; |
070 | heap_size--; |
071 | MinHeapify(0); |
072 | } |
073 | return root; |
074 | } |
075 |
076 | // 替换root和给定的x |
077 | int MinHeap::replaceMin( int x) |
078 | { |
079 | int root = harr[0]; |
080 | harr[0] = x; |
081 | if (root < x) |
082 | MinHeapify(0); |
083 | return root; |
084 | } |
085 |
086 | // 调整小顶堆(递归的方法) |
087 | void MinHeap::MinHeapify( int i) |
088 | { |
089 | int l = left(i); |
090 | int r = right(i); |
091 | int smallest = i; |
092 | if (l < heap_size && harr[l] < harr[i]) |
093 | smallest = l; |
094 | if (r < heap_size && harr[r] < harr[smallest]) |
095 | smallest = r; |
096 | if (smallest != i) |
097 | { |
098 | swap(&harr[i], &harr[smallest]); |
099 | MinHeapify(smallest); |
100 | } |
101 | } |
102 |
103 | // 交换 |
104 | void swap( int *x, int *y) |
105 | { |
106 | int temp = *x; |
107 | *x = *y; |
108 | *y = temp; |
109 | } |
110 |
111 | //打印数组 |
112 | void printArray( int arr[], int size) |
113 | { |
114 | for ( int i=0; i < size; i++) |
115 | cout << arr[i] << " " ; |
116 | cout << endl; |
117 | } |
118 |
119 | // 测试 |
120 | int main() |
121 | { |
122 | int k = 3; |
123 | int arr[] = {2, 6, 3, 12, 56, 8}; |
124 | int n = sizeof (arr)/ sizeof (arr[0]); |
125 | sortK(arr, n, k); |
126 |
127 | cout << "Following is sorted array\n" ; |
128 | printArray (arr, n); |
129 |
130 | return 0; |
131 | } |