Erasing duplicates from nested map (k-core algorithm) - c++

i am trying to finish a homework and came with an error.
What i am trying to do is: There is an unordered map with keys containing values as unordered_set which may have keys inside that "may have the same names as keys of unordered outer map".
To be spesific, i am trying to implement a k-core algorithm, the count and k numbers are there for it. I am trying to erase unordered_map keys (lets call P) that have unordered_sets(containing neighbors, call N) which have lower than k sizes.
But since i delete the N entry from the P node, i also need to find the N entry from the outer map and find P and delete it there.
The following code is what i came up with so far (there is more but i don't want plagiarism or anything). During the debugging i noticed that somewhat cout<<nodes.size() never executes. I don't know what is the problem. Probably the inner while infinitely loops, and it is related with how iterators function with erase etc. but i am not sure. Hope it is clear.
EDIT : I have updated the code according to the suggestion, but there is a new problem: for k=1 the amount of nodes are right. But after k=2,3,4.. there is infinite loop of outermost loop. And the size stays constant (after taking out nodes with k=1)
while(nodes.size()!=0)
{
int count = 0;
while(stillLeft){
stillLeft = 0;
it = nodes.begin();
while(it != nodes.end()){
if(it->second.size()<=k){
stillLeft = 1;
++count;
for ( innerit = it->second.begin(); innerit != it->second.end();innerit++ ){
nodes.find(*innerit)->second.erase(it->first);
}
it = nodes.erase(it);
}
else{
++it;
}
}
}
cout << nodes.size() << endl;
kCoreNumbers.push_back(count);
++k;
}

You don't need to loop till while(nodes.size()!=0) i.e. till nodes is empty. After running the k-core algorithm, you might be left with nodes whose vertexes have a degree greater than k. You only need to loop till there are no more nodes that have a degree less than k.
Your initial code was in the right direction but with the problem of deleting a node in the map while traversing it. I solved it by just adding the vertices to be deleted to a vector while iterating the graph and then after each graph traversal just deleted all the nodes makred for deletion. Here's my version of code, based off yours with a few variable name changes to reflect what they are:
void runKCore(size_t k) {
bool nodesDeleted;
do {
// To keep track of how many times the loop needs to run
nodesDeleted = false;
// To keep track of the nodes to be deleted after one pass
std::vector<int> markDeleted;
for(auto const& node: graph) {
auto vertex = node.first;
auto const& neighbours = node.second;
// Pick vertex if it has less than k neighbours
if(neighbours.size() < k) {
for(auto neighbour: neighbours) {
// Get the neighbour's list of vertices
auto& neighBoursEdges = graph.at(neighbour);
// Remove vertex from this list
neighBoursEdges.erase(vertex);
}
// Mark this node as deleted
markDeleted.push_back(vertex);
}
}
// Update nodesDeleted if any nodes were marked for deletion
nodesDeleted = markDeleted.size() > 0;
for(auto node: markDeleted) {
// Erase the node and it's neighbours
graph.erase(node);
}
}while(nodesDeleted);
}
Here is the complete program.
For an initial graph
translating to initial graph
8: 6 5
7: 6 3 4
6: 7 3 8 5 4 2
5: 8 6 2 1
4: 7 3 6 2
3: 7 6 4 2
2: 6 3 4 5 1 0
1: 5 2 0
0: 2 1
the output of the program after running the k-core algo for k = 3 output is:
7: 6 3 4
6: 7 3 4 2
4: 7 3 6 2
3: 7 6 4 2
2: 6 3 4

Related

To make array identical by swapping elements

There are 2 i/p array's. They are identical when they have exactly same numbers in it. To make them identical, we can swap their elements. Swapping will have cost. If we are swapping a and b elements then cost = min(a, b).
While making array's identical, cost should be minimum.
If it is not possible to make array identical then print -1.
i/p:
3 6 6 2
2 7 7 3
o/p :
4
Here I have swapped (2,7) and (2,6). So min Cost = 2 + 2 = 4.
Logic :
Make 2 maps which will store frequency of i/p array's elements.
if element "a" in aMap is also present in bMap, then we have to consider number of swapping for a = abs(freq(a) in aMap - freq(a) in bMap)
if frequency of elements is "odd", then not possible to make them identical.
else , add total swaps from both maps and find cost using
cost = smallest element * total swaps
Here is the code
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int size;
long long int cost = 0;
cin >> size;
bool flag = false;
map<long long int, int> aMap;
map<long long int, int> bMap;
// storing frequency of elements of 1st input array in map
for( int i = 0 ; i < size; i++)
{
long long int no;
cin >> no;
aMap[no]++;
}
// storing frequency of elements of 2nd input array in map
for(int i = 0 ; i < size; i++)
{
long long int no;
cin >> no;
bMap[no]++;
}
// fetching smallest element (i.e. 1st element) from both map
long long int firstNo = aMap.begin()->first;
long long int secondNo = bMap.begin()->first;
long long int smallestNo;
// finding smallest element from both maps
if(firstNo < secondNo)
smallestNo = firstNo;
else
smallestNo = secondNo;
map<long long int, int> :: iterator itr;
// trying to find out total number of swaps we have to perform
int totalSwapsFromA = 0;
int totalSwapsFromB = 0;
// trversing a map
for(itr = aMap.begin(); itr != aMap.end(); itr++)
{
// if element "a" in aMap is also present in bMap, then we have to consider
// number of swapping = abs(freq(a) in aMap - freq(a) in bMap)
auto newItr = bMap.find(itr->first);
if(newItr != bMap.end())
{
if(itr->second >= newItr->second)
{
itr->second -= newItr->second;
newItr->second = 0;
}
else
{
newItr->second -= itr->second;
itr->second = 0;
}
}
// if freq is "odd" then, this input is invalid as it can not be swapped
if(itr->second & 1 )
{
flag = true;
break;
}
else
{
// if freq is even, then we need to swap only for freq(a)/ 2 times
itr->second /= 2;
// if swapping element is smallest element then we required 1 less swap
if(itr->first == smallestNo && itr->second != 0)
totalSwapsFromA += itr->second -1;
else
totalSwapsFromA += itr->second;
}
}
// traversing bMap to check whether there any number is present which is
// not in aMap.
if(!flag)
{
for(itr = bMap.begin(); itr != bMap.end(); itr++)
{
auto newItr = aMap.find(itr->first);
if( newItr == aMap.end())
{
// if frew is odd , then i/p is invalid
if(itr->second & 1)
{
flag = true;
break;
}
else
{
itr->second /= 2;
// if swapping element is smallest element then we required 1 less swap
if(itr->first == smallestNo && itr->second != 0)
totalSwapsFromB += itr->second -1;
else
totalSwapsFromB += itr->second;
}
}
}
}
if( !flag )
{
cost = smallestNo * (totalSwapsFromB + totalSwapsFromA);
cout<<"cost "<<cost <<endl;
}
else
cout<<"-1"<<endl;
}
return 0;
}
No error in the above code but giving wrong answer and not getting accepted.
Can anyone improve this code / logic ?
Suppose you have 2 arrays:
A: 1 5 5
B: 1 4 4
We know that we want to move a 5 down and a 4 up, so we have to options: swapping 4 by 5 (with cost min(4, 5) = 4) or using the minimum element to do achive the same result, making 2 swaps:
A: 1 5 5 swap 1 by 4 (cost 1)
B: 1 4 4
________
A: 4 5 5 swap 1 by 5 (cost 1)
B: 1 1 4
________
A: 4 1 5 total cost: 2
B: 5 1 4
So the question we do at every swap is this. Is it better to swap directly or swapping twice using the minimum element as pivot?
In a nutshell, let m be the minimum element in both arrays and you want to swap i for j. The cost of the swap will be
min( min(i,j), 2 * m )
So just find out all the swaps you need to do, apply this formula and sum the results to get your answer.
#user1745866 You can simplify your task of determining the answer -1 by using only variable:
let we have int x=0 and we will just do XOR of all the i/p integers like this:
int x = 0;
for(int i=0;i<n;i++){
cin>>a[i];
x = x^a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
x = x^b[i];
}
if(x!=0)
cout<<-1;
else{
...do code for remain 2 condition...
}
Now the point is how it will work because , as all the numbers of both array should occurs only even number of times and when we do XOR operation of any number which occured even number of times we will get 0.... otherwise they can't be identical arrays.
Now for 2nd condition(which gives answer 0) you should use multimap so you would be able to directly compare both arrays in O(n) time complexity as if all elements of both arrays are same you can output:0
(Notice: i am suggesting multimap because 1:You would have both array sorted and all elements would be there means also duplicates.
2: because they are sorted, if they consist of same element at same position we can output:0 otherwise you have to proceed further for your 3rd condition or have to swap the elements.)
For reducing the swap cost see Daniel's answer. For finding if the swap is actually possible, please do the following, the swaps are actually only possible if you have an even number of elements in total, so that you can split them out evenly, so if you have 2, 4 or 6 5's you are good, but if you have 1, 3, or 5 5's return -1. It is impossible if your number of duplicates of a number is odd. For actually solving the problem, there is a very simple solution I can think of, through it is a little bit expensive, you just need to make sure that there are the same number of elements on each side so the simple way to do that would be to declare a new array:
int temp[size of original arrays];
//Go through both arrays and store them in temp
Take half of each element, so something like:
int count[max element in array - min element in array];
for(int i = 0; i < temp.size(); i++){
count[temp[i]]++;
}
Take half of each element from temp. When you see an element that matches a element on your count array so whenever you see a 1 decrement the index on the count array by 1, so something like count[1]--; Assuming count starts at 0. If the index is at zero and the element is that one, that means a swap needs to be done, in this case find the next min in the other array and swap them. Albeit a little bit expensive, but it is the simplest way I can think of. So for example in your case:
i/p:
3 6 6 2
2 7 7 3
o/p :
4
We would need to store the min index as 2. Cause that is the smallest one. So we would have an array that looks like the following:
1 1 0 0 1 1
//one two one three zero four zero five 1 six and 1 seven
You would go through the first array, when you see the second six, your array index at 6 would be zero, so you know you need to swap it, you would find the min in the other array, which is 2 and then swap 6 with 2, after wards you can go through the array smoothly. Finally you go through the second array, afterwards when you see the last 7 it will look for the min on the other side swap them...., which is two, note that if you had 3 twos on one side and one two on the other, chances are the three twos will go to the other side, and 2 of them will come back, because we are always swapping the min, so there will always be an even number of ways we can rearrange the elements.
Problem link https://www.codechef.com/JULY20B/problems/CHFNSWPS
here for calculating minimum number of swap.we will having 2 cases
let say an example
l1=[1,2,2]
l2=[1,5,5]
case 1. swap each pair wrt to min(l1,l2)=1
step 1 swapping single 2 of a pair of 2 from l1-> [1,1,2]
[2,5,5] cost is 1
step 2 swapping single 5 of a pair of 5 from l1-> [1,5,2]
[2,1,5] cost is 1
total cost is 2
case 2. swap min of l1 with max of l2(repeat until both list end)
try to think if we sort 1st list in increasing order and other as decreasing order then we can minimize cost.
l1=[1,2,2]
l2=[5,5,1]
Trick is that we only need to store min(l1,l2) in variable say mn. Then remove all common element from both list.
now list became l1=[2,2]
l2=[5,5]
then swap each element from index 0 to len(l1)-1 with jump of 2 like 0,2,4,6..... because each odd neighbour wiil be same as previous number.
after perform swapping cost will be 2 and
l1=[5,2]
l2=[2,5] cost is 2
total cost is 2
Let say an other example
l1=[2,2,5,5]
l2=[3,3,4,4]
after solving wrt to min(l1,l2) total cost will be 2+2+2=6
but cost after sorting list will be swap of ((2,4) and (5,3)) is 2+3=5
so minimum swap to make list identical is min(5,6)=5
//code
l1.sort()
l2.sort(reverse=True)
sums=0
for i in range(len(l1)):
sums+=min(min(l1[i],l2[i]),2*minimum))
print(sums)
#print -1 if u get odd count of a key in total (means sums of count of key in both list)

How to find un-ordered numbers (lineal search)

A list partially ordered of n numbers is given and I have to find those numbers that does not follow the order (just find them and count them).
There are no repeated numbers.
There are no negative numbers.
MAX = 100000 is the capacity of the list.
n, the number of elements in the list, is given by the user.
Example of two lists:
1 2 5 6 3
1 6 2 9 7 4 8 10 13
For the first list the output is 2 since 5 and 6 should be both after 3, they are unordered; for the second the output is 3 since 6, 9 and 7 are out of order.
The most important condition in this problem: do the searching in a linear way O(n) or being quadratic the worst case.
Here is part of the code I developed (however it is no valid since it is a quadratic search).
"unordered" function compares each element of the array with the one given by "minimal" function; if it finds one bigger than the minimal, that element is unordered.
int unordered (int A[MAX], int n)
int cont = 0;
for (int i = 0; i < n-1; i++){
if (A[i] > minimal(A, n, i+1)){
count++;
}
}
return count;
"minimal" function takes the minimal of all the elements in the list between the one which is being compared in "unordered" function and the last of the list. i < elements <= n . Then, it is returned to be compared.
int minimal (int A[MAX], int n, int index)
int i, minimal = 99999999;
for (i = index; i < n; i++){
if (A[i] <= minimo)
minimal = A[i];
}
return minimal;
How can I do it more efficiently?
Start on the left of the list and compare the current number you see with the next one. Whenever the next is smaller than the current remove the current number from the list and count one up. After removing a number at index 'n' set your current number to index 'n-1' and go on.
Because you remove at most 'n' numbers from the list and compare the remaining in order, this Algorithmus in O(n).
I hope this helps. I must admit though that the task of finding numbers that are out of of order isn't all that clear.
If O(n) space is no problem, you can first do a linear run (backwards) over the array and save the minimal value so far in another array. Instead of calling minimal you can then look up the minimum value in O(1) and your approach works in O(n).
Something like this:
int min[MAX]; //or: int *min = new int[n];
min[n-1] = A[n-1];
for(int i = n-2; i >= 0; --i)
min[i] = min(A[i], min[i+1]);
Can be done in O(1) space if you do the first loop backwards because then you only need to remember the current minimum.
Others have suggested some great answers, but I have an extra way you can think of this problem. Using a stack.
Here's how it helps: Push the leftmost element in the array onto the stack. Keep doing this until the element you are currently at (on the array) is less than top of the stack. While it is, pop elements and increment your counter. Stop when it is greater than top of the stack and push it in. In the end, when all array elements are processed you'll get the count of those that are out of order.
Sample run: 1 5 6 3 7 4 10
Step 1: Stack => 1
Step 2: Stack => 1 5
Step 3: Stack => 1 5 6
Step 4: Now we see 3 is in. While 3 is less than top of stack, pop and increment counter. We get: Stack=> 1 3 -- Count = 2
Step 5: Stack => 1 3 7
Step 6: We got 4 now. Repeat same logic. We get: Stack => 1 3 4 -- Count = 3
Step 7: Stack => 1 3 4 10 -- Count = 3. And we're done.
This should be O(N) for time and space. Correct me if I'm wrong.

Array folding into a single element

This is an interview question not a homework.
Given a array of 1 to 2 ^N. For eg: 1 2 3 4 5 6 7 8 (2^3) .Imagine this array is written on a paper, we need to fold this into half, so that the left half will be mirrored and then moved underneath the right half like this
1 2 3 4 5 6 7 8
left | right
half | half
becomes
5 6 7 8
4 3 2 1
And the next fold we take the right half instead, mirroring it and moving it below the left half,
5 6
4 3
8 7
1 2
The paper has to be folded, changing direction (left-vs-right) each time, until we have all the elements in the single column like this
6
3
7
2
5
4
8
1
My solution,
First step :
Create a linked list for the second half of the original array, and reverse the first half and connect it with head pointers,
5 6 7 8
| | | |
4 3 2 1
And store the head pointers of linked list in an array called headarray
Iteratively :
fold the head array, for each fold either the first half and second half headers will be linked. Delete the head pointers from the headarray once it is linked.
Continue until w have a single head pointer in the head array.
But the interviewer asked me to solve it in stack. Could anyone help in getting this solved in stack and also point out if have done any mistake in my solution. Thanks in advance.
This problem can be solved by using a stack and the original array. I will not code the solution for you, but I will point out how to solve it.
push the array elements on to the stack following the rules we'll discuss further down
right after that pop the stack back into the array starting at index 0
repeat until the end condition is fulfilled
Rule for filling the stack:
initially consider your array as one 'segment'
divide the segment in half; the first half you will iterate in reverse order(right->left), the second one in natural order (left->right)
You start pushing on to the stack from the end of the array:
if the iteration is Odd, push the odd half(s) first,
if the iteration is even start with the even half(s) first
repeat, and keep half-ing your segments until they contain only one element; this is your stop condition
This is a little abstract, so let's consider your example:
iter=1 ->1234 <-5678 Arrows show the direction of iteration
start from the end and fill the stack; inter is odd so start with the first odd half encountered
5
6
7
8
4 <-notice that the order of pushing the halfs on the stack is shown by the arrows
3
2
1
pop the stack back : 5 6 7 8 4 3 2 1
Continue dividing the halfs:
iter=2 <-56 ->78 <-43 ->21; odd halfs 56,43; even halfs 78,21
start from the end and fill the stack; inter is even so start with the first even halfs
5
6
4
3
8 <-even halfs end, odd halfs start
7
1
2
Pop the stack back: 5 6 4 3 8 7 1 2
Divide the segments again, since there will be only one element in each new half the arrows are used just to highlight the rule:
iter=3 ->5 <-6 ->4 <-3 ->8 <-7 ->1 <-2
iter is odd, so fill the stack odd halfs first
6
3
7
2
5
4
8
1
Pop the stack back, and you are done: 63725481
I hope this makes sense; happy coding :)
I have found a law, the element in array whose index is (2*n-1, 2*n), the n is odd, always array before the rest elements whatever direction your folded. For example, the array 12345678, the elements 2367 always are front of the 1458. Now I have used dichotomy for getting two arrays. Next you maybe find the law in two arrays. I hope this can help you.
Maybe your interviewer expected something like:
private int[] Fold(int pow)
{
if (pow < 0)
throw new Exception("illegal input");
int n = 1;
for (int factor = 1; factor <= pow; factor++)
n *= 2;
Stack<int> storage = new Stack<int>(n);
this.Add(n, 1, storage);
int[] result = new int[n];
for (int k = 0; k < n; k++)
result[k] = storage.Pop();
return result;
}
private void Add(int n, int value, Stack<int> storage)
{
storage.Push(value);
int m = n;
while (true)
{
int mirror = m + 1 - value;
if (mirror <= value)
break;
this.Add(n, mirror, storage);
m /= 2;
}
}
{ demonstrating that you know about stacks AND about recursion ;-) }
Here's a recursive solution turned iterative; hence a stack, although probably not as intended. The function returns the starting position of an element based on the given position. It seems to be of time O(1/2n(log n + 1)) and space O(log n).
JavaScript Code:
function f(n,y,x,l){
var stack = [[n,y,x,l]];
while (stack[0]){
var temp = stack.pop();
var n = temp[0], y = temp[1], x = temp[2], l = temp[3];
var m = 1 << l;
if (m == 1)
return x;
if (l % 2 == 0){
if (y > m / 2)
stack.push([n * 2,y - m / 2,n + n - x + 1,l - 1]);
else
stack.push([n * 2,y,x,l - 1]);
} else if (y > m / 2){
stack.push([n * 2,y - m / 2,n - x + 1,l - 1]);
} else
stack.push([n * 2,y,x + n,l - 1]);
}
}
function g(p){
var n = 1 << p;
for (var i=1; i<n; i+=2){
var a = f(1,i,1,p);
console.log(a);
console.log(n - a + 1);
}
}
g(3)

Getting rid of non-unique entries in an adjacency list

I'm currently working on a project for my upper-level C++ class, and we are building a program that makes a maze, then solves it, the makes a PNG of said maze. Pretty cool stuff. Anyways, I'm current on the bit where I need to make the maze.
My program makes valid mazes just fine, but I have to make each number output unique. The output just spits out two indicies in a 2d matrix that have walls between them, sample output for a 3X4 maze is as follows:
rjeffor1:hydra20 ~/cs302/labs/lab5> ./mazemake 3 4 <- 9:49AM
1 2
1 5
2 1
2 3
3 2
5 1
5 9
6 7
7 6
8 9
9 8
9 5
However, my last problem is that I need to get rid of duplicate walls, for example 1 2 and 2 1. EDIT: and by this I mean just get rid of the 2 1, I still need the wall and therefore the 1 2.
Here is my function in which I attempt to fix the problem:
void aL::make_unique()
{
vector<int>::iterator it, it0;
//need to iterate thru all but last index
for (int i=0; i<(int)adjList.size()-1; i++) {
for (int j=0; j<(int)adjList.size(); j++) {
//find it
if (i!=j) {
it0 = std::find(adjList[i].begin(), adjList[i].end(), j);
it = std::find(adjList[j].begin(), adjList[j].end(), i);
if (it!=adjList[j].end() && it!=adjList[j].end())
//erase it if anything is there
adjList[j].erase(it);
}
}
}
}
Help is appreciated, my brain is so done at this point
EDIT: here is how I populate the adjancency lists, based on indicies directly left right above and below each index
aL::aL (const int &rows, const int &cols)
{
adjList.resize(rows*cols);
//run thru and figure out where indicies AREN'T
//to fill in their adjacency list
for (int i=0; i<(int)adjList.size(); i++) {
//if not on the left edge
if (i%cols!=0)
adjList[i].push_back(i-1);
//not on the right edge
if ((i+1)%cols!=0)
adjList[i].push_back(i+1);
//not on the top edge
if (i>=cols)
adjList[i].push_back(i-cols);
//not on the bottom edge
if (i<(rows*cols)-cols)
adjList[i].push_back(i+cols);
}
}
You could remove the need to post-process and make it unique at the end if you check as you are adding to your list. Don't add "a b" if "b a" is already there.

Counting number of members in a disjoint set

I am having a little bit of trouble counting the number of elements in each of my disjoint set members. For example, if someone enters in:
Note: first number = source vertex, 2nd number = destination vertex, 3rd number = length
0 2 1
4 8 7
5 8 6
1 2 5
2 3 17
I would have 2 as a count for the set
4 8 7
5 8 6
and 3 as a count for the set as both are connected by 2 and 3 (respective) elements.
0 2 1
1 2 5
2 3 17
I had the idea of storing the count of the number of elements for each disjoint set, into an integer array, so I can access the count for ever disjoint set. Below are my implementations for finding elements and unioning them into the same set. I also have a function for finding the root in every set.
int node::findSet(int v, int *parent)
{
if(parent[v] < 0)
return v;
else
{
return parent[v] = findSet(parent[v], parent);
}
}
void node::joinSets(int c, int p1, int p2, int *parents)
{
join(findSet(p1,parents),findSet(p2,parents),parents);
}
void node::join(int p1, int p2, int *parents)
{
if(parents[p2] < parents[p1])
parents[p1] = p2;
else
{
if(parents[p1] == parents[p2])
parents[p1]--;
parents[p2] = p1;
}
}
I'm just not sure where/when to increment and maintain my counter. Any help would be appreciated. Thanks!
If you want to count the number of edges connecting each disjoint set, store the current size of every root in an array similar to parents.
When an edge comes, find roots of both nodes. If they're equal, increment the counter for the root (I'm assuming there are no repeating edges). If they're not, union the roots, and for the resultant root's counter value put the sum of the roots' counter values plus one.