I have an array with elements as [0,1,0,3,12]. I want to shift all the zeros to the end of it while maintaining the relative order of the non-zero elements. After shifting the array should look like this [1,3,12,0,0]. So to do this i wrote the following code:
#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b){
if(a==0) return a>b;
else return true;
}
int main(){
int arr[]={0,1,0,3,12};
sort(arr,arr+5,cmp);
for(int i:arr){
cout<<i;
}
return 0;
}
i wrote this code thinking that by returning false whenever zero is placed before a non-zero element i can swap the elements but this code gives the output as [12,3,1,0,0] i.e it just sorted the elements in descending order. Can anyone explain me why is my comparator function not working? can we use comparator function to do this??
>Solution :
The comparator you pass to std::sort needs to be a strict weak order. In particular, you need cmp(x, x) = false for the same value, and at most one of cmp(x, y) and cmp(y, x) to be true (which is not true for cmp(1, 2) == cmp(2, 1) == true)
What you want in your order is for all elements to be equivalent, except 0 which is greater than all other elements. This can be done with a function like this:
bool cmp(int a, int b) {
if (a==0) {
if (b==0) return false; // equal
return false; // a is greater
}
if (b == 0) return true; // `a < 0`? True, since 0 is the greatest element
return false; // All other elements are equivalent, not less than
}
// Simplified to
bool cmp(int a, int b) {
return b == 0 && a != 0;
}
And then you want to use std::stable_sort to preserve the order of the equivalent elements.
However, sorting isn’t the best approach here. You can simply move all the non-zero elements forward, then fill in the back space with 0s:
void move_zeroes_to_back(int* begin, int* end) {
int* nonzero_begin = begin;
for (; begin != end; ++begin) {
if (*begin != 0) *nonzero_begin++ = *begin;
// else Don't move zero values forward
}
// Fill in the rest
for (; nonzero_begin != end; ++nonzero_begin) {
*nonzero_begin = 0;
}
}
Where the first part of this algorithm is what std::remove does:
void move_zeroes_to_back(int* begin, int* end) {
int* nonzero_end = std::remove(begin, end, 0); // Remove all zero values
std::fill(nonzero_end, end, 0); // Replace removed zero values at the end
}