leetcode673:why did my solution run out of time? - c++

Leetcode 673:
Given an integer array nums, return the number of longest increasing subsequences.
Notice that the sequence has to be strictly increasing.
And here is my code:
class Solution {
//recursively compute the max increasing subsequence length and number of them at a given start point
//and a Min which means the next value must be at least Min.The result will be recorded on dp and values in
//dp will be checked first
pair<int,int> length_and_count(int ptr,int Min,vector<int>& nums,vector<unordered_map<int,pair<int,int>>>& dp){
if(ptr==nums.size())
return make_pair(0,1);
if(dp[ptr].find(Min)!=dp[ptr].end()){
return dp[ptr][Min];
}
else{
if(nums[ptr]<Min)
return dp[ptr][Min]=length_and_count(ptr+1,Min,nums,dp);
else{
auto pair1=length_and_count(ptr+1,Min,nums,dp),pair2=length_and_count(ptr+1,nums[ptr]+1,nums,dp);
if(pair1.first>++pair2.first)
return dp[ptr][Min]=pair1;
else if(pair1.first<pair2.first)
return dp[ptr][Min]=pair2;
else return dp[ptr][Min]=make_pair(pair1.first,pair1.second+pair2.second);
}
}
}
public:
int findNumberOfLIS(vector<int>& nums) {
vector<unordered_map<int,pair<int,int>>> dp(nums.size());
return length_and_count(0,INT_MIN,nums,dp).second;
}
};
I think my solution is of complexity O(n2), because my dp parameters are the start point of nums and the current max value, which is got from the vector,so the size of dp can not be bigger than the square of the input vector.Since the problem size is less than 2000,my solution should be of 10s of ms.So whats's wrong with my solution?

Thanks for very interesting question! Was glad to implement my own solution for it, and to improve speed of yours.
On test of random input of 2000 elements I improved speed of your solution 8x-9x times. For that I did following things:
Interchanged order of vector and unordered_map, by making dp structure unordered_map<int, shared_ptr<vector<pair<int, int>>>>. This way we can later reuse pointer to Min entry of unordered map, by passing this entry downwards to recursive function.
In dp wrapped vector into std::shared_ptr. This allows to be sure that vector will not change memory location even if unordered map grows.
Instead of doing same unordered map search again with dp[ptr][Min] I did search just once inside single call of function through .find(Min) and reused this iterator later in all other places of a function.
Passed Min_vec pointer down to all recursive calls, so that other function calls don't search inside unordered map again for same Min entry. Just single place was not reusing this pointer, where nums[ptr] + 1 was passed as Min.
Created special lambda function GetMinVec() which finds and returns pointer to Min entry. In case if this entry didn't exist yet I initialize it by allocating shared_ptr and resizing vector to size of nums.size() filled with pair(-1, -1) which signifies empty value.
All steps above boosted your solution by 8x-9x times. Your original solution is located in my code as SolutionOriginal class. While my boosted variant is SolutionBoosted.
Also I created my own solution that is 100x faster than your original and 11x-12x faster than boosted original. This new solution I called SolutionSqrt. It works as follows:
Lets create K = sqrt(N) buckets. Each of K buckets will have around K elements on average at the end.
Each bucket will help us to narrow search of all elements that are smaller than current one. For each new number we will find the bucket index buck_i that says that all buckets with smaller index contain elements not bigger than current.
Inside current bucket buck_i we will do linear search to find all elements that are smaller than current.
Each bucket contains a Heap array that grows by using std::push_heap. This Heap is sorted in such a way that its first element (head) contains largest element.
Bucket's Heap is sorted in such a way that first element has longest chain of numbers located in this heap.
We do Sqrt(N) + Sqrt(N) searches on average for each new number. And we search how can we extend current longest chain by adding this new element.
If longest chain is succeded to be prolonged then we remember new maximal length and new maximal count of current bucket buck_i. Also then new element becomes first Heap element as providing the longest chain.
Each bucket has its own length of longest chain and count of such chains formed.
Overall complexity of such algorithm is O(N * Sqrt(N)). This complexity can be improved even further if instead of 2-level buckets we make 3-level buckets in this case we will achieve complexity O(N * N^(1/3)). This O(N * Sqrt(N)) complexity is much less than original OP's code that is O(N^2).
I did timings of all 3 variants of solutions on random test set of 2000 numbers. Timings can be seen after the code.
Try it online!
#include <cstdint>
#include <vector>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <limits>
class SolutionSqrt {
public:
using u64 = uint64_t;
using NumT = int;
u64 findNumberOfLIS(std::vector<NumT> const & nums) {
size_t const
N = nums.size(),
nbucks = std::max<size_t>(1, std::llround(std::sqrt(N))),
buck_size = (N + nbucks - 1) / nbucks;
std::vector<size_t> si(N), rsi(N), ll(N);
std::vector<u64> lc(N);
for (size_t i = 0; i < si.size(); ++i)
si[i] = i;
std::sort(si.begin(), si.end(), [&](size_t a, size_t b){
return nums[a] < nums[b];
});
for (size_t i = 0; i < rsi.size(); ++i)
rsi[si[i]] = i;
struct Buck {
std::vector<size_t> sj;
size_t max_ll = 0;
u64 max_lc = 0;
NumT max_num = std::numeric_limits<NumT>::min(),
min_num = std::numeric_limits<NumT>::max();
};
std::vector<Buck> bucks(nbucks);
for (size_t i = 0; i < N; ++i) {
auto const num = nums[i];
size_t const buck_i = rsi[i] / buck_size;
size_t max_ll = 0;
for (size_t j = 0; j <= buck_i; ++j) {
auto const & buckj = bucks[j];
if (buckj.sj.empty())
continue;
if (j < buck_i && buckj.max_num < num)
max_ll = std::max(max_ll, buckj.max_ll);
else {
if (buckj.min_num == buckj.max_num && buckj.max_num == num)
continue;
for (size_t k = 0; k < buckj.sj.size(); ++k)
if (nums[buckj.sj[k]] < num)
max_ll = std::max(max_ll, ll[buckj.sj[k]]);
}
}
if (max_ll > 0) {
u64 clc = 0;
for (size_t j = 0; j <= buck_i; ++j) {
auto const & buckj = bucks[j];
if (buckj.sj.empty())
continue;
if (j < buck_i && buckj.max_num < num) {
if (ll[buckj.sj[0]] == max_ll)
clc += buckj.max_lc;
} else {
if (buckj.min_num == buckj.max_num && buckj.max_num == num)
continue;
for (size_t k = 0; k < buckj.sj.size(); ++k)
if (nums[buckj.sj[k]] < num && ll[buckj.sj[k]] == max_ll)
clc += lc[buckj.sj[k]];
}
}
ll[i] = max_ll + 1;
lc[i] = clc;
} else {
ll[i] = 1;
lc[i] = 1;
}
auto & buck = bucks[buck_i];
bool const new_max = buck.sj.empty() || ll[i] > ll[buck.sj[0]];
buck.sj.push_back(i);
std::push_heap(buck.sj.begin(), buck.sj.end(), [&](size_t a, size_t b){
return ll[a] < ll[b];
});
if (ll[i] >= buck.max_ll)
buck.max_lc = lc[i] + (new_max ? u64(0) : buck.max_lc);
buck.max_ll = std::max(buck.max_ll, ll[i]);
buck.min_num = std::min(buck.min_num, num);
buck.max_num = std::max(buck.max_num, num);
}
size_t total_max_ll = 0;
for (size_t i = 0; i < bucks.size(); ++i)
if (!bucks[i].sj.empty())
total_max_ll = std::max(total_max_ll, bucks[i].max_ll);
u64 total_lc = 0;
for (size_t i = 0; i < bucks.size(); ++i)
if (!bucks[i].sj.empty() && ll[bucks[i].sj[0]] == total_max_ll)
total_lc += bucks[i].max_lc;
//std::cout << "Length " << total_max_ll << std::endl;
//std::cout << "Count " << total_lc << std::endl;
return total_lc;
}
};
#include <unordered_map>
#include <climits>
#include <memory>
using namespace std;
class SolutionOriginal {
// recursively compute the max increasing subsequence length and number of them at a given start point
// and a Min which means the next value must be at least Min.The result will be recorded on dp and values in
// dp will be checked first
pair<int, int> length_and_count(int ptr, int Min, vector<int> & nums,
vector<unordered_map<int, pair<int, int>>> & dp) {
if (ptr == nums.size())
return make_pair(0, 1);
if (dp[ptr].find(Min) != dp[ptr].end()) {
return dp[ptr][Min];
} else {
if (nums[ptr] < Min)
return dp[ptr][Min] = length_and_count(ptr + 1, Min, nums, dp);
else {
auto pair1 = length_and_count(ptr + 1, Min, nums, dp),
pair2 = length_and_count(ptr + 1, nums[ptr] + 1, nums, dp);
if (pair1.first > ++pair2.first)
return dp[ptr][Min] = pair1;
else if (pair1.first < pair2.first)
return dp[ptr][Min] = pair2;
else
return dp[ptr][Min] = make_pair(pair1.first, pair1.second + pair2.second);
}
}
}
public:
int findNumberOfLIS(vector<int> nums) {
vector<unordered_map<int, pair<int, int>>> dp(nums.size());
auto p = length_and_count(0, INT_MIN, nums, dp);
// std::cout << "Length " << p.first << std::endl;
// std::cout << "Count " << p.second << std::endl;
return p.second;
}
};
class SolutionBoosted {
// recursively compute the max increasing subsequence length and number of them at a given start point
// and a Min which means the next value must be at least Min.The result will be recorded on dp and values in
// dp will be checked first
pair<int, int> length_and_count(int ptr, int Min, vector<int> & nums,
unordered_map<int, shared_ptr<vector<pair<int, int>>>> & dp,
vector<pair<int, int>> * Min_vec = nullptr) {
if (ptr == nums.size())
return make_pair(0, 1);
auto GetMinVec = [&]{
auto it0 = dp.find(Min);
if (it0 != dp.end())
return it0->second.get();
auto it1 = dp.insert(make_pair(Min, make_shared<
vector<pair<int, int>>>())).first;
it1->second->resize(nums.size(), make_pair(-1, -1));
return it1->second.get();
};
if (!Min_vec)
Min_vec = GetMinVec();
auto & e = (*Min_vec)[ptr];
if (e.first != -1)
return e;
else {
if (nums[ptr] < Min)
return e = length_and_count(ptr + 1, Min, nums, dp, Min_vec);
else {
auto pair1 = length_and_count(ptr + 1, Min, nums, dp, Min_vec),
pair2 = length_and_count(ptr + 1, nums[ptr] + 1, nums, dp);
if (pair1.first > ++pair2.first)
return e = pair1;
else if (pair1.first < pair2.first)
return e = pair2;
else
return e = make_pair(
pair1.first, pair1.second + pair2.second);
}
}
}
public:
int findNumberOfLIS(vector<int> nums) {
unordered_map<int, shared_ptr<vector<pair<int, int>>>> dp;
auto p = length_and_count(0, INT_MIN, nums, dp);
// std::cout << "Length " << p.first << std::endl;
// std::cout << "Count " << p.second << std::endl;
return p.second;
}
};
#include <random>
#include <chrono>
int main() {
std::mt19937_64 rng(123);
std::uniform_int_distribution<int> distr(-100, 100);
std::vector<int> v;
for (size_t i = 0; i < (1 << 11); ++i)
v.push_back(distr(rng));
{
auto tb = std::chrono::system_clock::now();
std::cout << "Sqrt " << SolutionSqrt().findNumberOfLIS(v) << " ";
std::cout << "Time " << std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now() - tb).count() / 1000000.0 << " sec" << std::endl;
}
{
auto tb = std::chrono::system_clock::now();
std::cout << "Original "
<< SolutionOriginal().findNumberOfLIS(v) << " ";
std::cout << "Time " << std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now() - tb).count() / 1000000.0 << " sec" << std::endl;
}
{
auto tb = std::chrono::system_clock::now();
std::cout << "Original_Boosted "
<< SolutionBoosted().findNumberOfLIS(v) << " ";
std::cout << "Time " << std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::system_clock::now() - tb).count() / 1000000.0 << " sec" << std::endl;
}
}
Output:
Sqrt 15240960 Time 0.000856 sec
Original 15240960 Time 0.081954 sec
Original_Boosted 15240960 Time 0.011343 sec

Related

Prompt input until a mimimum number of values is entered

I am writing a small program in which I have to search for smallest and largest element entered.
The thing is I have made an array 10 elements wide and the user should enter at least 5 element.
If the user enter less than 5 elements I want to prompt insufficient elements.
This is the code I have written but it's not working:
#include <iostream>
using namespace std;
int main()
{
int arr[10], largest, smallest, i = 1, j = 1;
cout << "Enter the values in array at least 5 and maximum 10 " << endl;
for (int i = 0; i < 10; i++)
{
cin >> arr[i];
}
while (arr[i] >= 5)
{
largest = arr[0];
smallest = arr[0];
if (largest <= arr[j])
{
largest = arr[j];
}
if (smallest > arr[j])
{
smallest = arr[j];
}
j++;
i++;
}
getch();
return 0;
}
This code demonstrates how you'd find the min and max of a std::vector. The same principles apply to arrays. This piece of code does not allow a variable number of inputs.
#include <algorithm> // std::min_element() & std::max_element
#include <iostream>
#include <random>
#include <vector>
int main() {
// Set up; Fills vector with 10 random digits in range [1, 10]
std::mt19937 prng(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 10);
std::vector<int> numbers(10);
for (unsigned int i = 0; i < numbers.size(); ++i) {
numbers[i] = dist(prng);
}
// Find minimum and maximum, the easy way
auto min = std::min_element(numbers.begin(), numbers.end());
auto max = std::max_element(numbers.begin(), numbers.end());
// Find minimum and maximum, the hard way
int minIdx = 0;
int maxIdx = 0;
for (unsigned int i = 0; i < numbers.size(); ++i) {
if (numbers[i] < numbers[minIdx]) minIdx = i;
if (numbers[i] > numbers[maxIdx]) maxIdx = i;
}
// Print all numbers to verify
for (auto i : numbers) {
std::cout << i << ' ';
}
// This print goes with the std::min_element approach
std::cout << "\nMin: " << *min << "\nMax: " << *max << '\n';
// This print goes with the for loop approach
std::cout << "\nMin: " << numbers[minIdx] << "\nMax: " << numbers[maxIdx]
<< '\n';
}
Output:
1 5 2 10 2 8 6 7 6 7
Min: 1
Max: 10
Min: 1
Max: 10
In both methods, WHERE the min and max are stored are what's recorded. The first method returns an iterator, and the for-loop method records the index. It's more valuable to know where your min and max are versus just knowing the value.
The deal with a variable number of inputs and requiring a minimum is that things get complicated very quickly. The easiest, but not greatest (subjective) approach is to use a sentinel value. It's something the user must type to indicate that they are done typing. The wrinkle here that throws that out a bit is the fact that the user can enter the number 10. So I kind of pull out some bigger guns to handle this. A nice thing about the big guns is that they don't require a sentinel value. The user can just press Enter.
The code below uses a C-array like you, and it is a lot more tedious because of it. std::vectors know their size, and you can just add elements to it. With a C-array, you have to track the size separately, make sure it's always correct, etc. I lose the ability to use range-based for loops since the array may be only partially utilized.
#include <algorithm> // std::min_element() & std::max_element
#include <iostream>
#include <iterator>
#include <random>
#include <string>
int main() {
constexpr int capacity = 10;
int numbers[capacity];
int size = 0;
std::string tmp;
while (size < capacity && std::getline(std::cin, tmp)) {
if (tmp == "" && size >= 5) {
break;
}
if (tmp == "" && size < 5) {
std::cerr << "Need at least 5 elements.\n";
continue;
}
std::size_t pos;
int num;
try {
num = std::stoi(tmp, &pos);
} catch (...) {
std::cerr << "Exception thrown.\n";
continue;
}
if (pos != tmp.length()) {
std::cerr << "Bad Value.\n";
continue;
}
if (num >= 0 && num <= 10) {
numbers[size] = num;
++size;
}
}
// Find minimum and maximum, the easy way
auto min = std::min_element(std::begin(numbers), std::begin(numbers) + size);
auto max = std::max_element(std::begin(numbers), std::begin(numbers) + size);
// Find minimum and maximum, the hard way
int minIdx = 0;
int maxIdx = 0;
for (int i = 0; i < size; ++i) {
if (numbers[i] < numbers[minIdx]) minIdx = i;
if (numbers[i] > numbers[maxIdx]) maxIdx = i;
}
// Print all numbers to verify
for (int i = 0; i < size; ++i) {
std::cout << numbers[i] << ' ';
}
// This print goes with the std::min_element approach
std::cout << "\nMin: " << *min << "\nMax: " << *max << '\n';
// This print goes with the for loop approach
std::cout << "\nMin: " << numbers[minIdx] << "\nMax: " << numbers[maxIdx]
<< '\n';
}
You can tell how much extra work is required just for basic ASCII input. If you make some assumptions, a lot of the code can be cut out, but where user input is concerned, those assumptions are rarely safe, and are more of a convenience for quick testing or learning purposes.

Generate Partition of integer into k different integers (C++)

I have problem with the constraint
I have to partition it to "exact" k "different" integers
e.g. 10 = 1+2+7 -> valid
10 = 2+2+6 -> invalid
I don't want to print them, I want to store them in to vectors or something
I am thinking of recursive solution, but still can't come up with a efficient way to store them...(the solution I found can only print it)
And I think it should store in vector<vector>
the struct should be like this?
Partition(){
....
....
}
Partition_main(){
...
Partition()
....
}
In practice, the problem is to enumerate all the solutions.
This can be done by a DFS.
In the following code, the DFS is implemented with a help of a FIFO (i.e. a std::queue).
The little trick is, for a given candidate at a given step, to calculate the minimum
sum that can be obtained in the next steps.
If this minimum sum is larger than n, then we can stop the research in this direction.
#include <iostream>
#include <vector>
#include <queue>
#include <cassert>
struct Parti {
std::vector<int> v;
int sum = 0;
};
std::vector<std::vector<int>> get_partitions (int n, int k) {
std::vector<std::vector<int>> result;
std::queue<Parti> fifo;
Parti start;
fifo.push(start);
while (!fifo.empty()) {
Parti elt = fifo.front();
fifo.pop();
int sum = elt.sum;
int remain = k-elt.v.size();
if (remain == 1) {
int last = n - sum;
if (k > 1) assert (last > elt.v.back());
elt.v.push_back(last);
result.push_back (elt.v);
continue;
}
int i;
if (elt.v.size() == 0) i = 1;
else i = elt.v.back() + 1;
while (true) {
int min_sum = sum + remain*(2*i + remain - 1)/2;
if (min_sum > n) break;
Parti new_elt = elt;
new_elt.v.push_back(i);
new_elt.sum += i;
fifo.push (new_elt);
++i;
};
}
return result;
}
int main() {
int n = 15;
int k = 4;
auto res = get_partitions (n, k);
if (res.size() == 0) {
std::cout << "no partition possible\n";
return 0;
}
for (const auto& v: res) {
std::cout << n << " = ";
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i];
if (i == v.size()-1) {
std::cout << "\n";
} else {
std::cout << " + ";
}
}
}
return 0;
}

Computing the values of a std::map of vector as the key and double as the value?

std::map<std::vector<double>, double> MyMethod(std::map<std::vector<double>, double> mappingSourceToTarget, int radius)
{
std::map<std::vector<double>, double> mappingSourceToTargetNew(mappingSourceToTarget);
std::map<std::vector<double>, double>::iterator it_trgt,it_src;
double sumOfNormals1 = 0;
int size = mappingSourceToTarget.size(), i=0;
for (it_src = mappingSourceToTarget.begin(), it_trgt = mappingSourceToTargetNew.begin(); i < size + (radius * 2) ; i++, it_src, it_trgt++)
{
auto elem = mappingSourceToTargetNew;
if (i == 0)
{
//it_trgt->first.at(i % size) = it_src->first.at(i%size);
mappingSourceToTargetNew.at(i % size) = mappingSourceToTarget.at(i % size);
sumOfNormals1 += mappingSourceToTarget.at(i % size);
Here I am having trouble , regarding how should put the values from one map to another.
I am using the concept of circular arrays so i%size is being used here,
the vector is the coordinates and the double is the intensity values at that point.
The index thing is not working with .at(), I really don't know what to do as
I have to make it work by using maps only (task).
copying the values from one map to another at the index (i%size)
1) Read the key-value using an iterator, i.e. using first/second.
2) When the iterator reach the end, set it equal to begin(). That will function like i % size
Here is some sample code to show the idea:
#include <iostream>
#include <map>
int main() {
std::map<int, int> m {{1, 100}, {2, 200}};
auto s = m.size();
std::cout << "size = " << s << std::endl;
auto i1 = m.begin();
auto i2 = m.end();
for (auto i = 0; i < 5; ++i)
{
std::cout << "key=" << i1->first << " value=" << i1->second << std::endl;
++i1;
if (i1 == i2)
{
// We reached the end so go back to the begining
i1 = m.begin();
}
}
return 0;
}
output:
size = 2
key=1 value=100
key=2 value=200
key=1 value=100
key=2 value=200
key=1 value=100

C++ 3sum complexity

I was trying to solve the 3 sum problem in cpp.
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int size = nums.size();
vector<vector<int>> result;
for (int i = 0; i < size - 2; ++i) {
for (int j = i + 1; j < size - 1; ++j) {
for (int k = j + 1; k < size; ++k) {
if (sumToZero(i, j, k, nums)) {
vector<int> newComb = vectorify(i, j, k, nums);
//printComb(newComb);
if (!exist(newComb, result)) {
//cout << "not exist" << endl;
result.push_back(newComb);
} else {
//cout << "exist" << endl;
}
}
}
}
}
return result;
}
bool sumToZero(int i, int j, int k, vector<int>& nums) {
return nums[i] + nums[j] + nums[k] == 0;
}
vector<int> vectorify(int i, int j, int k, vector<int>& nums) {
vector<int> result;
result.push_back(nums[i]);
result.push_back(nums[j]);
result.push_back(nums[k]);
return result;
}
void printComb(vector<int>& input) {
cout << input[0] << input[1] << input[2] << endl;
}
bool isSameComb(vector<int>& a, vector<int> b) {
for (int i = 0; i < b.size(); ++i) {
if (a[0] == b[i]) {
b.erase(b.begin() + i);
}
}
for (int i = 0; i < b.size(); ++i) {
if (a[1] == b[i]) {
b.erase(b.begin() + i);
}
}
for (int i = 0; i < b.size(); ++i) {
if (a[2] == b[i]) {
b.erase(b.begin() + i);
}
}
return b.empty();
}
bool exist(vector<int>& niddle, vector<vector<int>>& haystack) {
int size = haystack.size();
for (int i = 0; i < size; ++i) {
if (isSameComb(niddle, haystack[i])) {
return true;
}
}
return false;
}
};
However, this solution exceeded the time limit. I cannot think of the source of extra complexity. Can someone help me point out where am I doing extra computation?
You can be in O(n²) with something like:
std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
std::sort(nums.begin(), nums.end());
std::vector<std::vector<int>> res;
for (auto it = nums.begin(); it != nums.end(); ++it) {
auto left = it + 1;
auto right = nums.rbegin();
while (left < right.base()) {
auto sum = *it + *left + *right;
if (sum < 0) {
++left;
} else if (sum > 0) {
++right;
} else {
res.push_back({*it, *left, *right});
std::cout << *it << " " << *left << " " << *right << std::endl;
++left;
++right;
}
}
}
return res;
}
Demo
I let duplicate handling as exercise.
The source of extra complexity is the third loop, which brings time complexity of your code to O(n3).
Key observation here is that once you have two numbers, the third number is fixed, so you do not need to loop around to find it: use hash table to see if it's there or not in O(1). For example, if your first loop looks at value 56 and your second loop looks at value -96, the third value must be 40 in order to yield zero total.
If the range of numbers is reasonably small (say, -10000..10000) you can use an array instead.
This would bring time complexity to O(n2), which should be a noticeable improvement on timing.
A couple of possibilities:
First, construct a hash table of all entries in the vector up front, then remove the third loop. Inside the second loop, simply check whether -nums[i] - nums[j] exists in the hash table. That should bring your time complexity from O(n3) back to something closer to O(n2).
Second, function calls aren't free though an optimiser can sometimes improve that considerably. There's no performance reason why you should be calling a function to check if three numbers add to zero so you could replace:
if (sumToZero(i, j, k, nums)) {
with:
if (nums[i] + nums[j] == -nums[k]) {
Of course, this is rendered moot if you adopt the first suggestion.
Third, don't check and insert the possible result every time you get one. Just add it to the vector no matter what. Then, at the end, sort the vector and remove any duplicates. That should hopefully speed things up a bit as well.
Fourth, there's quite possibly a performance hit for using a vector for the potential result when an int[3] would do just as well. Vectors are ideal if you need something with a variable size but, if both the minimum and maximum size of an array-type collection is always going to be a fixed value, raw arrays are fine.
But perhaps the most important advice is measure, don't guess!
Make sure that, after each attempted optimisation, you test to see whether it had a detrimental, negligible, or beneficial effect. A test suite of various data sets, and automating the process, will make this much easier. But, even if you have to do it manually, do so - you can't improve what you can't measure.
Here is my solution that finds all unique triplets in O(n^2) run-time.
class Solution {
public: vector<vector<int>> threeSum(vector<int>& nums) {
int len = nums.size();
if(len<3) return {};
sort(nums.begin(), nums.end());
vector<vector<int>> retVector;
int target, begin, end;
int i=0;
while(i < len - 2)
{
int dup; // to find duplicates entries
target = -nums[i];
begin = i + 1; end = len - 1;
while (begin < end)
{
if (nums[begin] + nums[end] < target) begin++;
else if (nums[begin] + nums[end] > target) end--;
else
{
retVector.push_back({nums[i], nums[begin], nums[end]});
// its time to remove duplicates
dup=nums[begin];
do begin++; while(nums[begin] == dup); // removing from front
dup=nums[end];
do end--; while(nums[end] == dup); // removing from back
}
}
dup=nums[i];
do i++; while(nums[i] == dup) ; // removing all ertries same as nums[i]
}
return retVector;
}
};

C++ Calculating the Mode of a Sorted Array

I have to write a C++ code that finds the median and mode of an array. I'm told that it's much easier to find the mode of an array AFTER the numbers have been sorted. I sorted the function but still cannot find the mode.
int counter = 0;
for (int pass = 0; pass < size - 1; pass++)
for (int count = pass + 1; count < size; count++) {
if (array [count] == array [pass])
counter++;
cout << "The mode is: " << counter << endl;
If the array has been sorted already, you can count the occurrences of a number at once. Then just save the number that has biggest occurrences. And you can find out the mode in only one for-loop.
Otherwise, you'll have to do more than one for-loops.
See a details example at the link below
Find-the-Mode-of-a-Set-of-Numbers
Here is the code,
int number = array[0];
int mode = number;
int count = 1;
int countMode = 1;
for (int i=1; i<size; i++)
{
if (array[i] == number)
{ // count occurrences of the current number
++count;
}
else
{ // now this is a different number
if (count > countMode)
{
countMode = count; // mode is the biggest ocurrences
mode = number;
}
count = 1; // reset count for the new number
number = array[i];
}
}
cout << "mode : " << mode << endl;
One way is that you can use Run Length encoding. In Run Length encoding, representation would be like; (Item, Its frequency).
While doing so, keep track of the maximum frequency and Item. This will give you the mode once you complete the Run Length.
for example:
1 1 2 2 2 3 3 4 5
It run length encoding would be
{1, 2}, {2, 3}, {3, 2}, {4, 1}, {5, 1}
It needs O(n) space.
This is how I did it, my solution will take a sorted vector as input. It has O(n) time complexity and can work with the case where there are more than 1 "mode" number in the vector.
void findMode(vector<double> data) {
double biggestMode = 1;
vector<double> mode, numbers;
numbers.push_back(data.at(0));
mode.push_back(1);
int count = 0;
for (int i = 1; i < data.size(); i++) {
if (data.at(i) == numbers.at(count)) {
mode.at(count)++;
}
else {
if (biggestMode < mode.at(count)) {
biggestMode = mode.at(count);
}
count++;
mode.push_back(1);
numbers.push_back(data.at(i));
}
}
for (int i = 0; i < mode.size(); i++) {
if (mode.at(i) == biggestMode)
cout << numbers.at(i) << " ";
}
cout << endl;
}
Here is the code snippet:
int number = array[0];
int mode = number;
int count = 1;
int countMode = 1;
for (int i=1; i<size; i++)
{
if (array[i] == number)
{
count++;
}
else
{
if (count > countMode)
{
countMode = count;
mode = number;
}
count = 1;
number = array[i];
}
}
cout << "mode : " << mode << endl;
There is an old adage that states "If you put 10 programmers in a room and give them the same program to code you will get 12 different results", hence my version of answering your question. It may not be as fast (I'm planning on testing it's speed versus some of the other suggestions) but I feel it is easy to understand.
#include <iostream>
using namespace std;
int main ()
{
short z[10];
short maxCount = 0, curCount = 0, cur = 0, most = 0;
for (int i = 0; i < 10; i++)
{
cout << "Enter a number: " << endl;
cin >> z[i];
}
for (int i = 0; i < 10; i++)
{
cur = z[i];
for (int a = i; a < 10; a++)
{
if (cur == z[a])
{
curCount++;
cur = z[a];
}
if (curCount > maxCount)
{
maxCount = curCount;
most = z[a];
}
}
curCount = 0;
}
cout << "the mode is : " << maxCount << ", the number is: " << most << endl;
}
int number = array[0];
int mode = number;
int count = 1;
int countMode = 1;
for (int i=1; i<size; i++)
{
if (array[i] == number)
{ // count occurrences of the current number
++count;
}
else
{ // now this is a different number
count = 1; // reset count for the new number
number = array[i];
}
if (count > countMode) {
countMode = count;
mode = number;
}
}
cout << "mode : " << mode << endl;
While Diedrei's answer is close, several people have pointed out some shortcomings such as if the mode is defined by the last numbers of the sorted array (1,2,3,3,4,4,4 would return 3 as the mode). Also, depending on the requirements on how to handle multiple modes, there will be different solutions.
This solution does several things:
Solves the issue of the mode being at the end of the array
If there are multiple modes (more than 1 number has the same number of occurrences with a count > 1), returns the smallest number as the mode
Returns -1 if there is no mode (each number only occurs once)
int number = array[0];
int mode = number;
int count = 1;
int countMode = 1;
for (int i=1; i<size; i++)
{
if (array[i] == number)
{ // increment the count of occurrences for the current number
++count;
if (count > countMode)
{
countMode = count; // this number now has the most occurrences
mode = number; // this number is now the mode
}
}
else
{ // now this is a different number
count = 1; // reset count for the new number
number = array[i]; // set the new number
}
}
if (countMode == 1) {
mode = -1; // set the mode to -1 if each number in the array occur only once
}
cout << "mode : " << mode << endl;
The "mode" is the value that occurs most often. If no number is repeated, then there is no mode for the list.
So there would be no benefit to sorting if you needed to know the "mode".
Are you sure you are not referring to the median? The median is the middle number in a set.
If you have 1,2,3,4,5 the Median (middle number) is the (total_number)/2) rounded up if it is odd, 2.5 -> 3 and our median would be 3. you can only really calculate the median if your numbers are sorted.
If you have an even number in a set 1,2,3,4,5,6
your mode is slots 3,4 (coincidentally also, 3,4)
(total_number)/2 slot and (total_number)/2 + 1 slot, for any even array of numbers.
http://www.purplemath.com/modules/meanmode.htm
This code should give you the mode. If there are equal number of two different numbers, it will output the first of such.
int count = 1, mode = 0, m = 0, i = 1;
size_t sz = sizeof(array)/sizeof(*array);
while(i != sz+1) {
if(array[i-1] != array[i]) {
if(count > m) {
mode = array[i-1];
m = count;
count = 1;
}
}
else
++count;
++i;
}
std::cout << "mode: " << mode << std::endl;
This code finds the mode in C++:
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
int i,j,k=0,n,repeat_max=0,cn=0;
int array1[50],mode[50],count[50]={0},c[50];
cout<<"\n inter count:\t";
cin>>n;
cout<<"\n";
for(i=0;i<n;i++)
cin>>array1[i];
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
if(array1[i]==array1[j])
{
count[i]++;
if(count[i]>=repeat_max)
{
repeat_max=count[i];
mode[k++]=array1[i];
}
}
}
}
cout<<"\n================\n";
for(i=1;i<k;i++)
cout<<"\t mode[i]="<<mode[i]<<"\n";
cout<<"\t\n\nrepeat array:"<<repeat_max;
return 0;
}
I did it this way:
int main()
{
int mode,modecount2,modecount1;
bool is_nomode=false;
vector<int> numbers = { 15,43,25,25,25,25,16,14,93,93,58,14,55,55,55,64,14,43,14,25,15,56,78,13,15,29,14,14,16 };
sort(numbers);
//If you uncomment the following part, you can see the sorted list of above numbers
//for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i] << '\n';
//keep_window_open();
mode = numbers[0];
modecount1 = 0;
modecount2 = 1; //Obviously any number exists at least once!
for (int i = 1; i < numbers.size(); ++i) {
if(numbers[i]==numbers[i-1]) ++modecount2;
else {
if (modecount2 > modecount1) {
mode = numbers[i - 1];
modecount1 = modecount2;
}
else if (i != 1 && modecount2 == modecount1) { std::cout << "No mode!\n"; is_nomode = true; break; }
modecount2 = 1;
}
}
if(!is_nomode) std::cout << "Mode of these numbers is: " << mode << std::endl;
keep_window_open();
Also you can add another 25 to the list of numbers and see what happens if two numbers have the same occurrence!
I hope it helps.
This code uses "map" to find out the MODE from the given array.
It assumes the array is already sorted.
int findMode(int * arr, int arraySize)
{
map<int, int> modeMap;
for (int i = 0; i < arraySize; ++i) {
++modeMap[arr[i]];
}
auto x = std::max_element(modeMap.begin(), modeMap.end(),
[](const pair<int, int>& a, const pair<int, int>& b) {
return a.second < b.second; });
return x->first;
}
This is the code I've written for sorted vector
void print_mode(vector<int>& input)
{
int mode=0, count = 0;
int current_number = input[0];
int mode_number = current_number;
for (int i=0; i < input.size(); i++)
{
if (current_number == input[i])//check if the number is the same
{
count++;
}
else //this fuction works when the value are no longer the same and
//this is when it updates the mode value
{
if (count > mode)//update mode value
{
mode = count;
mode_number = current_number;
}
count = 1;// it is not reset back to zero because when it the program detect a
//different number it doesn't count it so this is to solve that issue
}
if (i == input.size() - 1)// this function before it doesn't work when the largest value
//is mode so I added this if state to solve it
{
if (count > mode)
{
mode = count;
mode_number = current_number;
}
}
current_number = input[i];//prepare for next value
}
cout << mode_number << " is the mode number and it is repeated " << mode << " times" << endl;
}
1. Finding the mode without sorting
I'm told that it's much easier to find the mode of an array AFTER the numbers have been sorted
I'm not so sure.
std::vector<std::pair<int, unsigned>> mode(const std::vector<int> &v)
{
if (v.empty())
return {};
std::unordered_set<int> seen;
unsigned max_count(0);
std::vector<std::pair<int, unsigned>> ret;
for (auto i(v.begin()); i != v.end(); ++i)
if (seen.find(*i) == seen.end())
{
const auto count(std::count(i, v.end(), *i));
if (count > max_count)
{
max_count = count;
ret = {{*i, max_count}};
}
else if (count == max_count)
ret.emplace_back(*i, max_count);
seen.insert(*i);
}
return ret;
}
The algorithm
uses a hash table (seen) to skip already seen numbers;
doesn't need a copy of the input vector;
only requires a container with forward iterator support.
Also note that for small input vectors the function can be simplified removing the hash table.
You can play with the code here.
2. Finding the mode sorting
std::vector<std::pair<int, unsigned>> mode(std::vector<int> v)
{
if (v.empty())
return {};
std::sort(v.begin(), v.end());
auto current(*v.begin());
unsigned count(1), max_count(1);
std::vector<std::pair<int, unsigned>> ret({{current, 1}});
for (auto i(std::next(v.begin())); i != v.end(); ++i)
{
if (*i == current)
++count;
else
{
count = 1;
current = *i;
}
if (count > max_count)
{
max_count = count;
ret = {{current, max_count}};
}
else if (count == max_count)
ret.emplace_back(current, max_count);
}
return ret;
}
We assume an unsorted input vector, so the function works on a copy of the original vector that is sorted and processed.
If the original vector is already sorted, the input argument can be passed by reference and the std::sort call can be removed.
You can play with the code here.
Performance
Performance depends on multiple factor (size of the input vector, distribution of values...).
E.g. if the range of the input integers is small algorithm 1 is faster than algorithm 2.
You can experiment here.
I know the question is old, but here is a clean and short code that calculates statistical mode:
std::sort(vector.begin(), vector.end());
int mode = vector[0], count = 0, countMode = 1;
int last = mode;
for (int i = 1; i < vector.size(); ++i)
{
if (vector[i] == mode) ++countMode;
else
{
if (last != vector[i]) count = 0;
++count;
}
if (count > countMode)
{
mode = vector[i];
countMode = count;
count = 0;
}
last = vector[i];
}
int findModa(int *arr, int n) {
int count=1;
int countmax=0;
int current = arr[0];
int moda = 0;
for (int i=1; i<n; i++) {
if(arr[i] == curr) {
count++;
}
else if (count>countmax) {
countmax=count;
count=1;
moda=arr[i-1];
current=arr[i];
}
current=arr[i];
}
return moda;
}