Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1047e1b

Browse files
committedJan 26, 2022
implement 315 using binary indexed tree
1 parent 9ff360b commit 1047e1b

File tree

6 files changed

+159
-2
lines changed

6 files changed

+159
-2
lines changed
 

‎CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
22

33
project(LeetCode VERSION 1.0)
44

5-
set (CMAKE_CXX_STANDARD 11)
5+
set (CMAKE_CXX_STANDARD 17)
66

77
include_directories(PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
88

‎Divide_Conquer/315.Count-of-Smaller-Numbers-After-Self/315.Count-of-Smaller-Numbers-After-Self.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,90 @@ class Solution {
3333
// 如果写归并排序的code会更快一些。这里就偷懒了,直接用sort函数。
3434
sort(sortedNums.begin()+start,sortedNums.begin()+end+1);
3535
}
36+
37+
std::vector<int> count_smaller_binary_indexed_tree(std::vector<int> &nums)
38+
{
39+
// when iterating to index i, we want to efficiently
40+
// know how many elements on the right are less than nums[i]
41+
42+
// it may be impossible to get to know this information in O(1)
43+
// therefore, O(logn) is the best achievement.
44+
45+
// considering O(logn), there are two data structure candidates
46+
47+
// binary indexed tree and segment tree
48+
49+
// since, we only want to count element of which value < nums[i]
50+
// instead of ? < value < nums[i]
51+
52+
// binary indexed tree is the choice.
53+
54+
if (nums.empty())
55+
return std::vector<int>();
56+
57+
auto [min_it, max_it] = std::minmax_element(nums.begin(), nums.end());
58+
59+
// mapping *min_it to be 1
60+
int delta = 1 - *min_it;
61+
62+
int min = *min_it + delta;
63+
int max = *max_it + delta;
64+
65+
// There are 2107 trees on the dota 2 map.
66+
binary_indexed_tree tree(max);
67+
68+
std::vector<int> counts(nums.size(), 0);
69+
70+
for (int i = nums.size()-1; i >= 0; --i)
71+
{
72+
// query how many elements are less than nums[i]
73+
int num = nums[i] + delta;
74+
counts[i] = tree.query(num-1);
75+
76+
tree.update(num, 1);
77+
}
78+
79+
return counts;
80+
}
81+
82+
private:
83+
struct binary_indexed_tree
84+
{
85+
std::vector<int> _arr;
86+
87+
binary_indexed_tree(int max_val)
88+
: _arr(max_val+1, 0)
89+
{
90+
// for binary indexed tree, index 0 means nothing,
91+
// therefore, if we want to store the information of max_val
92+
// we need an array of which the length is equal to max_val+1
93+
}
94+
95+
/** @brief query prefix sum of range [0, i] of internal array
96+
* @param: i int inclusive end pos of prefix sum array
97+
* @return: prefix sum value
98+
**/
99+
int query(int i)
100+
{
101+
assert (i < _arr.size());
102+
103+
int ret = 0;
104+
105+
for (auto j = i; j > 0; j -= lowbit(j))
106+
ret += _arr[j];
107+
108+
return ret;
109+
}
110+
111+
void update(int i, int val)
112+
{
113+
assert (i > 0); // logical error to touch array[i]
114+
assert (i < _arr.size());
115+
116+
for (auto j = i; j < _arr.size(); j += lowbit(j))
117+
_arr[j] += val;
118+
}
119+
120+
int lowbit(int i) { return i & (-i); }
121+
};
36122
};

‎tests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ FetchContent_MakeAvailable(googletest)
1010
include(GoogleTest)
1111

1212
add_subdirectory(BFS)
13-
add_subdirectory(Dynamic_Programming)
13+
add_subdirectory(Divide_Conquer)
14+
add_subdirectory(Dynamic_Programming)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "gtest/gtest.h"
2+
#include <vector>
3+
#include <algorithm>
4+
using namespace std;
5+
6+
#include <unistd.h>
7+
#include <ctime>
8+
9+
#include <chrono> // benchmark
10+
11+
12+
#include <Divide_Conquer/315.Count-of-Smaller-Numbers-After-Self/315.Count-of-Smaller-Numbers-After-Self.cpp>
13+
14+
TEST(count_of_smaller_numbers_after_self, consistency)
15+
{
16+
Solution s;
17+
18+
std::vector<int> nums{5,2,6,1};
19+
std::vector<int> expected{2,1,1,0};
20+
21+
ASSERT_EQ(s.countSmaller(nums), expected);
22+
ASSERT_EQ(s.count_smaller_binary_indexed_tree(nums), expected);
23+
}
24+
25+
TEST(count_of_smaller_numbers_after_self, benchmark)
26+
{
27+
std::vector<int> nums(1e5, 0);
28+
29+
for (auto &num: nums)
30+
num = rand() % static_cast<int>(2 * 1e4) - 1e4;
31+
32+
Solution s;
33+
std::vector<int> counts1;
34+
std::vector<int> counts2;
35+
36+
counts1.reserve(1e5);
37+
counts2.reserve(1e5);
38+
39+
{
40+
auto start = chrono::steady_clock::now();
41+
counts1 = s.countSmaller(nums);
42+
auto end = chrono::steady_clock::now();
43+
44+
printf("time cost of countSmaller using merge sort is %ld [ms]\n",
45+
chrono::duration_cast<chrono::milliseconds>(end - start).count());
46+
}
47+
48+
{
49+
auto start = chrono::steady_clock::now();
50+
counts2 = s.count_smaller_binary_indexed_tree(nums);
51+
auto end = chrono::steady_clock::now();
52+
53+
printf("time cost of countSmaller using binary indexed tree is %ld [ms]\n",
54+
chrono::duration_cast<chrono::milliseconds>(end - start).count());
55+
}
56+
57+
ASSERT_EQ(counts1, counts2);
58+
}
59+
60+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
set(BINARY count_of_smaller_numbers_after_self)
2+
3+
add_executable(${BINARY} 315.Count-of-Smaller-Numbers-After-Self.t.cpp)
4+
5+
target_link_libraries(${BINARY} gtest_main)
6+
7+
include(GoogleTest)
8+
9+
gtest_discover_tests(${BINARY})

‎tests/Divide_Conquer/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(315.Count-of-Smaller-Numbers-After-Self)

0 commit comments

Comments
 (0)
Please sign in to comment.