Skip to content

Commit

Permalink
separate the solution into two implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
AchillesMiller committed Feb 1, 2022
1 parent f13ca2d commit 17a0b80
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 148 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
class Solution {
public:
std::vector<int> countSmaller(std::vector<int> &nums)
{
// when iterating to index i, we want to efficiently
// know how many elements on the right are less than nums[i]

// it may be impossible to get to know this information in O(1)
// therefore, O(logn) is the best achievement.

// considering O(logn), there are two data structure candidates

// binary indexed tree and segment tree

// since, we only want to count element of which value < nums[i]
// instead of ? < value < nums[i]

// binary indexed tree is the choice.

if (nums.empty())
return std::vector<int>();

auto [min_it, max_it] = std::minmax_element(nums.begin(), nums.end());

// mapping *min_it to be 1
int delta = 1 - *min_it;

int min = *min_it + delta;
int max = *max_it + delta;

// There are 2107 trees on the dota 2 map.
binary_indexed_tree tree(max);

std::vector<int> counts(nums.size(), 0);

for (int i = nums.size()-1; i >= 0; --i)
{
// query how many elements are less than nums[i]
int num = nums[i] + delta;
counts[i] = tree.query(num-1);

tree.update(num, 1);
}

return counts;
}

private:
struct binary_indexed_tree
{
std::vector<int> _arr;

binary_indexed_tree(int max_val)
: _arr(max_val+1, 0)
{
// for binary indexed tree, index 0 means nothing,
// therefore, if we want to store the information of max_val
// we need an array of which the length is equal to max_val+1
}

/** @brief query prefix sum of range [0, i] of internal array
* @param: i int inclusive end pos of prefix sum array
* @return: prefix sum value
**/
int query(int i)
{
assert (i < _arr.size());

int ret = 0;

for (auto j = i; j > 0; j -= lowbit(j))
ret += _arr[j];

return ret;
}

void update(int i, int val)
{
assert (i > 0); // logical error to touch array[i]
assert (i < _arr.size());

for (auto j = i; j < _arr.size(); j += lowbit(j))
_arr[j] += val;
}

int lowbit(int i) { return i & (-i); }
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,90 +33,4 @@ class Solution {
// 如果写归并排序的code会更快一些。这里就偷懒了,直接用sort函数。
sort(sortedNums.begin()+start,sortedNums.begin()+end+1);
}

std::vector<int> count_smaller_binary_indexed_tree(std::vector<int> &nums)
{
// when iterating to index i, we want to efficiently
// know how many elements on the right are less than nums[i]

// it may be impossible to get to know this information in O(1)
// therefore, O(logn) is the best achievement.

// considering O(logn), there are two data structure candidates

// binary indexed tree and segment tree

// since, we only want to count element of which value < nums[i]
// instead of ? < value < nums[i]

// binary indexed tree is the choice.

if (nums.empty())
return std::vector<int>();

auto [min_it, max_it] = std::minmax_element(nums.begin(), nums.end());

// mapping *min_it to be 1
int delta = 1 - *min_it;

int min = *min_it + delta;
int max = *max_it + delta;

// There are 2107 trees on the dota 2 map.
binary_indexed_tree tree(max);

std::vector<int> counts(nums.size(), 0);

for (int i = nums.size()-1; i >= 0; --i)
{
// query how many elements are less than nums[i]
int num = nums[i] + delta;
counts[i] = tree.query(num-1);

tree.update(num, 1);
}

return counts;
}

private:
struct binary_indexed_tree
{
std::vector<int> _arr;

binary_indexed_tree(int max_val)
: _arr(max_val+1, 0)
{
// for binary indexed tree, index 0 means nothing,
// therefore, if we want to store the information of max_val
// we need an array of which the length is equal to max_val+1
}

/** @brief query prefix sum of range [0, i] of internal array
* @param: i int inclusive end pos of prefix sum array
* @return: prefix sum value
**/
int query(int i)
{
assert (i < _arr.size());

int ret = 0;

for (auto j = i; j > 0; j -= lowbit(j))
ret += _arr[j];

return ret;
}

void update(int i, int val)
{
assert (i > 0); // logical error to touch array[i]
assert (i < _arr.size());

for (auto j = i; j < _arr.size(); j += lowbit(j))
_arr[j] += val;
}

int lowbit(int i) { return i & (-i); }
};
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include "gtest/gtest.h"
#include <vector>
#include <algorithm>
using namespace std;

#include <unistd.h>
#include <ctime>

#include <chrono> // benchmark

#include <315.binary-indexed-tree.h>
#include <315.divided-conquer.h>

#include <cstdlib> // atexit
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>


static std::vector<int>
count_of_smaller_numbers_after_self_dc(std::vector<int> &nums)
{
static void * handle = dlopen("@DC_315_SO@", RTLD_LAZY);
assert (handle);

static auto counter =
(std::vector<int> (*)(std::vector<int> &))dlsym(handle, "count_v1");

// handle keep alive, no need to release

return counter(nums);
}

static std::vector<int>
count_of_smaller_numbers_after_self_bit(std::vector<int> &nums)
{
static void * handle = dlopen("@BIT_315_SO@", RTLD_LAZY);

assert (handle);

static auto counter =
(std::vector<int> (*)(std::vector<int> &))dlsym(handle, "count_v2");

// handle keep alive, no need to release

return counter(nums);
}

TEST(count_of_smaller_numbers_after_self, consistency)
{
std::vector<int> nums{5,2,6,1};
std::vector<int> expected{2,1,1,0};

ASSERT_EQ(count_of_smaller_numbers_after_self_dc(nums), expected);
ASSERT_EQ(count_of_smaller_numbers_after_self_bit(nums), expected);
}

TEST(count_of_smaller_numbers_after_self, benchmark)
{
std::vector<int> nums(1e5, 0);

for (auto &num: nums)
num = rand() % static_cast<int>(2 * 1e4) - 1e4;

std::vector<int> counts1;
std::vector<int> counts2;

counts1.reserve(1e5);
counts2.reserve(1e5);

{
auto start = chrono::steady_clock::now();
counts1 = count_of_smaller_numbers_after_self_dc(nums);
auto end = chrono::steady_clock::now();

printf("time cost of countSmaller using merge sort is %ld [ms]\n",
chrono::duration_cast<chrono::milliseconds>(end - start).count());
}

{
auto start = chrono::steady_clock::now();
counts2 = count_of_smaller_numbers_after_self_bit(nums);
auto end = chrono::steady_clock::now();

printf("time cost of countSmaller using binary indexed tree is %ld [ms]\n",
chrono::duration_cast<chrono::milliseconds>(end - start).count());
}

ASSERT_EQ(counts1, counts2);
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <315.binary-indexed-tree.h>
#include <Divide_Conquer/315.Count-of-Smaller-Numbers-After-Self/315.Count-of-Smaller-Numbers-After-Self-v2.cpp>

#include <stdio.h>

std::vector<int> count_v2(std::vector<int> &nums)
{
static Solution s;
return s.countSmaller(nums);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef TESTS_DIVIDE_CONQUER_315_BINARY_INDEXED_TREE
#define TESTS_DIVIDE_CONQUER_315_BINARY_INDEXED_TREE

#include <vector>
#include <algorithm>
using namespace std;
#include <assert.h>

// binary indexed tree
extern "C" { std::vector<int> count_v2(std::vector<int> &nums); }

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <315.divided-conquer.h>
#include <Divide_Conquer/315.Count-of-Smaller-Numbers-After-Self/315.Count-of-Smaller-Numbers-After-Self.cpp>

#include <stdio.h>

std::vector<int> count_v1(std::vector<int> &nums)
{
static Solution s;
return s.countSmaller(nums);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef TESTS_DIVIDE_CONQUER_315_DIVIDE_CONQUER
#define TESTS_DIVIDE_CONQUER_315_DIVIDE_CONQUER

#include <vector>
#include <algorithm>
using namespace std;
#include <assert.h>

// divided conquer
extern "C" { std::vector<int> count_v1(std::vector<int> &nums); }

#endif
Loading

0 comments on commit 17a0b80

Please sign in to comment.