I was asked this in an interview:
Check if given two stacks are equal (size and order of elements) using O(1) space.
Stacks should maintain their earlier state (order of elements).
Recursion uses stack space so not valid.
One of my friend suggested to use String variable and append elements from one stack into it and push popped to the other one and then push from S2(old elements of S1) to S1 and do the same thing with S2. But this depends on the size of the Stack as String will grow depending on the size of stack.
Recursion vs iteration is not the issue here. The issue is how to leave the stacks intact, because the only way to inspect a stack in full is to empty it.
So what you need to do is store the popped members onto temporary stacks. This does not change the overall space requirements, since you only push onto the temporary stack objects that are popped elsewhere, and thus you are satisfying the O(1) spare requirement.
Here’s the pseudocode for an iterative solution:
function stacks_are_equal
precondition: stacks s1, s2
set equal? to true
while s1 is not empty and s2 is not empty
pop x from s1
pop y from s2
push x onto temp1
push y onto temp2
if x ≠ y then
set equal? to false
break out of loop
if s1 is not empty or s2 is not empty set equal? to false
while temp1 is not empty
pop x from temp1
pop y from temp2
push x onto s1
push y onto s2
return equal?
And here’s the recursive solution:
function stacks_are_equal(s1, s2)
if s1 is empty and s2 is empty return true
if s1 is empty or s2 is empty return false
pop x from s1
pop y from s2
empty? := x = y and stacks_are_equal(s1, s2)
push x onto s1
push y onto s2
return empty?
Use recursion...Here is an example using java.
private static boolean equalhelper(Stack<Integer> s1, Stack<Integer> s2)
{
if(s1.isEmpty() && s2.isEmpty())
return true;
if(s1.isEmpty() || s2.isEmpty())
return false;
Integer a=s1.pop();
Integer b=s2.pop();
if(!a.equals(b))
{
s1.push(a);
s2.push(b);
return false;
}
else
{
boolean check= equalhelper( s1, s2);
s1.push(a);
s2.push(b);
return check;
}
}
Related
I have implemented union-find algorithm inspired by this code with this API:
void unify(int p, int q)
int find(int p)
boolean connected(int p, int q)
int clusterSize(int p)
Then I detect clusters with a nested loop like this:
for i = (0) to (data size)
for j = (i+1) to (data size)
if ( "i" and "j" meet this condition )
unify(i,j)
end for
end for
To actually store each cluster/group in a separate array, currently I loop over all my data elements and call find like this:
for i = 0 to data-size
k = find(i);
// ... Store data element "i" in cluster "k" (array "k")
end-for
I have a feeling like, the last for loop to store data elements in different groups/clusters might not be necessary. I wonder if I'm on right track?
First you need to initialize by making each element a separate cluster. While doing so there's no need to call find method as initially each of them itself is a cluster.
int clusters[data_size];
for(int i = 0; i < data_size; i++)
{
clusters[i] = i; // each element refers to itself initially, indicating that it's a separate cluster
}
Now upon the nested loop check which elements are needed to be clustered, and then unify those -
for i = (0) to (data size)
for j = (i+1) to (data size)
if ( "i" and "j" meet this condition )
unify(i,j)
end for
end for
In the unify(p, q) method, what you can do is -
clusters[find(p)] = find(q); // indicates p belongs to cluster number q
Or the vice versa is also true -
clusters[find(q)] = find(p); // indicates q belongs to cluster number p
There's a ranking technique in the Union Find algorithm, to choose which of the above to use. That ensures - we will always merge the smaller cluster into the bigger cluster. But I assume you are not worried about that right now.
Now, the next question would be - why are we calling find in the above expression. It's because, we are actually finding out the root element of a certain cluster(Or simply find the element which actually represent the cluster).
This is my homework question i have tried to solve it just need someone to look and tell me if i am doing it right or worng..
The dynamic-set operation UNION takes two disjoint sets S1 and S2 as input, and it returns a set S = S1 U S2 consisting of all the elements of S1 and S2. The sets S1 and S2 are usually destroyed by the operation. Show how to support UNION in O(1) time using a suitable list data structure
I am thinking of having two linked lists which can be done in constant time but for that we need to remember a pointer to both first(head) and last(tail) element of list.
struct node{
char* word;
struct node* next;
}
struct Set{
struct node* head;
struct node* tail;
}
For every list beside with the head pointer we'll also keep a tail pointer.
Supporting union operation in O(1) time: Suppose we've two Sets S1 and S2.
PSEUDO-CODE:
node* Union(Set S1,Set S2){
S1.tail->next = S2.head;
S1.tail = S2.tail;
Remove S2 from the list of sets;
return S1;
}
is my approach going in right direction?
Just in case we don't have the tail pointer at hand (actually a common case for linked lists...), we cannot UNION the two lists in O(1), since we have to traverse one of the list to get its tail pointer, which takes O(n).
In this case, with only two head pointers at hand, the "suitable list data structure" has to be a doubly circular linked list. We disconnect the link between the head element of LIST_1 and its next element, and disconnect the link between the head element of LIST_2 and its prev element. Then we connect the two head elements and connect the other two elements. Thus, we get another doubly circular linked list, and the "pointer flow" is kept (which is why we shouldn't disconnect the head element of LIST_2 with its next element).
Yea, that's the same approach I'd take.
S1:
A1->A2->A3
S2:
B1->B2->B3
Tail node of S1 (A3) linked to head node of S2 (B1)
S1US2:
A1->A2->A3*->*B1->B2->B3
In the question that you mentioned it's written that we can use any suitable list data structure ( the answer that you have given considers a pointer to tail as well, which is not necessary unless you want to do it using a singly linked list in O(1) and generally we consider only the concept of head node when we talk about linked lists ) thus we will use doubly circular linked list. Now we have to join the 2 sets so we can do the following operations to achieve it in O(1)
(headS1->prev)->next = headS2;
temp = headS1->prev;
(headS2->prev)->next = headS1 ;
headS1->prev = headS2->prev ;
headS2->prev = temp;
The following link explains it. The implementation is said to work by storing the XOR of the previous and next address(say nxp), instead of storing both(previous and next address) separately.However, further along the implementation is said to work by xor-ing the previous address and nxp, in order to get the next address.
But isnt this practically using the same space as having previous and next pointers?
In a doubly linked list, you store two pointers per node: prev and next. In an XOR linked list, you store one 'pointer' per node, which is the XOR of prev and next (or if one of them is absent, just the other (the same as XORing with 0)). The reason why you can still traverse an XOR linked list in both directions relies on the properties of XOR and the redundancy of information inherent in a double linked list.
Imagine you have three nodes in your XOR linked list.
A is the head, and has an unobfuscated pointer to B (B XOR 0, next only)
B is the middle element, and has the XOR of pointers to A and to C.
C is the tail, and an unobfuscated pointer to B (0 XOR B, prev only)
When I am iterating over this list, I start at A. I note A's position in memory as I travel to B. When I wish to travel to C, I XOR B's pointer with A, granting me the pointer to C. I then note B's position in memory and travel to C.
This works because XOR has the property of undoing itself if applied twice: C XOR A XOR A == C. Another way to think about it is, the doubly linked list stores no extra information a singly linked list does not (since it's just storing all the previous pointers as copies of next pointers somewhere else in memory), so by exploiting this redundancy we can have doubly linked list properties with only as many links as are needed. However, This only works if we start our XOR linked list traversal from the start or end — as if we just jump into a random node in the middle, we do not have the information necessary to start traversing.
While an XOR linked list has the advantage of smaller memory usage, it has disadvantages — it will confuse the compiler, debugging and static analysis tools as your XOR of two pointers will not be correctly recognized by a pointer by anything except your code. It also slows down pointer access to have to do the XOR operation to recover the true pointer first. It also can't be used in managed code — XOR obfuscated pointers won't be recognized by the garbage collector.
Let us consider the following XOR list
A->B->C->D
suppose you created nodes in this format below
Key|Link|
A|0^addr(B)| -> B|addr(A)^addr(C)| -> C|addr(B)^addr(D)| -> D|addr(C)^0|
CASE #1:[Forward Traversal] Now Suppose you are in B (current_node=>B) want visit C , so you need Address of C . How you will get ?
Addressof(Next_node) = addressof(Prev_node) ^ Current_node(Link)
addr(A)^ ( addr(A)^ addr(C) )
=>(addr(A) ^ addr(A)) ^ addr(C)
=> 0 ^ addr(C)
=>addr(C)
CASE #2: [Backward traversal] Now Suppose you are in C (current_node=> C) want visit B , so you need Address of B . How you will get ?
Addressof(Prev_node) = addressof(Next_node) ^ Current_node(Link)
addr(D) ^ ((addr(B) ^ addr(D))
=> (addr(D)^ addr(D)) ^ addr(B)
=> 0^addr(B)
=> addr(B)
Traversing:
To traverse whole list ,You will need 3 pointers prevPtr , currPtr , nextPtr to store relative current, previous and next node's address starting with head.
Then in each iteration these pointers need be move to one position ahead.
struct Node *currPtr = head;
struct Node *prevPtr = NULL;
struct Node *nextPtr;
printf ("Following are the nodes of Linked List: \n");
while (currPtr != NULL)
{
// print current node
printf ("%d ", currPtr->key);
// Save the address of next node
nextPtr = XOR (prevPtr, currPtr->link);
//move prevPtr and currPtr one position for next iteration
prevPtr = currPtr;
currPtr = nextPtr;
}
But isnt this practically using the same space as having previous and
next pointers?
No - it uses about half the space, as the size of the result of XOR-ing the "prev" and "next" is equal to the size of the larger of the two.
XOR has a very special property about it, namely, given a XOR b = c, only two (any two) of the variable are required to compute the the third, with some restrictions. See the XOR swap algorithm for why this works.
In this case the previous (or next) pointer must still be carried, but only through traversal calculations and not as a seperate member.
Double linked list needs 2*N pointers stored for N nodes, plus at least one additional pointer(head, or perhaps head and tail).
XOR linked list needs N pointers stored for N nodes, plus at least two additional pointers (head and last visited node, or perhaps head and tail and last visited node). While traversing, you store one node (the last visited node), but when you go to the next node, you rewrite that with the now-previous node's address.
[Interview Question]
Write a function that would return the 5th element from the tail (or end) of a singly linked list of integers, in one pass, and then provide a set of test cases against that function.
It is similar to question : How to find nth element from the end of a singly linked list?, but I have an additional requirement that we should traverse the linked list only once.
This is my solution:
struct Lnode
{
int val;
Lnode* next;
Lnode(int val, Lnode* next=NULL) : val(val), next(next) {}
};
Lnode* kthFromTail(Lnode* start, int k)
{
static int count =0;
if(!start)
return NULL;
Lnode* end = kthFromTail(start->next, k);
if(count==k) end = start;
count++;
return end;
}
I'm traversing the linked list only once and using implicit recursion stack. Another way can be to have two pointers : fast and slow and the fast one being k pointers faster than the slow one.Which one seems to be better? I think the solution with two pointers will be complicated with many cases for ex: odd length list, even length list, k > length of list etc.This one employing recursion is clean and covers all such cases.
The 2-pointer solution doesn't fit your requirements as it traverses the list twice.
Yours uses a lot more memory - O(n) to be exact. You're creating a recursion stack equal to the number of items in the list, which is far from ideal.
To find the kth from last item...
A better (single-traversal) solution - Circular buffer:
Uses O(k) extra memory.
Have an array of length k.
For each element, insert at the next position into the array (with wrap-around).
At the end, just return the item at the next position in the array.
2-pointer solution:
Traverses the list twice, but uses only O(1) extra memory.
Start p1 and p2 at the beginning.
Increment p1 k times.
while p1 is not at the end
increment p1 and p2
p2 points to the kth from last element.
'n' is user provided value. eg, 5 from last.
int gap=0 , len=0;
myNode *tempNode;
while (currNode is not NULL)
{
currNode = currNode->next;
gap = gap+1;
if(gap>=n)
tempNode = currNode;
}
return tempNode;
Question: I have a single linked list (i.e. a list with only pointer to the next node). Additionally this is a circular linked list (in this example, the last node has a pointer to the first node). Every node in the list contains a char.
An example of such a list can be: a->b->c->b->a
Now how can I verify if this list is a pallindrome?
I have thought of the following solution:
Start from the head of list. Find the length of the list and then the mid node. Now start again from the head of the list and keep pushing elements in stack until the mid. Now traverse the list from the mid and pop element. If the value of the popped element is equal to the value of the current node. if not, return false. otherwise, continue until the stack is empty and we've verified all chars. CONS: uses extra stack space :(
Start from the head of list. Find the length of the list and then the mid node. now reverse the 2nd half of this list. and then using 2 pointers (one pointing to start and the other pointing to the mid+1'th element), check if the values are same. if not, return false. else continue until we reach the start node again. CONS: Changing original data structure.
Is there a more elegant way to approach this problem (which hopefully does not use O(n) extra space or changes original list)? I'm interested in the algorithm rather than any specific implementation.
Thanks
Since you're dealing with a single linked list, you must use a little extra space or a lot more extra time.
Your first approach sounds reasonable, but you can determine the length of the list and palindrome-ness in a single run.
We modify the so-called Floyd's Cycle-Finding Algorithm:
two pointers, "slow" and "fast", both start at the list head; the slow pointer advances one list element per iteration, the fast pointer two elements
in each step, the slow pointer pushes the current element on the stack
if the fast pointer reaches the end of the list, the slow pointer points to the middle of the list, so now:
the slow pointer advances to the end of the list, and in each step:
it pops one element from the stack and compares it to the current list element (if they are not equal, return false)
if the slow pointer reaches the end of the list, it is a palindrome
A little extra work is required for lists with an odd number of elements.
This is in pseudo-Haskell (I can't remember the exact syntax these days) and I've written for the non-circular case -- to fix that, just replace the clause matching against [] with whatever condition you use to identify you've come full circle.
p(xs) = q(xs, Just(xs)) != Nothing
q([], maybeYs) = maybeYs
q(x : xs, Nothing) = Nothing
q(x : xs, maybeYs) =
let maybeZs = q(xs, maybeYs) in
case maybeZs of
Nothing -> Nothing
Just (x :: zs) -> Just(zs)
otherwise -> Nothing
Since you know the Linked List does make a cycle, and you are only looking for palindromes starting at head, you can make this easier on yourself.
A -> B -> C -> B -> A
In this case, start with a pointer at head (call it H), and a pointer at head.Left() (call it T).
Now keep moving the head pointer H to the right, and the tail pointer T to the left.
As you walk the list, verify that the values of those elements are equal (i.e. a palindrome).
Your stopping condition however take a bit more. There are two cases:
Both pointers end point at the same element (i.e. odd number of elements)
The H pointer is pointing at the element just to the right of T.
So, you stop if H==T or if H==(T.Right()).
Using this approach (or similar) you visit each element just once.
Use the Tortoise and Hare approach as in the other solutions if you don't know if the linked list is cyclic.
Just paste my implementation so we could compare with each others, full test here:
/**
* Given a circular single linked list and the start pointer, check if it is a palindrome
* use a slow/fast pointer + stack is an elegant way
* tip: wheneve there is a circular linked list, think about using slow/fast pointer
*/
#include <iostream>
#include <stack>
using namespace std;
struct Node
{
char c;
Node* next;
Node(char c) {this->c = c;}
Node* chainNode(char c)
{
Node* p = new Node(c);
p->next = NULL;
this->next = p;
return p;
}
};
bool isPalindrome(Node* pStart)
{
Node* pSlow = pStart;
Node* pFast = pStart;
stack<Node*> s;
bool bEven = false;
while(true)
{
// BUG1: check fast pointer first
pFast = pFast->next;
if(pFast == pStart)
{
bEven = false;
break;
}
else
{
pFast = pFast->next;
if(pFast == pStart)
{
bEven = true;
break;
}
}
pSlow = pSlow->next;
s.push(pSlow);
}
if(s.empty()) return true; // BUG2: a, a->b->a
if(bEven) pSlow = pSlow->next; // BUG3: a->b->c->b->a, a->b->c->d->c->b->a: jump over the center pointer
while(!s.empty())
{
// pop stack and advance linked list
Node* topNode = s.top();
s.pop();
pSlow = pSlow->next;
// check
if(topNode->c != pSlow->c)
{
return false;
}
else
{
if(s.empty()) return true;
}
}
return false;
}
I think we dont need an extra space for this. And this can be done with O(n) complexity.
Modifying Philip's solution:
We modify the so-called Floyd's Cycle-Finding Algorithm:
Two pointers, "slow" and "fast", both start at the list head; the slow pointer advances one list element per iteration, the fast pointer two elements
in each step, the slow pointer pushes the current element on the stack
if the fast pointer reaches the end of the list, the slow pointer points to the middle of the list, so now:
Have another pointer at the start of the linked-list (start pointre) and now -
move the start pointer and the slow pointer one by one and compare them - if they are not equal, return false
- if the slow pointer reaches the end of the list, it is a palindrome
This is O(n) time complexity and no extra space is required.