问题描述:输入N的元素以及一个整数k,要求是找出第k大的元素。
一,普通排序:
算法1A:
将所有元素读入数组并且从大到小排序,然后直接返回第K个元素。如果使用简单的排序算法,运行时间就是O(N^2)。很不好。
1
//从大到小排序
2
int choose_1A(VI& in, int k)
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
4
int count = in.size();
5
for (int i = 0; i < count; ++i)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
7
for (int j = i; j < count; ++j)
8
if(in[i] < in[j])
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
10
swap(in[i], in[j]);
11
}
12
}
13
return in[k-1];
14
}
算法1B:
先将k个元素读入一个数组并且将其从大到小排序,这样的话这k个元素的最小者就在第k个位置上。然后我们依次的处理其余的元素。处理一个元素的时候,先将此元素跟数组中的第k个元素进行比较,如果此元素大就将第k个元素删除,而将这个新的元素放在其余k-1个元素间的正确的位置上。算法结束时,第k个元素上就是正确的答案。此方法的运行时间就是O(k*k+(N-k)*k)=O(N*k)。但是如果当K=[N/2]的时候(找中位数时),这种算法也是O(N^2)的。
1
//先排k个
2
int choose_1B(VI& in, int k)
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
4
int count = in.size();
5
FORR(i, 0, k)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
7
FORR(j, i, k)
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9
if (in[i] < in[j])
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
11
swap(in[i], in[j]);
12
}
13
}
14
}
15
list<int> lst_k(in.begin(), in.begin()+k);
16![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
17
FORR(i, k, count)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
19
if (in[i] > lst_k.back())
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
21
for (list<int>::iterator it = lst_k.begin(); it != lst_k.end(); ++it)
22![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
23
if (in[i] > *it)
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
25
lst_k.insert(it, in[i]);
26
break;
27
}
28
}
29
lst_k.pop_back();
30
}
31
}
32
return lst_k.back();
33
}
34![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
二,使用优先队列(二叉堆)
算法2A:
简单起见现在我们把问题改一下:找出第k个最小的元素。算法是,先将N个元素读入一个数组,然后对此数组应用buildHeap算法。最后执行k次deleteMin操作。这时候从该堆中取出的就是正确答案。这个算法显然是正确的,如果使用buildHeap,构造堆的最坏情形就是O(N)的时间,而每次deleteMin的时间是O(logN)的时间。由于是k次deleteMin,则总时间是O(N+klogN)。当k=[N/2]的时候,运行时间就是O(NlogN)(找中位数时间)。其实注意的是,如果令k=N,那么实际上就是对输入文件以时间O(NlogN)进行了排序。这就是堆排序。
//优先队列O(N + k*logN)
int choose_2A(VI& in, int k)
{
priority_queue<int> maxPQ(in.begin(), in.end());
FORR(i, 0, k-1)
{
maxPQ.pop();
}
return maxPQ.top();
}
算法2B:
使用算法1B的思路,在任意一个时刻,都将维持k个最大元素的集合S。在前k个元素读入以后,再读入一个新的元素时候,该元素将会和第k个最大最大元素进行比较,设其为Sk(是S中最小的)。如果新的元素大于Sk,那么就用新元素代替S中的Sk。这个时候S中就会有一个新的最小元素,可能是新添加的也可能不是。输入完成时,找到S中的最小元素,返回就是问题的答案。
上面看来基本和算法1B差不多,不过这里我们用一个堆来实现S了(上面我是用的链表)。通过buildHeap将前k个元素以总时间O(k)放到堆中。处理其余每个元素时间只要O(1)+O(logk)。总时间是O(Nlogk)。当k=[N/2]时候即找中位数时间也是O(NlogN)。
1
//1B算法加上二叉堆
2
int choose_2B(const VI& in, int k)
3![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
4
int count = in.size();
5
//下面定义的是最小堆(即最小的在最顶部)
6
priority_queue<int, vector<int>, greater<int>> minPQ(in.begin(), in.begin()+k);
7
FORR(i, k, count)
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9
if (in[i] > minPQ.top())
10![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
11
minPQ.pop();
12
minPQ.push(in[i]);
13
}
14
}
15
return minPQ.top();
16
}
17![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
三,使用快速排序思想
算法3A:快速选择
这种算法称为快速选择,令|Si|是Si中的元素的个数,那么快速选择的步骤是:
(1)如果|S|=1,那么k=1并且将S中的元素作为答案返回。如果我们选用小数组的截止方法,则当|S|<=CUTOFF的时候将S排序并且返回第k个最小元。
(2)在S中选取一个枢纽元。
(3)将集合S-{v}分割成S1和S2,就像快速排序中所做的那样。
(4)如果k<=|S1|,那么第k个最小元必定在S1中。在这种情况下,返回quickselect(S1,k)。如果k=1+|S1|那么枢纽元就是第k个最小元,将它作为答案返回。否则第k个最小元就在S2中,它是S2中的第(k-|S1|-1) 个最小元。进行一次递归调用返回quickselect(S2,k-|S1|-1)。
跟快速排序相比,快速选择只是进行了一次递归调用不是两次。快速选择的最快情形也是O(N^2),不过平均时间是O(N)。
1
const int& median3(VI& a, int left, int right)
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
int center = (left+right)/2;
4
if (a[center] < a[left])
5
swap(a[left], a[center]);
6
if (a[right] < a[left])
7
swap(a[left], a[right]);
8
if (a[right] < a[center])
9
swap(a[center], a[right]);
10![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11
swap(a[center], a[right-1]);
12
return a[right-1];
13
}
14![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
15
//快速选择,注意这里我们找到的是第k小的元素
16
void quickSelect(VI& in, int left, int right, int k)
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
18
if (left < right)
19![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
20
int pivot = median3(in, left, right);
21
int i = left, j = right-1;
22
23
while(true)
24![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(in[++i] < pivot)
{}
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(pivot < in[--j])
{}
27
28
if (i < j)
29
swap(in[i], in[j]);
30
else
31
break;
32
}
33![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
34
if(i < right-1)
35
swap(in[i], in[right-1]);
36![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
37
if (k <= i)
38
quickSelect(in, left, i-1, k);
39
else if(k > i+1)
40
quickSelect(in, i+1, right, k);
41
}
42
else
43
return;
44
}
45![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
46
int choose_3A(VI& in, int k)
47![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
48
int count = in.size();
49
quickSelect(in, 0, count-1, k);
50
return in[k-1];
51
}
52![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
算法3B:
是一个最坏为O(n)的算法,要准备考研了,没时间研究了,以后再说吧~~
全部源代码:
1
#include <iostream>
2
#include <sstream>
3
#include <algorithm>
4
#include <string>
5
#include <map>
6
#include <set>
7
#include <list>
8
#include <stack>
9
#include <queue>
10
#include <cctype>
11
#include <vector>
12
#include <bitset>
13
#include <cmath>
14
//#include <hash_map>
15![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
16
#define FORR(i, a, b) for(int i = a; i < b; ++i)
17
#define BE(x) x.begin(),x.end()
18
#define MP(a, b) make_pair(a, b)
19![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
20
using namespace std;
21
//using namespace stdex;
22![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
23
typedef vector<string> VS;
24
typedef vector<int> VI;
25![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
26
//从大到小排序O(N^2)
27
int choose_1A(VI& in, int k)
28![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
29
int count = in.size();
30
for (int i = 0; i < count; ++i)
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
32
for (int j = i; j < count; ++j)
33
if(in[i] < in[j])
34![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
35
swap(in[i], in[j]);
36
}
37
}
38
return in[k-1];
39
}
40![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
41
//先排k个,O(K*N)
42
int choose_1B(VI& in, int k)
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
44
int count = in.size();
45
FORR(i, 0, k)
46![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
47
FORR(j, i, k)
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
49
if (in[i] < in[j])
50![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
51
swap(in[i], in[j]);
52
}
53
}
54
}
55
list<int> lst_k(in.begin(), in.begin()+k);
56![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
57
FORR(i, k, count)
58![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
59
if (in[i] > lst_k.back())
60![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
61
for (list<int>::iterator it = lst_k.begin(); it != lst_k.end(); ++it)
62![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
63
if (in[i] > *it)
64![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
65
lst_k.insert(it, in[i]);
66
break;
67
}
68
}
69
lst_k.pop_back();
70
}
71
}
72
return lst_k.back();
73
}
74![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
75
//优先队列O(N + k*logN)
76
int choose_2A(const VI& in, int k)
77![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
78
priority_queue<int> maxPQ(in.begin(), in.end());
79![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
80
int count = maxPQ.size();
81![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
82
FORR(i, 0, k-1)
83![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
84
maxPQ.pop();
85
}
86
return maxPQ.top();
87
}
88![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
89
//1B算法加上二叉堆
90
int choose_2B(const VI& in, int k)
91![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
92
int count = in.size();
93
//下面定义的是最小堆(即最小的在最顶部)
94
priority_queue<int, vector<int>, greater<int>> minPQ(in.begin(), in.begin()+k);
95
FORR(i, k, count)
96![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
97
if (in[i] > minPQ.top())
98![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
99
minPQ.pop();
100
minPQ.push(in[i]);
101
}
102
}
103
return minPQ.top();
104
}
105![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
106
const int& median3(VI& a, int left, int right)
107![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
108
int center = (left+right)/2;
109
if (a[center] < a[left])
110
swap(a[left], a[center]);
111
if (a[right] < a[left])
112
swap(a[left], a[right]);
113
if (a[right] < a[center])
114
swap(a[center], a[right]);
115![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
116
swap(a[center], a[right-1]);
117
return a[right-1];
118
}
119![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
120
//快速选择,注意这里我们找到的是第k小的元素
121
void quickSelect(VI& in, int left, int right, int k)
122![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
123
if (left < right)
124![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
125
int pivot = median3(in, left, right);
126
int i = left, j = right-1;
127
128
while(true)
129![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
130![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(in[++i] < pivot)
{}
131![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
while(pivot < in[--j])
{}
132
133
if (i < j)
134
swap(in[i], in[j]);
135
else
136
break;
137
}
138![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
139
if(i < right-1)
140
swap(in[i], in[right-1]);
141![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
142
if (k <= i)
143
quickSelect(in, left, i-1, k);
144
else if(k > i+1)
145
quickSelect(in, i+1, right, k);
146
}
147
else
148
return;
149
}
150![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
151
int choose_3A(VI& in, int k)
152![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
153
int count = in.size();
154
quickSelect(in, 0, count-1, k);
155
return in[k-1];
156
}
157![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
158![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
159![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
160
int main()
161![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
162![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
int a[] =
{45, 34, 12, 9 ,5, 23, 34, 32, 5, 10, 56, 29, 14, 2};
163
VI input(a, a+14);
164![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
165
int k = 8;
166![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
167
//cout << choose_1A(input, k) << endl;
168
//print(input);
169
//cout << endl;
170
//cout << choose_1B(input, k) << endl;
171
//cout << choose_2A(input, k) << endl;
172
//cout << choose_2B(input, k) << endl;
173![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
174
//cout << choose_3A(input, k) << endl;
175![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
176
return 0;
177
}