Remove duplicates from array C++ - c++

Input: int arr[] = {10, 20, 20, 30, 40, 40, 40, 50, 50}
Output: 10, 30
My code:
int removeDup(int arr[], int n)
{
int temp;
bool dupFound = false;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(arr[i] == arr[j]){
if(!dupFound){
temp = arr[i];
dupFound = true;
}
else{
arr[i] = temp;
}
}
}
}
//shift here
}
First of all, I don't know if this is the most efficient way of doing this.
I'm trying to find the first duplicate element, assign it to every duplicate element and shift them to the end of the array, which doesn't work because the last duplicate element cannot be compared.
I need some help with finding the last duplicate element, so I can assign temp to it.

I do not understand the logic of your code. When you find the second element arr[j] that equals arr[i] you will assign temp to arr[i]. However, temp has been assigned arr[i] when you found the first duplicate. Essentially you do arr[i] = arr[i]. Its not clear how this is supposed to find unique elements.
You can use a map to count frequency of elements, then print those with frequency 1:
#include <unordered_map>
#include <iostream>
int main()
{
std::unordered_map<int,size_t> freq;
int arr[] = {10, 20, 20, 30, 40, 40, 40, 50, 50};
// count frequencies
for (auto e : arr) { ++freq[e]; }
// print the elements e where freq[e] == 1
for (const auto& f : freq) {
if (f.second == 1) {
std::cout << f.first << "\n";
}
}
}
Only small modifications needed to add the unique elements to a vector.

Instead of trying to do everything at once, let us focus on correctness first:
int removeDup(int* arr, int n) {
// Note: No i++! This depends on whether we find a duplicate.
for (int i = 0; i < n;) {
int v = arr[i];
bool dupFound = false;
for (int j = i+1; j < n; j++) {
if (v == arr[j]) {
dupFound = true;
break;
}
}
if (!dupFound) {
i++;
continue;
}
// Copy values to the sub-array starting at position i,
// skipping all values equal to v.
int write = i, skipped = 0;
for (int j = i; j < n; j++) {
if (arr[j] != v) {
arr[write] = arr[j];
write++;
} else {
skipped++;
}
}
// The previous loop duplicated some non-v elements.
// We decrease n to make sure these duplicates are not
// considered in the output
n -= skipped;
}
return n;
}

Let's start with logistics (so to speak). An array always contains a fixed number of items. There's simply no way to start with an array of 5 items, and turn it into an array of 2 items. Simply can't be done.
So, as a starting point, you need to either return something like an std::vector that keeps track of its size along with the data it contains, or else you're going to need to track the size, and return something to indicate how many elements in the array are valid after the processing.
Probably the simplest way to do things would be to use something like an std::unoredered_map to count the items, then walk through the map, and insert an item in the output if (and only if) its count is 1.
std::unordered_map<int, std::size_t> counts;
for (int i=0;i<n; i++)
++counts[arr[i]];
std::vector<int> output;
for (auto item : counts)
if (item.second == 1)
output.push_back(item.first);
return output;
If you want to modify the data in place, I'd start by sorting the input data. Then you'll start with two indices: one for your "input" position, and one for your "output" position. output starts as zero, and input as 1.
The general idea from there is pretty simple: we look at data[input] and see if it's different from both the preceding and succeeding elements. If so, we copy it to data[output], and increment the output position.
Since this tries to look at both the preceding and succeeding elements, we have to include special cases for the beginning and end of the array. The first element is unique if it's different from the following, and the end is unique if it's different from the preceding. Code can look like this:
#include <algorithm>
#include <iostream>
unsigned remove_dupe(int *data, unsigned n) {
if (n < 2) {
return n;
}
std::sort(data, data+n);
unsigned output = data[0] != data[1];
for (unsigned input = output+1; input<n-1; input++)
if (data[input] != data[input-1] && data[input] != data[input+1])
data[output++] = data[input];
if (data[n-1] != data[n-2]) {
data[output++] = data[n-1];
}
return output;
}
template <class T, std::size_t N>
void test(T (&arr)[N]) {
unsigned end = remove_dupe(arr, N);
for (int i=0; i<end; i++)
std::cout << arr[i] << "\t";
std::cout << "\n";
}
int main() {
int arr0[] = {10, 20, 20, 30, 40, 40, 40, 50, 50};
int arr1[] = { 1, 2};
int arr2[] = { 1, 1};
test(arr0);
test(arr1);
test(arr2);
}
Result:
10 30
1 2

Another option that might be available is to sort() the array. When this is done, all duplicate values throughout the array are now adjacent. You simply compare element [n] with element [n+1] to see if they are the same. You can now find and count all duplicates in a single linear pass through the sorted array.
Sorting is one of the most heavily-studied class of algorithms in computer science, and very efficient processes can be developed which rely upon things being sorted a certain way.

Related

Duplicates in Array

Given an integer array nums sorted in non-decreasing order, remove some duplicates in-place such that each unique element appears at most twice. The relative order of the elements should be kept the same.
Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.
Return k after placing the final result in the first k slots of nums.
What is wrong with my code ??
map<int,int> m;
for(int i = 0 ; i < nums.size() ; i++){
m[nums[i]]++;
if(m[nums[i]] > 2)nums.erase(nums.begin() + i);
}
return nums.size();
From the given text, we can derive the following requirements
Given an integer array nums
sorted in non-decreasing order,
remove some duplicates in-place such that each unique element appears at most twice.
The relative order of the elements should be kept the same.
Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums.
More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result.
It does not matter what you leave beyond the first k elements
Return k after placing the final result in the first k slots of nums.
So, after elicitating the requirements, we know that we have a fixed size array, presumably (because of the simplicity of the task) a C-Style array or a C++ std::array. Because of the shown source code, we assume a std::array.
It will be sorted in increasing order. Their shall be an in-place removal of elements. So, no additional variables. The rest of the requirements already shows the solution.
--> If we find duplicates (more than 2) we will shift the rest of the values one to the left and overwrite one of the duplicates. Then the logical number of elements in the array will be one less. So, the loop must run one step less.
This ends up in a rather simple program:
#include <iostream>
#include <array>
// Array with some test values
constexpr int ArraySize = 25;
std::array<int, ArraySize> nums{ 1,2,2,2,3,3,3,4,4,4,4,4,6,5,5,5,5,5,6,6,6,6,6,6,9,9 };
int main() {
// Currentlogical end of the data in the array. In the beginning, last value in the array
size_t endIndex = nums.size();
// Check allelments from left to tright
for (size_t index = 0; index < endIndex;) {
// Check, if 3 elements are same
if ((index < (endIndex -2)) and nums[index] == nums[index + 1] and nums[index + 1] == nums[index + 2]) {
// Yes, found 3 same elements. We willdelete one, so the endIndex needs to be decremented
--endIndex;
// Now hsift all array elements one to the left
for (size_t shiftIndex = index + 2; shiftIndex < endIndex; ++shiftIndex)
nums[shiftIndex] = nums[shiftIndex + 1];
}
else ++index;
}
// SHow result
std::cout << endIndex << '\n';
}
I can offer the solution of your problem.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
void showContentSet(set<int>& input)
{
for(auto iterator=input.begin(); iterator!=input.end(); ++iterator)
{
cout<<*iterator<<", ";
}
return;
}
void showContentVector(vector<int>& input)
{
for(int i=0; i<input.size(); ++i)
{
cout<<input[i]<<", ";
}
return;
}
void solve()
{
vector<int> numbers={1, 2, 1, 3, 4, 5, 7, 5, 8, 5, 9, 5};
set<int> indicesToDelete;
for(int i=0; i<numbers.size(); ++i)
{
int count=0;
for(int j=0; j<numbers.size(); ++j)
{
if(numbers[i]==numbers[j])
{
++count;
if(count>2)
{
indicesToDelete.insert(j);
}
}
}
}
cout<<"indicesToDelete <- ";
showContentSet(indicesToDelete);
int newOrder=0;
cout<<endl<<"Before, numbers <- ";
showContentVector(numbers);
for(auto iterator=indicesToDelete.begin(); iterator!=indicesToDelete.end(); ++iterator)
{
numbers.erase(numbers.begin()+(*iterator-newOrder));
++newOrder;
}
cout<<endl<<"After, numbers <- ";
showContentVector(numbers);
cout<<endl;
return;
}
int main()
{
solve();
return 0;
}
Here is the result:
indicesToDelete <- 9, 11,
Before, numbers <- 1, 2, 1, 3, 4, 5, 7, 5, 8, 5, 9, 5,
After, numbers <- 1, 2, 1, 3, 4, 5, 7, 5, 8, 9,
I suggest using a frequency array.
frequency array works, That you count how many duplicates of each number while inputting, It's stored usually in an array called freq, Also can be stored in a map<int, int> or unordered_map<int, int>.
And because of input is in non-decreasing order, outputting this solution will be easy.
Note: this solution won't work if input numbers is bigger than 10^5
Solution:
#include <iostream>
const int N = 1e5 + 1; // Maximum size of input array
int n;
int nums[N], freq[N];
int main()
{
// Input
std::cin >> n;
for (int i = 0; i < n; i++)
{
std::cin >> nums[i];
freq[nums[i]]++;
}
// Outputting numbers, Using frequency array of it
for (int i = 0; i < N; i++)
{
if (freq[i] >= 1)
std::cout << i << ' ';
if (freq[i] >= 2)
std::cout << i << ' ';
}
return 0;
}
This is basically a conditional copy operation. Copy the entire range, but skip elements that have been copied twice already.
The following code makes exactly one pass over the entire range. More importantly it avoids erase operations, which will repeatedly shift all elements to the left.
vector<int> nums; // must be sorted already
if (nums.size()<=1)
return; // already done
// basically you copy all elements inside the vector
// but copy them only if the requirement has been met.
// `in` is the source iterator. It increments every time.
// `out` is the destination iterator. It increments only
// after a copy.
auto in=nums.begin();
auto out=nums.begin();
// current is the current 'int' value
int current=*in;
// `count` counts the repeat count of the current value
int count=0;
while (in!=nums.end()) {
if (*in==current) {
// The current value repeats itself, so increment
// the count value
++count;
} else {
// No, this is a new value.
// initialise current and count
current=*in;
count=1;
}
if (count<=2) {
// only if at most two elements of the same value
// copy the current value to `out`
*out=current;
++out;
}
// try next element
++in;
}
// out points to the last valid element + 1

iterating over vector elements backwards

I am trying to implement a small program that iterates over a 2d vector backwards and adds the value of the element.
If that element have already been added then I want to overwrite the value of the vector with 99.
So for example if number of climbs is four then add the of the program points should have the value 5 and the vector should look like this at the end
{1, 1 ,1},
{99, 1, 1},
{99(should start here), 99, 99}
But I keep getting a segmentation fault and I don't know whether I am iterating over the vector backwards incorrectly.
This is my full code
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<vector<int>> vect
{ {3}, //finish here
{1, 1 ,1},
{2, 1, 1},
{1, 1, 1} //start here
};
int points = 0;
for (int i = 0; i < vect.size(); i++)
{
for (int j = 0; j < vect[i].size(); j++)
{
cout << vect[i][j] << " ";
}
cout << endl;
}
int visited = 99;
int number_of_climbs = 4;
for(int i = 4; i >= 0; i--)
for (int j = 0; j < number_of_climbs; j++)
{
if(vect[i][j] != 99)
{
points += vect[i][j];
vect[i][j] = 99;;
continue;
}
}
return 0;
}
Looping over things backwards always trips me up too, especially when I accidentally declare my loop using an unsigned int. Your backwards loop is close to correct, but can be simplified into:
for (int i = vect.size() - 1; i >= 0; --i)
{
for (int j = vect[i].size() - 1; j >= 0; --j)
{
}
}
You need to start at size() - 1 because the only indices available are [0, size) (that is EXCLUSIVE of size), so in a vector of 4 elements, that's 0, 1, 2 and 3 (and you need to start at 3), which is likely the cause of your segmentation fault since you start your initial loop at 4 so it's immediately out-of-bounds.
Here in the inner loop you can see we account for the vectors being different sizes by just using the size of each vector.
It's also possible to use reverse iterators, but iterators can be confusing to people:
for (auto outer = vect.rbegin(); outer != vect.rend(); ++outer)
{
for (auto inner = outer->rbegin(); inner != (*outer).rend(); ++inner)
{
}
}
Note I've shown both ways to access the iterator, using it-> or (*it). Within the inner loop, you will simply use *inner to get the actual value held by the vector, so if you simply had std::cout << *inner << " "; that would print the 2d vector on one line in reverse order.
Even though we're looping in reverse, we still use ++

Advanced SelectionSort - Search two elements in one iteration

I've got a homework where I have to "improve" SelectionSort with following parameters:
Sorting an given list with an "improved" SelectionSort
In one iteration, finding the smallest and 2nd smallest element
Bring the smallest and 2nd smallest element in the right position.
Ok so far I wrote this c++ code:
#include <iostream>
#include <array>
#include <string>
using namespace std;
int main()
{
int min, min2;
// doesn't work
array<int, 9> list = { 97,34,15,25,27,4,19,41,68 };
/* this one works:
array<int, 10> list = { 4,8,1,3,10,6,5,7,9,2 };
*/
// First loop
for (int i = 0; i < list.size(); i+=2) {
min = i;
min2 = i + 1;
// 2nd Loop for searching the smallest elements
for (int j = i + 2; j < list.size(); j++) {
if (list.at(j) < list.at(min)) {
min = j;
}
// outer if -> stop before out of array
if (j+1 < list.size()) {
if (list.at(j+1) < list.at(min2)) {
min2 = j+1;
}
}
}
swap(list.at(i), list.at(min));
// Prevent out of array error
if (i + 1 < list.size()) {
swap(list.at(i+1), list.at(min2));
}
}
cout << '\n' << '\n';
cout << "Sorted list: " << endl;
for (int elem : list) {
cout << elem << ", ";
}
}
Of course it's sorting and this is the result... but not the one I was hoping for:
4, 97, 15, 19, 25, 34, 27, 41, 68,
I'm out of ideas and the only hint I got was: "no third loop".
I would appreciate any help :-)
EDIT:
Due the voting to hold on I try to specify the problem.
When I'm using high int-values for example the ones in the code, the sorting-algorithm doesn't work properly
List: array<int, 9> list = { 97,34,15,25,27,4,19,41,68 };
Result: 4, 97, 15, 19, 25, 34, 27, 41, 68,
As you can see the values on position 0, 2, 4, 6, 8 from the first if-statement were sorted properly but the others form the 2nd if-statement not.
When I change the int values for example to values from 1-10 and mix them random, the algorithm seems to work properly (thanks for the comment!):
List: array<int, 10> list = { 4,8,1,3,10,6,5,7,9,2 };
Result: 1, 2, 4, 3, 5, 6, 8, 7, 9, 10,
I'm out of ideas - Is it a programming error or a bad algorithm?
EDIT 3:
Here is my (finally working) updated code:
//array<int, 10> list = { 4,8,1,3,10,6,5,7,9,2 };
//array<int, 4> list = { 97,15,25,18 };
//array<int, 2> list = { 97,18 };
array<int, 3> list = { 4,5,3 };
// First loop
for (int i = 0; i < list.size(); i+=2) {
if (i == list.size() - 1) {
break;
}
min = i;
min2 = i + 1;
// Enforce that list.at(min) <= list.at(min2) -> Sorting pivot (element) for the algorithm to smallest, 2nd smallest.
if (list.at(min) > list.at(min2)) {
swap(list.at(min), list.at(min2));
}
// Second Loop
for (int j = i + 2; j < list.size(); ++j) {
if (list.at(j) < list.at(min)) {
min2 = min; // min2 points now on the 2nd smallest element
min = j; // min points now on the smallest element
}
else if (list.at(j) < list.at(min2)) {
min2 = j;
}
}
// Swapping the elements in the right position.
swap(list.at(i + 1), list.at(min2));
swap(list.at(i), list.at(min));
}
Results:
{ 4,8,1,3,10,6,5,7,9,2 } -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
{ 97,15,25,18 } -> 15, 18, 25, 97,
{ 97,18 } -> 18, 97,
{ 4,5,3 } -> 3, 4, 5,
Try your program with the array [97,18]. I think you'll find that it doesn't work, because the 2nd loop is never entered, and the lines at the end of the first loop won't swap anything.
When I said in my comment that min must be less than or equal to min2, I meant to say that you must ensure list.at(min) <= list.at(min2). My example above of the 2-item array shows why that's important.
The way to enforce that is to modify your first loop:
// First loop
for (int i = 0; i < list.size(); i+=2) {
if (i == list.size()-1) {
// There is an odd number of items in the list.
// At this point, we know that the last item is in place.
// So just exit.
break;
}
min = i;
min2 = i + 1;
// enforce list(min) <= list(min2)
if (list.at(min) > list.at(min2)) {
swap(list.at(min), list.at(min2));
}
// second loop
And, yes, you must maintain that in the inner loop. If you try with the array [4,5,3], the result will be [3,5,4].
That's primarily because in your inner loop, when you find that list.at(j) < list.at(min), you swap the items. But when list.at(min2) > list.at(min), what you've done is swap things out of order.
You should single-step your code in the debugger using that 3-element list to understand what's happening. If you don't know how to use the debugger, stop right now and learn. This type of programming error is very easy to discover when you can watch a line-by-line execution of your code. The alternative is to do it by hand: with a piece of paper and pencil, walk through the code step by step, writing down the change to every variable.
The other problem with your inner loop is that you're checking list.at(j+1) with list.at(min2). But you're only incrementing j by 1 each time, so you end up doing extra work. There's no reason to do that extra check. It'll be handled the next time through the loop.
The fix in the inner loop, assuming that you maintain the proper relationship between min and min2, is easy enough. If list.at(j) < list.at(min), then set min2=min (because min is now pointing to the second-smallest item), and set min=j. If list.at(j) < list.at(min2), then just set min2=j. The code looks like this:
for (j = i+2; j < list.size(); ++j) {
if (list.at(j) < list.at(min)) {
min2 = min;
min = j;
} else if (list.at(j) < list.at(min2)) {
min2 = j;
}
}
Now your code at the end of the outer loop works correctly.
Debugging
Run your program in the debugger with the array [4,5,3]. Place a breakpoint on the line just after the inner loop, here:
swap(list.at(i), list.at(min));
If you examine the array, you'll see that it's still [4,5,3]. But look at min and min2. You'll see that min=2 and min2=0. i is equal to 0. Now, what happens when when you swap the items at list[i] and list[min]? You get [3,5,4], and min2 no longer points to the second smallest item.
There are several different conditions in which something like this can happen. You have to think of those conditions, and handle them. The code I provided lets you find the smallest and 2nd smallest items. But you have to figure out how to swap things into the correct places after you've found them.

Problems with vectors, how to remove the arrays in my vectors?

I have created a function that creates all the possible solutions for a game that I am creating... Maybe some of you know the bullcow game.
First I created a function that creates a combination of numbers of max four integers and the combination can't have any repeating number in it... like...
'1234' is a solution but not '1223' because the '2' is repeating in the number. In total there is 5040 numbers between '0123' and '9999' that haven't repeating numbers.
Here is my function:
std::vector <std::array<unsigned, 4>> HittaAllaLosningar(){
std::vector <std::array<unsigned, 4>> Losningar;
for (unsigned i = 0; i < 10; i++) {
for (unsigned j = 0; j < 10; j++) {
for (unsigned k = 0; k < 10; k++) {
for (unsigned l = 0; l < 10; l++) {
if (i != j && i != k && i != l && j != k && j != l && k != l) {
Losningar.push_back({i,j,k,l});
}
}
}
}
}
return Losningar;
}
Now let's say I have the number '1234' and that is not the solution I am trying to find, I want to remove the solution '1234' from the array since that isn't a solution... how do I do that? have been trying to find for hours and can't find it. I have tried vector.erase but I get errors about unsigned and stuff... also its worth to mention the guesses are in strings.
What I am trying to do is, to take a string that I get from my program and if it isn't a solution I want to remove it from the vector if it exists in the vector.
Here is the code that creates the guess:
std::string Gissning(){
int random = RandomGen();
int a = 0;
int b = 0;
int c = 0;
int d = 0;
for (unsigned i = random-1; i < random; i++) {
for (unsigned j = 0; j < 4; j++) {
if (j == 0) {
a = v[i][j];
}
if (j == 1) {
b = v[i][j];
}
if (j == 2) {
c = v[i][j];
}
if (j == 3) {
d = v[i][j];
}
}
std::cout << std::endl;
AntalTry++;
}
std::ostringstream test;
test << a << b << c << d;
funka = test.str();
return funka;
}
The randomgen function is just a function so I can get a random number and then I go in the loop so I can take the element of the vector and then I get the integers of the array.
Thank you very much for taking your time to help me, I am very grateful!
You need to find the position of the element to erase.
std::array<unsigned, 4> needle{1, 2, 3, 4};
auto it = std::find(Losningar.begin(), Losningar.end(), needle);
if (it != Losningar.end()) { Losningar.erase(it); }
If you want to remove all the values that match, or you don't like checking against end, you can use std::remove and the two iterator overload of erase. This is known as the "erase-remove" idiom.
std::array<unsigned, 4> needle{1, 2, 3, 4};
Losningar.erase(std::remove(Losningar.begin(), Losningar.end(), needle), Losningar.end());
To erase from a vector you just need to use erase and give it an iterator, like so:
std::vector<std::array<unsigned, 4>> vec;
vec.push_back({1,2,3,4});
vec.push_back({4,3,2,1});
auto it = vec.begin(); //Get an iterator to first elements
it++; //Increment iterator, it now points at second element
it = vec.erase(it); // This erases the {4,3,2,1} array
After you erase the element, it is invalid because the element it was pointing to has been deleted. Ti continue to use the iterator you can take the return value from the erase function, a valid iterator to the next element after the one erased, in this the case end iterator.
It is however not very efficient to remove elements in the middle of a vector, due to how it works internally. If it's not important in what order the different solution are stored, a small trick can simplify and make your code faster. Let's say we have this.
std::vector<std::array<unsigned, 4>> vec;
vec.push_back({1,2,3,4});
vec.push_back({4,3,2,1});
vec.push_back({3,2,1,4});
To remove the middle one we then do
vec[1] = vec.back(); // Replace the value we want to delete
// with the value in the last element of the vector.
vec.pop_back(); //Remove the last element
This is quite simple if you have ready other functions:
using TestNumber = std::array<unsigned, 4>;
struct TestResult {
int bulls;
int cows;
}
// function which is used to calculate bulls and cows for given secred and guess
TestResult TestSecretGuess(const TestNumber& secret,
const TestNumber& guess)
{
// do it your self
… … …
return result;
}
void RemoveNotMatchingSolutions(const TestNumber& guess, TestResult result)
{
auto iter =
std::remove_if(possibleSolutions.begin(),
possibleSolutions.end(),
[&guess, result](const TestNumber& possibility)
{
return result == TestSecretGuess(possibility, guess);
});
possibleSolutions.erase(iter, possibleSolutions.end());
}
Disclaimer: it is possible to improve performance (you do not care about order of elements).

C++ sort ascending non-zero values

I'm a bit rusted with c++ and after one day of thinking I coulnd't come out with an efficient way of computing this problem.
Suppose I have an array of 5 float values
lints[5]={0, 0.5, 3, 0, 0.6};
I would like to introduce a new array:
ranks[5] that contains the ascending rank of the non-0 values of the array lints.
in this case the answer would read
ranks[1]=0;
ranks[2]=1;
ranks[3]=3;
ranks[4]=0;
ranks[5]=2;
In this example the 0 values returns rank 0 but they're not relevant since i only need the rank of positive values.
Thanks in advance
edit:
Thanks to everybody for help, this is what I found suiting my needs in case you have the same task :)
double lengths[5], ranks[5];
double temp;
int i,j;
lengths[0] = 2,lengths[1] = 0,lengths[2] = 1,lengths[3] = 0,lengths[4] = 4;
ranks[0] = 1, ranks[1] = 2, ranks[2] = 3, ranks[3] = 4, ranks[4] = 5;
for(i=0;i<4;i++){
for(j=0;j<4-i;j++){
if((lengths[j]>lengths[j+1] && lengths[j+1]) || lengths[j]==0){
// swap lenghts
temp=lengths[j];
lengths[j]=lengths[j+1];
lengths[j+1]=temp;
// swap ranks
temp=ranks[j];
ranks[j]=ranks[j+1];
ranks[j+1]=temp;
}
}
}
cheers.
You can use any sorting algorithm with a simple addition. When swapping 2 values you can swap index values too.
Create index values for initial indexes
ranks[5] = {1,2,3,4,5}; //or 0,1,2,3,4
for (int i = 0 ; i < 5 ; i++){
for(int j = 0 ; j < 5 ; j++){
//if array[i] < array[j]
//swap array[i] - array[j]
//swap ranks[i] - ranks[j]
}
}
As #cokceken said (I know answers shouldn't refer to other answers but I'm not a high enough Stack Overflow rank to comment on answers :/ ), use any simple sorting algorithm, and simply add in your own functionality for any special cases, such as values of 0 or negative values in your example.
For example, assuming you don't actually want to sort the original array and just create a new array that links indices in the array to their sorted rank,
array[arraySize] = // insert array here;
ranks[arraySize];
for (int i = 0; i < arraySize; i++){
int indexRank = 0;
for (int j = 0; j < arraySize; j++){
if (array[j] < array[i]){
indexRank++;
}
}
if (array[i] <= 0) {
ranks[i] = -1 // or whatever implementation you want here
} else {
ranks[i] = indexRank;
}
}
(note that arraySize must be a value and not a variable, since C++ does not let you statically define an array with a variable size)
I found this was easier if you keep separate values for the value, original position and the rank in a class:
#include <vector>
#include <iostream>
#include <algorithm>
struct Item {
float value;
int original_position;
int rank;
};
int main() {
float lints[5] = {0, 0.5, 3, 0, 0.6};
std::vector<Item> items{};
int index{};
for(auto i : lints)
items.push_back(Item{i,index++,0}); // assign index to original_position
std::sort(items.begin(), items.end(), [](auto& l, auto& r) {return l.value < r.value; }); // sort by float value
auto it = std::find_if(items.begin(), items.end(), [](auto& i) {return i.value > 0; }); // find first non-zero position (as iterator)
int new_rank_value{1}; // start numbering non-zero numbers from 1
std::for_each(it, items.end(), [&new_rank_value](auto& i) {i.rank = new_rank_value++; }); // assign non-zero numbers a rank value
std::sort(items.begin(), items.end(), [](auto& l, auto& r) {return l.original_position < r.original_position ; }); // sort by original position again
for(auto i : items)
std::cout << "ranks[" << i.original_position << "]=" << i.rank << ";\n";
}
Output:
ranks[0]=0;
ranks[1]=1;
ranks[2]=3;
ranks[3]=0;
ranks[4]=2;