0

Sort Integers by The Number of 1 Bits

Leetcode : Problem Link

Example Testcase :

Example 1:

Input: arr = [0,1,2,3,4,5,6,7,8]
Output: [0,1,2,4,8,3,5,6,7]
Explantion: [0] is the only integer with 0 bits.
[1,2,4,8] all have 1 bit.
[3,5,6] have 2 bits.
[7] has 3 bits.
The sorted array by bits is [0,1,2,4,8,3,5,6,7]\

Example 2:

Input: arr = [1024,512,256,128,64,32,16,8,4,2,1]
Output: [1,2,4,8,16,32,64,128,256,512,1024]
Explantion: All integers have 1 bit in the binary representation, you should just sort them in ascending order.

My Solution :

class Solution {
public:
unsigned int setBit(unsigned int n){
    unsigned int count = 0;
    while(n){
        count += n & 1;
        n >>= 1;
    }
    return count;
}
vector<int> sortByBits(vector<int>& arr) {
    map<int,vector<int>>mp;
    for(auto it:arr){
        mp[setBit(it)].push_back(it);
    }
    for(auto it:mp){
        vector<int>vec;
        vec=it.second;
        sort(vec.begin(),vec.end()); //This Sort Function of vector is not working
    }
    vector<int>ans;
    for(auto it:mp){
        for(auto ele:it.second){
            ans.push_back(ele);
        }
    }
    return ans;
}
};

In my code why sort function is not working ?

[1024,512,256,128,64,32,16,8,4,2,1]

For the above testcase output is [1024,512,256,128,64,32,16,8,4,2,1] because of sort function is not working. It's correct output is [1,2,4,8,16,32,64,128,256,512,1024]

Note : In the above example testcase every elements of the testcase has only one set-bit(1)

Evg
  • 25,259
  • 5
  • 41
  • 83
  • All the numbers in the input have *one* bit set in them. Which means "the number of 1 bits" makes them all equal. – Some programmer dude Feb 17 '22 at 06:08
  • 1
    And you don''t need the map or a lot of loops or many calls to `std::sort`. A single call to `std::sort` with an ordering function that checks first for bits, and if equal checks the values, will be enough. – Some programmer dude Feb 17 '22 at 06:15
  • As for the problems with your current code, you should not use sites like Leetcode to learn programming, programming languages, or computer science. That's not what they're for. Instead invest in [some good C++ books](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282) and take classes. And (*big hint*) learn about *references* (and the difference between a value and a reference). – Some programmer dude Feb 17 '22 at 06:17
  • @Someprogrammerdude as `setBit` is quite expensive it might be more efficient using this method which only calculates the value once for each element? – Alan Birtles Feb 17 '22 at 06:24
  • Unrelated to your question (which was answered). The algorithm of using intermediate map with vector slice is very inefficient. There is a way to sort entire thing in-place, using a predicate for sorting. Time complexity appears to be the same, but there's also space complexity, and there are also number of allocation and memory layout, which impact real perf of the function – Alex Guteniev Feb 17 '22 at 06:36
  • @AlanBirtles True, but better to start simple and with a good working solution. Then if needed start profiling and benchmarking to find out where problems and bottlenecks might be. None of which is taught on such sites. – Some programmer dude Feb 17 '22 at 06:37

5 Answers5

2

As your iteration in //This sort function ... refers to mp as the copy of the value inside the map, sort function will not sort the vector inside it, but the copy of it. Which does not affecting the original vector<int> inside the mp. Therefore, no effect occurs. You should refer the vector inside the map as a reference like this:

class Solution {
public:
    unsigned int setBit(unsigned int n) {
        unsigned int count = 0;
        while (n) {
            count += n & 1;
            n >>= 1;
        }
        return count;
    }
    vector<int> sortByBits(vector<int>& arr) {
        map<int, vector<int>>mp;
        for (auto it : arr) {
            mp[setBit(it)].push_back(it);
        }
        for (auto& it : mp) {
            sort(it.second.begin(), it.second.end()); //Now the sort function works
        }
        vector<int>ans;
        for (auto it : mp) {
            for (auto ele : it.second) {
                ans.push_back(ele);
            }
        }
        return ans;
    }
};

Although there is more design problem inside your solution, this will be a solution with minimized modification.

K.R.Park
  • 1,015
  • 1
  • 4
  • 18
  • No idea why this was down-ticked by someone. It is not only the root of the problem, it is the absolute minimal change necessary to address it. The OP's design choices could be improved (obviously), but that wasn't the question. How the OP approached doing this task is up to them, but the problem they had whilst doing so is clearly explained and addressed in this answer. – WhozCraig Feb 17 '22 at 07:08
  • @ArminMontigny Although this problem was a Leetcode problem, the OP was almost close to the answer, and I guess he/she would arrive to the answer even if I did not answer his/her question. I just assisted him/her with little bit of knowledge, and I do not regret it.. – K.R.Park Feb 17 '22 at 10:35
1

vector<int>vec is a copy of a copy of the one in the map which is then discarded. Try:

for(auto& entry:mp){
    vector<int>&vec=entry.second;
    sort(vec.begin(),vec.end());
}

Your other for loops should also use references for efficiency but it won't affect the behaviour.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
1

Here is memory efficient and fast solution. I don't know why you are using map and extra vector. we can solve this questions without any extra memory efficiently. We just have to make a Comparator function which will sort elements according to our own requirements. Please let me know in comments if you require further help in code (or if you find difficult to understand my code). I am using __builtin_popcount() function which will return me number of set bits in a number.

bool sortBits(const int a, const int b){ //Comparator function to sort elements according to number of set bits
    int numOfBits1 = __builtin_popcount(a);
    int numOfBits2 = __builtin_popcount(b);
    if(numOfBits1 == numOfBits2){ //if number of set bits are same, then sorting the elements according to magnitude of element (greater/smaller element)
        return a < b;
    }
    return (numOfBits1 < numOfBits2); //if number of set bits are not same, then sorting the elements according to number of set bits in element
}
class Solution {
public:

vector<int> sortByBits(vector<int>& arr) {
    sort(arr.begin(),arr.end(), sortBits);
    return arr;
}
};
Landi logan
  • 73
  • 1
  • 7
  • Answers containing code with no explanation are often not ideal. Don't be too offended through, someone seems to have down voted most answers to this question. – Alan Birtles Feb 17 '22 at 07:17
  • `sortBits` is misnamed as it doesn't **sort** but **compare**. – Jarod42 Feb 17 '22 at 09:18
  • Using tuple might help to ensure strict weak ordering: `return std::tuple{setBit(lhs), lhs} < std::tuple{setBit(rhs), rhs};` – Jarod42 Feb 17 '22 at 09:21
  • @Jarod42 yes agree with you that I am bad in naming functions. Name should be compareBits or ComapreElements or something like this – Landi logan Feb 17 '22 at 09:32
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 17 '22 at 10:41
  • `__builtin_popcount()` seems to be a non-standard. how about using `std::popcount()`? – K.R.Park Feb 17 '22 at 10:56
  • @K.R.Park in which header this function is? This showing me error 'error: no member named 'popcount' in namespace 'std' ' – Landi logan Feb 17 '22 at 11:00
  • @Landilogan https://en.cppreference.com/w/cpp/numeric/popcount – K.R.Park Feb 17 '22 at 11:02
  • oh okay. This function available from c++ 20. I am running code on c++ 11. Thanks for providing link – Landi logan Feb 17 '22 at 11:21
  • __builtin_popcount() is new to me. I was not aware about that function . That's why I tried in simple way .. as simple as possible. Yes, No I understood your solution. Thanks. – SUKRITI GUIN Feb 17 '22 at 13:45
  • @SUKRITIGUIN https://www.geeksforgeeks.org/builtin-functions-gcc-compiler/ These are very useful functions in competitive programming for saving time and effort. – Landi logan Feb 18 '22 at 04:31
1

I assume the OP is just learning, so fiddling with various data structures etc. can carry some educational value. Still, only one of the comments pointed out that the starting approach to the problem is wrong, and the whole point of the exercise is to find a custom method of comparing the numbers, by number of bits first, then - by value.

Provided std::sort is allowed (OP uses it), I guess the whole solution comes down to, conceptually, sth likes this (but I haven't verified it against LeetCode):

template <typename T>
struct Comp
{
    std::size_t countBits(T number) const
    {
        size_t count;
        while(number) {
            count += number & 1;
            number>>=1;
        }
        return count;
    }
    
    bool operator()(T lhs, T rhs) const
    {
        /*
        auto lb{countBits(lhs)};
        auto rb{countBits(rhs)};
        return lb==rb ? lhs < rhs : lb < rb;
        * The code above is the manual implementation of the line below
        * that utilizes the standard library
        */
        return std::tuple{countBits(lhs), lhs} < std::tuple{countBits(rhs), rhs};
    }
};


class Solution {
public:
    void sortByBits(vector<int>& arr) {
        std::sort(begin(arr), end(arr), Comp<int>{});
    }
};

Probably it can improved even further, but I'd take it as starting point for analysis.

alagner
  • 3,448
  • 1
  • 13
  • 25
  • Using tuple might help to ensure strict weak ordering: `return std::tuple{countBits(lhs), lhs} < std::tuple{countBits(rhs), rhs};`. – Jarod42 Feb 17 '22 at 09:23
  • @Jarod42 right, but I omitted that on purpose to show the "gory details" of comparator's logic as they seem to be the main point of the exercise (regardless how they're implemented). – alagner Feb 17 '22 at 09:43
  • So many ways to be wrong by chaining the comparisons manually (and scale badly with numbers of criteria) whereas tuple handles most cases trivially and scale well. – Jarod42 Feb 17 '22 at 09:56
0

The problem is already evaluated and the fix is aready explained.

I want to give 2 additional/alternative solution proposals.

  • In C++17 we have the std::bitset count function. Please see here
  • And in C++20 we have directly the std::popcount function. Please see here.

(Elderly and grey haired people like me would also find 5 additional most efficient solutions in the Book "Hackers Delight")

Both variants lead to a one statement solution using std::sort with a lambda.

Please see:

#include <algorithm>
#include <vector>
#include <iostream>
#include <bitset>

// Solution
class Solution {
public:
    std::vector<int> sortByBits(std::vector<int>& arr) {
        std::sort(arr.begin(), arr.end(), [](const unsigned int i1, const unsigned int i2)
            { size_t c1{ std::bitset<14>(i1).count() }, c2{ std::bitset<14>(i2).count() }; return c1 == c2 ? i1 < i2 : c1 < c2; });
            //{ int c1=std::popcount(i1), c2=std::popcount(i2); return c1 == c2 ? i1 < i2 : c1 < c2; });
        return arr;
    }
};

// Test
int main() {
    std::vector<std::vector<int>> testData{
        {0,1,2,3,4,5,6,7,8},
        {1024,512,256,128,64,32,16,8,4,2,1}
    };

    Solution s;
    for (std::vector<int>& test : testData) {
        for (const int i : s.sortByBits(test)) std::cout << i << ' ';
        std::cout << '\n';
    }
}

A M
  • 14,694
  • 5
  • 19
  • 44