Counting number of distinct integers in array - c++

To find the number of distinct numbers in an array from the lth to the rth index, I wrote a code block like:
int a[1000000];
//statements to input n number of terms from user in a.. along with l and r
int count=r-l+1; //assuming all numbers to be distinct
for(; l<=r; l++){
for(int i=l+1; i<=r; i++){
if(a[l]==a[i]){
count--;
break;
}
}
}
cout<<count<<'\n';
Explanation
For an array say, a=5 6 1 1 3 2 5 7 1 2 of ten elements. If we wish to check the number of distinct numbers between a[1] and a[8] that is the second and the 9th elements (including both), The logic I tried to implement would first take count=8 (no. of elements to be considered) and then it starts from a[1] that is 6 and checks for any other 6 after it, if it does find, it decreases the count by one and goes on for the next number in the row. So that if there are any more occurrence of 6 after that one, it would not be included twice.
Problem I tried small test cases and it works. But when I tried with bigger data, it did not work, so I wanted to know where would my logic fail?
Bigger data, as in integrated with other parts of the program and then used. Which gave incorrect output

You can try to use std::set
Basic idea is to add all the elements into your new set, and just output the size of your set.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int main()
{
int l = 1, r = 6;
int arr[] = {1, 1, 2, 3, 4, 5, 5, 5, 5};
set<int> s(&arr[l], &arr[r + 1]);
cout << s.size() << endl;
return 0;
}

Here is an answer that does not use std::set, although that solution is probably simpler.
#include <algorithm>
#include <vector>
int main()
{
int input[10]{5, 6, 1, 1, 3, 2, 5, 7, 1, 2}; //because you like raw arrays, I guess?
std::vector<int> result(std::cbegin(input), std::cend(input)); //result now contains all of input
std::sort(std::begin(result), std::end(result)); //result now holds 1 1 1 2 2 3 5 5 6 7
result.erase(std::unique(std::begin(result), std::end(result)), std::end(result)); //result now holds 1 2 3 5 6 7
result.size(); //gives the count of distinct integers in the given array
}
Here it is live on Coliru if you're into that.
--
EDIT: Here, have a short version of the set solution, too.
#include <set>
int main()
{
int input[10]{5, 6, 1, 1, 3, 2, 5, 7, 1, 2}; //because you like raw arrays, I guess?
std::set<int> result(std::cbegin(input), std::cend(input));
result.size();
}

The first question to ask with this type of problem is what is the possible range of the values. if the range of numbers N is "reasonably small", then you can use a boolean array of size N to indicate whether the number corresponding to the index is present. You iterate from l to r, setting the flag, and if the flag was not already set increment a counter.
count = 0;
for(int i=l; i<=r; i++) {
if (! isthere[arr[i]]) {
count++;
isthere[arr[i]] = TRUE;
}
}
In terms of complexity, both this approach and the one based on set are O(n), but this one is faster as there is no hashing involved. For small N, for example for numbers between 0-255, most likely this is also likely to be less memory intensive. For larger N, for example if any 32-bit integers is allowed, the set based approach is more suitable.

You said you didn't mind another solution. So here it is. It uses set - a structure that stores only unique elements. By the way, on the bigger data - it will much faster than solution with two cycles.
set<int> a1;
for (int i = l; i <= r; i++)
{
a1.insert(a[i]);
}
cout << a1.size();

In the below process I'm giving process of counting unique numbers. In this technique you just get unique elements in an array. this process will update your array with garbage value. So in this process you can't use this array (that we will use) further anymore. This array will automatically resize with distinct elements.
#include <stdio.h>
#include <iostream>
#include <algorithm> // for using unique (library function)
int main(){
int arr[] = {1, 1, 2, 2, 3, 3};
int len = sizeof(arr)/sizeof(*arr); // finding size of arr (array)
int unique_sz = std:: unique(arr, arr + len)-arr; // Counting unique elements in arr (Array).
std:: cout << unique_sz << '\n'; // Printing number of unique elements in this array.
return 0;
}
If you want to handle that problem (That I told before), you can follow this process. You can handle this by coping your array in another array.
#include <stdio.h>
#include <iostream>
#include <algorithm> // for using copy & unique (library functions)
#include <string.h> // for using memcpy (library function)
int main(){
int arr[] = {1, 1, 2, 2, 3, 3};
int brr[100]; // we will copy arr (Array) to brr (Array)
int len = sizeof(arr)/sizeof(*arr); // finding size of arr (array)
std:: copy(arr, arr+len, brr); // which will work on C++ only (you have to use #include <algorithm>
memcpy(brr, arr, len*(sizeof(int))); // which will work on C only
int unique_sz = std:: unique(arr, arr+len)-arr; // Counting unique elements in arr (Array).
std:: cout << unique_sz << '\n'; // Printing number of unique elements in this array.
for(int i=0; i<len; i++){ // Here is your old array, that we store to brr (Array) from arr (Array).
std:: cout << brr[i] << " ";
}
return 0;
}

Personally, I'd just use standard algorithms
#include<algorithm>
#include <iostream>
int main()
{
int arr[] = {1, 1, 2, 3, 4, 5, 5, 5, 5};
int *end = arr + sizeof(arr)/sizeof(*arr);
std::sort(arr, end);
int *p = std::unique(arr, end);
std::cout << (int)(p - arr) << '\n';
}
This obviously relies on being allowed to modify the array (any duplicates are moved to the end of arr). But it is easy to create a copy of an array if needed and work on the copy.

TL;DR: Use this:
template<typename InputIt>
std::size_t countUniqueElements(InputIt first, InputIt last) {
using value_t = typename std::iterator_traits<InputIt>::value_type;
return std::unordered_set<value_t>(first, last).size();
}
There are two approaches:
Insert everything into a set, count the set. Because you don't care about the order you can use a std::unordered_set which will be faster than std::set. std::set is implemented as a tree which does a lot of allocations so it can be slow.
Use std::sort. If you want to preserve the original array you'll need to make a copy of it.
Here is a complete example.
#include <algorithm>
#include <cstdint>
#include <vector>
#include <unordered_set>
#include <iostream>
template<typename RandomIt>
std::size_t countUniqueElementsSort(RandomIt first, RandomIt last) {
if (first == last)
return 0;
std::sort(first, last);
std::size_t count = 1;
auto val = *first;
while (++first != last) {
if (*first != val) {
++count;
}
val = *first;
}
return count;
}
template<typename InputIt>
std::size_t countUniqueElementsSet(InputIt first, InputIt last) {
using value_t = typename std::iterator_traits<InputIt>::value_type;
return std::unordered_set<value_t>(first, last).size();
}
int main() {
std::vector<int> v = {1, 3, 4, 4, 3, 6};
std::cout << countUniqueElementsSet(v.begin(), v.end()) << "\n";
std::cout << countUniqueElementsSort(v.begin(), v.end()) << "\n";
int v2[] = {1, 3, 4, 4, 3, 6};
std::cout << countUniqueElementsSet(v2, v2 + 6) << "\n";
std::cout << countUniqueElementsSort(v2, v2 + 6) << "\n";
}
Using that loop in the sort version should be faster than std::unique.
The complexity of 2. is worse than 1. - the average case is O(N) vs O(N log N). But it avoids allocation so may end up being faster for small arrays or ones that are already sorted or mostly already sorted.
You should definitely not use std::set, and probably not use std::unique (though it does lead to fewer lines of code, and won't make that much difference to performance so up to you).
In any case, in most cases you should go with the set version - it's a lot simpler simpler and should be faster in almost all cases.
As other people have mentioned, if you know your input domain is small you can use a bool array instead of an unordered_set.

Related

Get number of same values in arrays in C++

I need a function int countDifferentNumbers(int v[], int n) which counts how many different values the array v with n entries contains.
Example:
It should return the result 3 for the array v = {1, 5, 5, 8, 1, 1} because the array contains only 3 different values.
This is how the code looks like so far:
int countDifferentNumbers(int v[], int n)
{
int counter = 0;
for(int i = 0; i < n; ++i)
{
for(int j = i; j < n; ++j)
{
if(v[i] == v[j + 1])
{
cout << "match" << endl;
counter++;
cout << v[i] << endl;
}
}
}
return counter;
}
I would appreciate an explanation of what is wrong in my function and how I need to redesign it.
Note: Unfortunately, I have not found a suitable thread for this either. All threads with my problems were solved in Java and Python languages.
Recently I see more and more answers here on SO that lead users in the wrong direction by giving bad answers.
Also, for C++, the question has already been answered in the comment by Igor Tandetnik, and that should finally be used.
But let me answer the question of the OP as asked. What is wrong with my function? OK, there are several aspects. Let us first look at the style.
You have 0 lines of comments, so the code quality is 0. If you would write comments, then you would already find most bugs by yourself, because then, you need to explain your own wrong statements.
Then please see your source code with my amendments. I added the problems as comment.
// This is just a dumped function and not a minimum reproducible example
// All header files are messing
// Obviously "using namespace std;" was used that should NEVER be done
// The function should retrun an unsigned value, best size_t, because a count can never be negative
// Same for n, that is the size of an array. Can also never be negative
// C-sytle arrays should NEVER be used in C++. NEVER. Use std::vector or std::array instead
int countDifferentNumbers(int v[], int n)
{
int counter = 0; // Now in C++ we can use braced initialzation instead of assignement
for (int i = 0; i < n; ++i)
{
for (int j = i; j < n; ++j)
{
if (v[i] == v[j + 1]) // Accessing out of bounds element
{
cout << "match" << endl; // Now endl needed here. Can all be done in one cout statement in one line
counter++; // Always counting up the same counter for all kind of double numbers.
cout << v[i] << endl;
}
}
}
return counter;
That was one point of the answer. But now the second point. Evene more important. The algorithm or the design is wrong. And finding the correct solution, this thinking before codingt, you need to do, before you write any line of code.
You obviously want to find the count of unique numbers in an array.
Then you could look what is already there on Stackoverflow. You would probaly find 20 answers already that coud give you a hint.
You could use std::unique. Please see here for a description. This function sounds like it does what you want, right? Some example implementation:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>
// If you want to keep the original data, remove the reference-specifier &
size_t countDifferentNumbers(std::vector<int>& v) {
std::sort(v.begin(), v.end()); // Sorting is precondition for std::unique
v.erase(std::unique(v.begin(), v.end()), v.end()); // Erase all non-unique elements
return v.size(); // Return the result
}
int main() {
std::vector test{ 1, 5, 5, 8, 1, 1 }; // Some test data
std::cout << countDifferentNumbers(test) << '\n'; // SHow result to user
return 0;
}
Then, we could count the occurence of each number in a std::map or std::unordered_map. And the number of counters will be the result. Example:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <algorithm>
// If you want to keep the original data, remove the reference-specifier &
size_t countDifferentNumbers(std::vector<int>& v) {
std::unordered_map<int, size_t> counter{}; // Here we will count all occurences of different numbers
for (const int i : v) counter[i]++; // Iterate over vector and count different numbers
return counter.size(); // Count of different numbers
}
int main() {
std::vector test{ 1, 5, 5, 8, 1, 1 }; // Some test data
std::cout << countDifferentNumbers(test) << '\n'; // Show result to user
return 0;
}
But, then, thinking further, about what conatiners we could use, we will find out the answer from Igor Tandetnik. There are 2 containers that can hold unique values only. No double values. And these are: std::set and std::unordered_set., So, we can simply copy the data into one of those containers, and, only unique values will be stored there.
There are many ways to get the data into a set. But the simplest one is to use its range constructor. Then, we have unique elements, and, the containers size function will give the result:
See here: Constructor Number 2.
The result will be a function with one line like this
#include <iostream>
#include <unordered_set>
#include <vector>
// If you want to keep the original data, remove the reference-specifier &
size_t countDifferentNumbers(std::vector<int>& v) {
return std::unordered_set<int>(v.begin(), v.end()).size();
}
int main() {
std::vector test{ 1, 5, 5, 8, 1, 1 }; // Some test data
std::cout << countDifferentNumbers(test) << '\n'; // Show result to user
return 0;
}
And since functions with one line are often not so usefull, we can also write the final solution:
#include <iostream>
#include <unordered_set>
#include <vector>
int main() {
std::vector test{ 1, 5, 5, 8, 1, 1 }; // Some test data
std::cout << std::unordered_set<int>(test.begin(), test.end()).size() << '\n'; // Show result to user
return 0;
}
So, by analyzing the problem and choosing the right algorithm and container and using C++, we come to the most easy solution.
Please enable C++17 for your compiler.
first sort the array v. if n >0 then initially there must be one number which is unique so just increment the value of counter once. then with loop check if the two consecutive number are same or not. if same do nothing else increment the value of counter.
if you are writing code in c then use qsort. #include <stdlib.h> add this in header and. use qsort() func
here is the code:
#include <bits/stdc++.h>
using namespace std;
int countDifferentNumbers(int v[] , int n)
{
int counter = 0;
sort(v, v+ n); // if you are writing code in c then just write a decent sort algorithm.
if (n>0 ){
printf("%d\n", v[0]);
counter ++;
}
for(int i = 0; i < n-1; ++i)
{
if(v[i] == v[i+1]){
continue;
} else {
printf("%d\n", v[i+1]);
counter++;
}
}
return counter;
}
int main()
{
int v[] = {1, 5, 5, 8, 1, 1};
int result = countDifferentNumbers(v,6);
printf("unique number %d", result );
return 0;
}

C++ - difference between first two elements

I've recently began to work with C++ std::sets, so there is a question I did not find answer to in Google.
I have a std::set of some int values (e.g., let it be 1, 2, 3, 4, 5). The task is to calculate difference between two first elements. Is it possible to do with C++?
I'm using data structure std::set; And I know that first element can be got like that:
int diff = *arSeq.begin();
Where arSeq is mentioned set.
Is there any way to get the second element?
Yes it is possible. Access the first element via the std::set::begin iterator and the next one using the std::next function.
#include <iostream>
#include <set>
int main() {
std::set<int> s = { 1, 2, 3, 4, 5 };
auto result = *s.begin() - *std::next(s.begin());
std::cout << result;
}
Please note that you will again get the same result of -1 even if you defined your set as:
std::set<int> s = { 5, 3, 1, 4, 2 };
because the std::set is a container that:
contains a sorted set of unique objects...
You can do it the following way
#include <iostream>
#include <set>
#include <iterator>
int main()
{
std::set<int> s({ 1, 2, 3, 4, 5 });
long long int diff = 0;
if (not (s.size() < 2))
{
auto first = s.begin();
auto second = std::next(first);
diff = *first < *second ? ( long long int )*second - *first : ( long long int )*first - *second;
}
std::cout << "difference = " << diff << std::endl;
return 0;
}
What did you mean by first two elements. First two element inserted? then its not possible.
Set elements are sorted with given comparator. By default primitive elements are sorted in ascending order. See the output of following program.
// set::begin/end
#include <iostream>
#include <set>
#include<functional>
int main ()
{
int myints[] = {75,23,65,42,13};
std::set<int, std::greater<int>> myset (myints,myints+5);
auto first = myset.begin();
auto second = std::next(first);
std::cout <<*first<< ", " <<*second<<"\n";
return 0;
}`enter code here`

Sum of different numbers in an array

I want to have a function that returns the sum of different (non duplicate) values from an array: if I have {3, 3, 1, 5}, I want to have sum of 3 + 1 + 5 = 9.
My attempt was:
int sumdiff(int* t, int size){
int sum=0;
for (int i=0; i<=size;i++){
for(int j=i; j<=size;j++){
if(t[i]!=t[j])
sum=sum+t[i];
}
}
return sum;
}
int main()
{
int t[4]={3, 3, 1, 5};
cout << sumdiff(t, 4);
}
It returns 25 and I think I know why, but I do not know how to improve it. What should I change?
Put all the items in a set, then count them.
Sets are data structures that hold only one element of each value (i.e., each of their elements is unique; if you try to add the same value more than once, only one instance will be count).
You can take a look in this interesting question about the most elegant way of doing that for ints.
First of all, your loop should be for (int i=0; i<size;i++). Your actual code is accessing out of the bounds of the array.
Then, if you don't want to use STL containers and algorithms (but you should), you can modify your code as follows:
int sumdiff(int* t, int size){
int sum=0;
for (int i=0; i<size;i++){
// check if the value was previously added
bool should_sum = true;
for(int j=0; should_sum && j<i;j++){
if(t[i]==t[j])
should_sum = false;
}
if(should_sum)
sum=sum+t[i];
}
return sum;
}
int main()
{
int t[4]={3, 3, 1, 5};
cout << sumdiff(t, 4);
}
You could:
Store your array contents into an std::unordered_set first. By doing so, you'd essentially get rid of the duplicates automatically.
Then call std::accumulate to compute the sum
**wasthishelpful's answer was exactly what i was talking about. I saw his post after i posted mine.
So, you're trying to check the duplicate number using your inner loop.
However, your outer loop will loop 4 times no matter what which gives you wrong result.
Try,
Do only checking in inner loop. (use a flag to record if false)
Do your sum outside of inner loop. (do the sum when flag is true)
Here is another solution using std::accumulate, but it iterates over the original elements in the call to std::accumulate, and builds the set and keeps a running total as each number in the array is encountered:
#include <iostream>
#include <numeric>
#include <set>
int main()
{
int t[4] = { 3, 3, 1, 5 };
std::set<int> mySet;
int mySum = std::accumulate(std::begin(t), std::end(t), 0,
[&](int n, int n2){return n += mySet.insert(n2).second?n2:0;});
std::cout << "The sum is: " << mySum << std::endl;
return 0;
}
The way it works is that std::insert() will return a pair tbat determines if the item was inserted. The second of the pair is a bool that denotes whether the item was inserted in the set. We only add onto the total if the insertion is successful, otherwise we add 0.
Live Example
Insert array elements into a set and use the std::accumulate function:
#include <iostream>
#include <numeric>
#include <set>
int main()
{
int t[4] = { 3, 3, 1, 5 };
std::set<int> mySet(std::begin(t), std::end(t));
int mySum = std::accumulate(mySet.begin(), mySet.end(), 0);
std::cout << "The sum is: " << mySum << std::endl;
return 0;
}

How to compare vector with array?

I would like to compare vector with an array. Elements in vector and in array are in different order, unsorted and can duplicated. E.g.
Below are the same:
vector<int> lvector = {5,7,3,1,2,7};
int larray[6] = {3,5,1,7,2,7}
Below, not the same:
vector<int> lvector = {5,7,3,1,2,7,5};
int larray[7] = {3,5,1,7,2,7,3}
And something like this is also not the same:
vector<int> lvector = {1,1,1,1,2,2};
int larray[6] = {1,1,1,1,1,2}
Now I need to check if vector and array have got the same elements. I can't modify the vector and the array, but I can create a new container and copy the element from vector and array to this new container and then copare them. I am asking about this, because I would like to do this in en efficient way. Thanks.
That's a variant of what proposed by soon:
#include <iostream>
#include <unordered_set>
#include <vector>
int main()
{
std::vector<int> v{5, 7, 3, 1, 2, 7};
int arr[] = {3, 5, 1, 7, 2, 7};
std::vector<int> mv(std::begin(v), std::end(v));
std::vector<int> ma(std::begin(arr), std::end(arr));
std::sort(mv.begin(), mv.end()) ;
std::sort(ma.begin(), ma.end()) ;
std::cout << "Are equal? " << (mv == ma) << std::endl;
return 0;
}
There are a lot of different ways of solving this problem, each has proc and cons.
Some pre-tests
Obviously, two ranges cannot be equal, if they have different size.
You could calculate an order independent hash function for elements in the ranges (thanks, #Michael Anderson). It could be a sum of elements, or you just could xor them all. Arrays with different hash value cannot be equal.
std::unordered_multiset
You could create an unordered_multiset, which holds frequency of elements in the range. While it has linear complexity in average, it may be O(n^2) because of hash collisions. Quote from the Standard (N3337, § 23.5.7.2):
Complexity: Average case linear, worst case quadratic.
However, you should also remember the complexity of std::unordered_set::operator==:
For unordered_set and unordered_map, the complexity of
operator== (i.e., the number of calls to the == operator of the
value_type, to the predicate returned by key_equal(), and to the
hasher returned by hash_function()) is proportional to N in the
average case and to N^2 in the worst case, where N is a.size().
For unordered_multiset and unordered_multimap, the complexity of
operator== is proportional to sum of Ei^2 in the average case and
to N^2 in the worst case, where N is a.size(), and Ei is the
size of the ith equivalent-key group in a.
However, if the
respective elements of each corresponding pair of equivalent-key
groups Eai and Ebi are arranged in the same order (as is commonly
the case, e.g., if a and b are unmodified copies of the same
container), then the average-case complexity for unordered_multiset
and unordered_multimap becomes proportional to N (but worst-case
complexity remains O(N2), e.g., for a pathologically bad hash
function).
Example:
#include <iostream>
#include <unordered_set>
#include <vector>
int main()
{
std::vector<int> v{5, 7, 3, 1, 2, 7};
int arr[] = {3, 5, 1, 7, 2, 7};
std::unordered_multiset<int> mv(std::begin(v), std::end(v));
std::unordered_multiset<int> ma(std::begin(arr), std::end(arr));
std::cout << "Are equal? " << (mv == ma) << std::endl;
return 0;
}
std::sort
You could compare sorted copies of your range. According to the Standard (N3337, § 25.4.1.1) , it has O(n * log(n)) complexity:
Complexity: O(N log(N)) (where N == last - first) comparisons.
Example:
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v{5, 7, 3, 1, 2, 7};
int arr[] = {3, 5, 1, 7, 2, 7};
std::vector<int> sv(v);
std::vector<int> sa(std::begin(arr), std::end(arr));
std::sort(std::begin(sv), std::end(sv));
std::sort(std::begin(sa), std::end(sa));
std::cout << "Are equal? " << (sv == sa) << std::endl;
return 0;
}
Here is template function to compare vector with array:
#include <array>
#include <algorithm>
#include <vector>
template <class T, std::size_t N>
bool equal(const std::vector<T>& v, const std::array<T, N>& a)
{
if (v.size() != N)
return false;
return std::equal(v.begin(), v.end(), a.begin());
}
Usage example:
std::vector<int> v = {1, 2, 3};
std::array<int, 4> a = {1, 2, 3, 4};
bool eq = equal(v, a);
At first convert the array into v1 vector.
v={1,1,2,3,4}; vector and
v1={1,1,2,3,4}; converted from array
bool f=0;
if(equal(v.begin(),v.end(),v1.begin())) //compare two vector, if equal return true
{
f=1;
}
}
if(f==1)
cout<<"Yes"<<endl;
else cout<<"No"<<endl;

How to get distinct values from an arrays of different sizes?

Q:
arr1[]={1,1,1,2,5,5,6,6,6,6,8,7,9}
Ans:
values[]={1,2,5,6,7,9}
Q:
arr1[]={1,1,1,2,5,5,6,6,6,6,8,7,9,101,1502,1502,1,9}
Ans:
values[]={1,2,5,6,7,9,101,1502}
here is what i tried but not working
for(int i=0;i<(index-1);i++) {
if(data[i].age != data[i+1].age) {
c=new list;
c->value=data[i].age;
c->next=NULL; clas++;
if(age_head==NULL) {
p=c; age_head=c;
}
for(c=age_head;c!=NULL,c->next!=NULL;p=c,c=c->next) {
if(data[i].age!=c->value)
found=false;
else
found=true;
}
if((age_head!=NULL)&& (found=false)) {
p->next=c; c->next=NULL;
}
}
}
This is not the most efficient, but it has some values:
It uses STL objects
It uses a cool little known template trick for knowing at compile time the size of your C-like arrays
...
int a[] = {1,1,1,2,5,5,6,6,6,6,8,7,9} ;
int b[] = {1,1,1,2,5,5,6,6,6,6,8,7,9,101,1502,1502,1,9} ;
// function setting the set values
template<size_t size>
void findDistinctValues(std::set<int> & p_values, int (&p_array)[size])
{
// Code modified after Jacob's excellent comment
p_values.clear() ;
p_values.insert(p_array, p_array + size) ;
}
void foo()
{
std::set<int> values ;
findDistinctValues(values, a) ;
// values now contain {1, 2, 5, 6, 7, 8, 9}
findDistinctValues(values, b) ;
// values now contain {1, 2, 5, 6, 7, 8, 9, 101, 1502}
}
Another version could return the set, instead of taking it by reference. It would then be:
int a[] = {1,1,1,2,5,5,6,6,6,6,8,7,9} ;
int b[] = {1,1,1,2,5,5,6,6,6,6,8,7,9,101,1502,1502,1,9} ;
// function returning the set
template<size_t size>
std::set<int> findDistinctValues(int (&p_array)[size])
{
// Code modified after Jacob's excellent comment
return std::set<int>(p_array, p_array + size) ;
}
void foo()
{
std::set<int> valuesOne = findDistinctValues(a) ;
// valuesOne now contain {1, 2, 5, 6, 7, 8, 9}
std::set<int> valuesTwo = findDistinctValues(b) ;
// valuesTwo now contain {1, 2, 5, 6, 7, 8, 9, 101, 1502}
}
The first thing I spot in your code is
if((age_head!=NULL)&& (found=false)) {
you use assignment (=) instead of equality (==). The expression should be
if((age_head!=NULL)&& (found==false)) {
Then, in this loop
for(c=age_head;c!=NULL,c->next!=NULL;p=c,c=c->next) {
you are looking for a value in the list. However, in its current form, when the loop terminates, found will show whether the last element in the list equals to c->value. You need to check for found in the loop condition (and you need to AND the expressions instead of listing them separated by comma!):
for(c=age_head, found = false; !found && c!=NULL && c->next!=NULL; ...) {
The result of the comma operator is the result of the last subexpression inside - this is definitely not what you want. Moreover, with comma all subexpressions are evaluated, which results in dereferencing a null pointer if c == NULL - whereas the && operator is evaluated lazily, thus c->next!=NULL is evaluated only if c != NULL.
The next thing is that you need to search for the value in the list before you add it to the list! Also note that you are trying to check for two different things: that the actual data element is different from the next one, and that its value is not yet added to the list. The second condition is stronger - it will always work, while the first only works if the input data is ordered. So you can omit the first check altogether. The result of all the above, plus some more simplifications and clarifications, is
for(int i=0;i<index;i++) {
for(list* c=age_head, found=false; !found&&c&&c->next; p=c,c=c->next) {
if(data[i].age==c->value)
found=true;
}
if(!found) {
list* newc=new list;
newc->value=data[i].age;
newc->next=NULL;
clas++;
if(age_head==NULL) {
p=newc; age_head=newc;
} else {
p->next=newc; newc->next=NULL;
}
}
}
I still don't guarantee that your linked list handling logic is right though :-) In its current form, your code is hard to understand, because the different logical steps are not separated. With a bit of refactoring, the code could look a lot clearer, e.g.
for(int i=0;i<index;i++) {
if(!foundInList(data[i].age)) {
addToList(data[i].age);
}
}
Of course the simplest and most efficient would be using STL containers/algorithms instead, as shown in other answers. But I think there is much more educational value in improving your first attempt :-)
If the output need not to be sorted, you can use a Hashtable.
E.g. something like this:
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
#include <boost/unordered_set.hpp>
#include <vector>
using namespace std;
using namespace boost;
int main() {
int arr1[]={1,1,1,2,5,5,6,6,6,6,8,7,9};
size_t n = sizeof(arr1)/sizeof(int);
unordered_set<int> h;
for (size_t i = 0; i < n; ++i)
h.insert(arr1[i]);
vector<int> values;
foreach(int a, h)
values.push_back(a);
return 0;
}
The runtime is then in O(n).
An alternative to that is sorting the array and then to eliminate neighboring identical elements (advantage only STL is needed). But then the runtime is in O(n log n):
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int arr1[]={1,1,1,2,5,5,6,6,6,6,8,7,9};
size_t n = sizeof(arr1)/sizeof(int);
sort(arr1, arr1+n);
int *end = unique(arr1, arr1+n);
vector<int> values(arr1, end);
return 0;
}
Easily done using STL.
int array[] = { 1, 1, 2, 2, 1, 3, 3, 4, 5, 4, 4, 1, 1, 2 };
int nElements = sizeof(array)/sizeof(array[0]);
std::sort(&array[0], &array[nElements]);
int newSize = std::unique(&array[0], &array[nElements]) - &array[0];
first you need to sort the array and than do something like this:
for(int i = 0; i < size -1; i++)
{
if(array[i]!=array[i+1])
unique++;
// store it wherever you want to.
stored.push(array[i]);
}
#include <vector>
#include <algorithm>
#include <iostream>
int
main ()
{
int array[] = { 1, 1, 2, 2, 1, 3, 3, 4, 5, 4, 4, 1, 1, 2 };
std::vector < int >values;
values.push_back (array[0]);
for (int i = 1; i < sizeof (array) / sizeof (int); ++i)
{
std::vector < int >::iterator it =
std::find (values.begin (), values.end (), array[i]);
if (it == values.end ())
values.push_back (array[i]);
}
std::cout << "Result:" << std::endl;
for (int i = 0; i < values.size (); i++)
std::cout << values[i] << std::endl;
}
This seems to be a duplicate of Removing duplicates in an array while preserving the order in C++
While the wording of the question is different, the result is the same.
Based on above ideas/codes, I am able to accomplish my job on finding distinct values in C++ array. Thanks every one who replied on this thread.
#include <set>
#include <iostream>
using namespace std;
// function setting the set values
template<size_t size>
void findDistinctValues(std::set<int> & p_values,int (&p_array)[size])
{
// Code modified after Jacob's excellent comment
p_values.clear() ;
p_values.insert(p_array, p_array + size) ;
}
void findDistinctValues2( int arr[],int size)
{
std::set<int> values_1 ;
std::set<int>::iterator it_1;
values_1.clear();
values_1.insert(arr,arr+size);
for (it_1=values_1.begin(); it_1!=values_1.end(); ++it_1)
std::cout << ' ' << *it_1<<endl;
}
int main()
{
int arr[] = {1,6100,4,94,93,-6,2,4,4,5,5,2500,5,4,5,2,3,6,1,15,16,0,0,99,0,0,34,99,6100,2500};
std::set<int> values ;
std::set<int>::iterator it;
int arr_size = sizeof(arr)/sizeof(int);
printf("Total no of array variables: %d\n",arr_size);
printf("Output from findDistinctValues (function 1)\n ");
findDistinctValues(values, arr) ;
for (it=values.begin(); it!=values.end(); ++it)
std::cout << ' ' << *it<<endl;
std::cout<<endl;
std::cout<<values.size()<<endl; //find the size of distict values
printf("Output from findDistinctValues (function 2) \n ");
findDistinctValues2(arr,arr_size);
getchar();
return 0;
}