Skip to content

Commit 824f11c

Browse files
committed
1 parent 691816d commit 824f11c

File tree

3 files changed

+183
-1
lines changed

3 files changed

+183
-1
lines changed

lib/cpalgo/util/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,14 @@ Arithmetic in Modulo
5050

5151
### References in Japanese
5252
- [「1000000007 で割ったあまり」の求め方を総特集! 〜 逆元から離散対数まで 〜 - Qiita](https://qiita.com/drken/items/3b4fdf0a78e7a138cd9a#8-modint)
53-
- [modint 構造体を使ってみませんか? (C++) - noshi91のメモ](https://noshi91.hatenablog.com/entry/2019/03/31/174006)
53+
- [modint 構造体を使ってみませんか? (C++) - noshi91のメモ](https://noshi91.hatenablog.com/entry/2019/03/31/174006)
54+
55+
56+
## Quick Select
57+
58+
Selects the k-th smallest element of an unsorted array in linear time.
59+
60+
[quick_select | C++ code](quick_select.hpp)
61+
62+
### References in English
63+
- [Median of medians - Wikipedia](https://en.wikipedia.org/wiki/Median_of_medians)

lib/cpalgo/util/quick_select.hpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <stdexcept>
5+
#include <vector>
6+
7+
8+
// Selects the k-th smallest element of an unsorted array.
9+
//
10+
// Complexity:
11+
// Time: O(N)
12+
// Space: O(logN) ~ O(N)
13+
//
14+
template <class T, class Comparator = std::less<T>>
15+
struct QuickSelect {
16+
Comparator less = Comparator();
17+
// Selects the k-th smallest element of the given unsorted array.
18+
// Returns the value of the k-th smallest element.
19+
// k = [0,N)
20+
// Time: O(N)
21+
// Space: O(N)
22+
T operator()(std::vector<T> const& array, size_t k) const {
23+
if (k < 0 || k >= array.size()) throw std::out_of_range("k");
24+
std::vector<T> buffer(array.begin(), array.end());
25+
auto index = index_of(buffer, k);
26+
return buffer[index];
27+
}
28+
// Selects the k-th smallest element of the given unsorted array.
29+
// Returns the index of the k-th smallest element.
30+
// k = [0,N)
31+
// Time: O(N)
32+
// Space: O(logN)
33+
size_t index_of(std::vector<T>& array, size_t k) const {
34+
if (k < 0 || k >= array.size()) throw std::out_of_range("k");
35+
return select(array, 0, array.size(), k);
36+
}
37+
private:
38+
size_t select(std::vector<T>& array, size_t begin, size_t end, size_t k) const {
39+
while (true) {
40+
if (end - begin == 1) {
41+
return begin;
42+
}
43+
auto pivot_index = pivot_of(array, begin, end);
44+
pivot_index = partition(array, begin, end, k, array[pivot_index]);
45+
std::cout << pivot_index << " " << k << std::endl;
46+
if (pivot_index == k) {
47+
return k;
48+
} else if (pivot_index < k) {
49+
begin = pivot_index + 1;
50+
} else {
51+
end = pivot_index;
52+
}
53+
}
54+
}
55+
size_t pivot_of(std::vector<T>& array, size_t begin, size_t end) const {
56+
if (end - begin <= 5) {
57+
return partition5(array, begin, end);
58+
}
59+
size_t n = 0;
60+
for (size_t i = begin; i < end; i += 5) {
61+
auto median = partition5(array, i, std::min(i+5,end));
62+
std::swap(array[median], array[begin + (i-begin)/5]);
63+
++n;
64+
}
65+
auto k = begin + (n-1)/2;
66+
return select(array, begin, begin + n, k);
67+
}
68+
inline size_t partition(std::vector<T>& array, size_t begin, size_t end, size_t k, T pivot_value) const {
69+
auto begin_equal = std::partition(array.begin() + begin, array.begin() + end, [&](auto value) {
70+
return less(value, pivot_value);
71+
});
72+
auto end_equal = std::partition(begin_equal, array.begin() + end, [&](auto value) {
73+
return !less(value, pivot_value) && !less(pivot_value, value);
74+
});
75+
size_t begin_equal_range = distance(array.begin(), begin_equal);
76+
size_t end_equal_range = distance(array.begin(), end_equal);
77+
if (k < begin_equal_range) {
78+
return begin_equal_range;
79+
} else if (k >= end_equal_range) {
80+
return end_equal_range-1;
81+
} else {
82+
return k;
83+
}
84+
}
85+
inline size_t partition5(std::vector<T>& array, size_t begin, size_t end) const {
86+
for (size_t i = begin + 1; i < end; ++i) {
87+
for (size_t j = i; j > begin && less(array[j], array[j-1]); --j) {
88+
std::swap(array[j-1], array[j]);
89+
}
90+
}
91+
return begin + (end - begin - 1) / 2;
92+
}
93+
};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include <bits/stdc++.h>
2+
#include "gtest/gtest.h"
3+
#include "cpalgo/util/quick_select.hpp"
4+
5+
using namespace std;
6+
7+
TEST(QuickSelectTest, ShouldSelectInSmallCases) {
8+
9+
auto select = QuickSelect<int>();
10+
vector<int> n1 = { 1 };
11+
EXPECT_EQ( 1, select(n1, 0));
12+
13+
vector<int> n12 = { 1, 2 };
14+
EXPECT_EQ( 1, select(n12, 0));
15+
EXPECT_EQ( 2, select(n12, 1));
16+
17+
vector<int> n11 = { 1, 1 };
18+
EXPECT_EQ( 1, select(n11, 0));
19+
EXPECT_EQ( 1, select(n11, 1));
20+
21+
vector<int> n112 = { 1, 1, 2 };
22+
EXPECT_EQ( 1, select(n112, 0));
23+
EXPECT_EQ( 1, select(n112, 1));
24+
EXPECT_EQ( 2, select(n112, 2));
25+
26+
}
27+
28+
TEST(QuickSelectTest, ShouldSelect) {
29+
30+
auto select = QuickSelect<int>();
31+
vector<int> values = { 1, 3, 2, 6, 7, 4, 5, 10, 8, 9 };
32+
EXPECT_EQ( 1, select(values, 0));
33+
EXPECT_EQ( 2, select(values, 1));
34+
EXPECT_EQ( 3, select(values, 2));
35+
EXPECT_EQ( 4, select(values, 3));
36+
EXPECT_EQ( 5, select(values, 4));
37+
EXPECT_EQ( 6, select(values, 5));
38+
EXPECT_EQ( 7, select(values, 6));
39+
EXPECT_EQ( 8, select(values, 7));
40+
EXPECT_EQ( 9, select(values, 8));
41+
EXPECT_EQ(10, select(values, 9));
42+
43+
}
44+
45+
TEST(QuickSelectTest, ShouldSelectUsingCustomComparator) {
46+
47+
auto select = QuickSelect<int, greater<int>>();
48+
vector<int> values = { 1, 3, 2, 6, 7, 4, 5, 10, 8, 9 };
49+
EXPECT_EQ(10, select(values, 0));
50+
EXPECT_EQ( 9, select(values, 1));
51+
EXPECT_EQ( 8, select(values, 2));
52+
EXPECT_EQ( 7, select(values, 3));
53+
EXPECT_EQ( 6, select(values, 4));
54+
EXPECT_EQ( 5, select(values, 5));
55+
EXPECT_EQ( 4, select(values, 6));
56+
EXPECT_EQ( 3, select(values, 7));
57+
EXPECT_EQ( 2, select(values, 8));
58+
EXPECT_EQ( 1, select(values, 9));
59+
60+
}
61+
62+
TEST(QuickSelectTest, ShouldSelectInRandomizedCase) {
63+
64+
size_t const N = 1000;
65+
vector<int> values(N);
66+
random_device seed_generator;
67+
mt19937 random(seed_generator());
68+
for (auto &value : values) {
69+
value = abs((int)(random() % 100));
70+
}
71+
auto sorted_values = values;
72+
sort(sorted_values.begin(), sorted_values.end());
73+
74+
auto select = QuickSelect<int>();
75+
for (size_t i = 0; i < N; ++i) {
76+
EXPECT_EQ(sorted_values[i], select(values, i));
77+
}
78+
79+
}

0 commit comments

Comments
 (0)