Assuming the tree is balanced, how much stack space will the routine use for a tree of 1,000,000 elements?
void printTree(const Node *node) {
char buffer[1000];
if(node) {
printTree(node->left);
getNodeAsString(node, buffer);
puts(buffer);
printTree(node->right);
}
}
This was one of the algo questions in "The Pragmatic Programmer" where the answer was 21 buffers needed (lg(1m) ~= 20 and with the additional 1 at very top)
But I am thinking that it requires more than 1 buffer at levels lower than top level, due to the 2 calls to itself for left and right node. Is there something I missed?
*Sorry, but this is really not a homework. Don't see this on the booksite's errata.
First the left node call is made, then that call returns (and so its stack is available for re-use), then there's a bit of work, then the right node call is made.
So it's true that there are two buffers at the next level down, but those two buffers are required consecutively, not concurrently. So you only need to count one buffer in the high-water-mark stack usage. What matters is how deep the function recurses, not how many times in total the function is called.
This assuming of course that the code is written in a language similar to C, and that the C implementation uses a stack for automatic variables (I've yet to see one that doesn't), blah blah.
The first call will recurse all the way to the leaf node, then return. Then the second call will start -- but by the time the second call takes place, all activation records from the first call will have been cleared off the stack. IOW, there will only be data from one of those on the stack at any given time.
Related
For example let consider a task where we need to find all permutations for given string preserving the character sequence but changing case.
Here is backtracking solution without .pop():
def letterCasePermutation(S):
"""
:type S: str
:rtype: List[str]
"""
def backtrack(sub="", i=0):
if len(sub) == len(S):
res.append(sub)
else:
if S[i].isalpha():
backtrack(sub + S[i].swapcase(), i + 1)
backtrack(sub + S[i], i + 1)
res = []
backtrack()
return res
And here is a solution with .pop():
def letterCasePermutation(s):
def backtrack(idx, path):
if idx == n:
res.append("".join(path))
return
ele = s[idx]
if ele.isnumeric():
path.append(ele)
backtrack(idx + 1, path)
path.pop()
else:
path.append(ele.lower())
backtrack(idx + 1, path)
path.pop()
path.append(ele.upper())
backtrack(idx + 1, path)
path.pop()
n = len(s)
res = []
backtrack(0, [])
return res
Are both code samples backtracking, or should I call the first one DFS and the second one backtracking?
With backtracking (and most recursive functions in general) the critical invariant for every function call is that it doesn't corrupt state in parent calls.
This should make intuitive sense becauase recursion relies on self-similarity. If unpredictable changes to state occur elsewhere in the call stack that affect data structures shared with ancestor calls, it's easy to see how the property of self-similarity is lost.
Recursive function calls work by pushing a frame onto the call stack, manipulating state locally as needed, then popping the call stack. Before returning to the parent frame, the child call is responsible for restoring state so that from the parent call frame's perspective, execution can carry on without any surprising state modifications by some random ancestor call down the chain.
To give a metaphor, you could think of each call frame as a run through the plot of The Cat in the Hat or Risky Business where the protagonists make a mess (in their call frame), then must restore order before the story ends (the function returns).
Now, given this high-level goal, there are multiple ways to achieve it as your snippets show. One way is to allocate some sort of data structure such as a list object once, then push (append) and pop on it per call frame, mirroring the call stack.
Another approach is to copy state when spawning child calls so that each frame receives fresh versions of the relevant data, and no modifications they make will upset their parent state. This typically requires a bit less bookkeeping and could be less susceptible to subtle bugs than mutating a single data structure, but tends to have higher overhead due to memory allocator and garbage collector action and copying data structures for every frame.
In short, don't confuse the high-level goal of keeping state intact per call frame and how code goes about implementing it.
As far as backtracking versus DFS, I think of backtracking as a specialized DFS that prunes off branches of the search tree that a heuristic determines aren't worth exploring further because they cannot lead to a solution. As before, how the code actually achieves state restoration to implement the backtracking (copying data structures or pushing/popping an explicit stack) shouldn't change the fact that it's the same fundamental algorithmic technique.
I've seen the term "backtracking" applied to permutation algorithms like this. Although the terminology may be fairly common, it seems like a misuse since the permutation algorithm is a full-state recursive walk that will always visit all of the nodes in the tree and isn't doing any intelligent heuristic pruning as backtracking does.
I've been asked this question somewhere.
I've been given 2 stacks. I have to implement the following operations:
// Pass one of the stacks and a value to insert
push(Stack stack, value)
pop(Stack stack, val)
merge(Stack s1, Stack s2)
I have to perform above stack operations like push and pop in O(1). So far I've used a linked list to successfully implement these operations.
But how can I merge the two stacks in O(1)? I couldn't find how to do it in O(1).
Maybe I need to use some other data structure or something?
It's really easy if your stack objects keep both ends of the stack (top/botton, start/end, head/tail, whatever). I'll use top/bottom for this answer.
When you implement push/pop you operate on the top object. The bottom will remain the same (unless the stack is empty) and the node that represents it will have it's next pointer set to null.
So to merge two stacks you take the bottom of one, point it to the top of the other and return a "new" stack formed of the other pointers.
Stack merge(Stack s1, Stack s2) {
// join the stacks
s2.bottom.next = s1.top
// make a nice object to give back
Stack result;
result.bottom = s1.bottom
result.top = s2.top
// cleanup the parameters so they don't mess up the new structure.
s1.bottom = s1.top = s2.bottom = s2.top = null;
return result;
}
If you don't have the two pointers nicely kept in the stack object you would need to traverse one of the stacks get what would be kept here as bottom, making the complexity O(N).
I would like to give another perspective, the programming/object oriented perspective. If you do not have a pointed to the end of the stack as suggested before and in case merging means first return the elements of one stack, then the other, i.e. define an order between them - this is a real important consideration you did not address. You could follow the following approach
Create a StackList object which extends Stack Java example:
class StackList extends Stack
Now, hold a linked list of Stacks in it, the merging is trivial by adding the Stacks to the list, pop/push will simply call the pop/push methods of the head Stack.
I have a problem related to runtime for push and pop in a stack.
Here, I implemented a stack using array.
I want to avoid overflow in the stack when I insert a new element to a full stack, so when the stack is full I do the following (Pseudo-Code):
(I consider a stack as an array)
Generate a new array with the size of double of the origin array.
Copy all the elements in the origin stack to the new array in the same order.
Now, I know that for a single push operation to the stack with the size of n the action executes in the worst case in O(n).
I want to show that the runtime of n pushes to an empty stack in the worst case is also O(n).
Also how can I update this algorithm that for every push the operation will execute in a constant runtime in the worst case?
Amortized constant-time is often just as good in practice if not better than constant-time alternatives.
Generate a new array with the size of double of the origin array.
Copy all the elements in the origin stack to the new array in the same order.
This is actually a very decent and respectable solution for a stack implementation because it has good locality of reference and the cost of reallocating and copying is amortized to the point of being almost negligible. Most generalized solutions to "growable arrays" like ArrayList in Java or std::vector in C++ rely on this type of solution, though they might not exactly double in size (lots of std::vector implementations increase their size by something closer to 1.5 than 2.0).
One of the reasons this is much better than it sounds is because our hardware is super fast at copying bits and bytes sequentially. After all, we often rely on millions of pixels being blitted many times a second in our daily software. That's a copying operation from one image to another (or frame buffer). If the data is contiguous and just sequentially processed, our hardware can do it very quickly.
Also how can I update this algorithm that for every push the operation
will execute in a constant runtime in the worst case?
I have come up with stack solutions in C++ that are ever-so-slightly faster than std::vector for pushing and popping a hundred million elements and meet your requirements, but only for pushing and popping in a LIFO pattern. We're talking about something like 0.22 secs for vector as opposed to 0.19 secs for my stack. That relies on just allocating blocks like this:
... of course typically with more than 5 elements worth of data per block! (I just didn't want to draw an epic diagram). There each block stores an array of contiguous data but when it fills up, it links to a next block. The blocks are linked (storing a previous link only) but each one might store, say, 512 bytes worth of data with 64-byte alignment. That allows constant-time pushes and pops without the need to reallocate/copy. When a block fills up, it just links a new block to the previous block and starts filling that up. When you pop, you just pop until the block becomes empty and then once it's empty, you traverse its previous link to get to the previous block before it and start popping from that (you can also free the now-empty block at this point).
Here's your basic pseudo-C++ example of the data structure:
template <class T>
struct UnrolledNode
{
// Points to the previous block. We use this to get
// back to a former block when this one becomes empty.
UnrolledNode* prev;
// Stores the number of elements in the block. If
// this becomes full with, say, 256 elements, we
// allocate a new block and link it to this one.
// If this reaches zero, we deallocate this block
// and begin popping from the previous block.
size_t num;
// Array of the elements. This has a fixed capacity,
// say 256 elements, though determined at runtime
// based on sizeof(T). The structure is a VLS to
// allow node and data to be allocated together.
T data[];
};
template <class T>
struct UnrolledStack
{
// Stores the tail end of the list (the last
// block we're going to be using to push to and
// pop from).
UnrolledNode<T>* tail;
};
That said, I actually recommend your solution instead for performance since mine barely has a performance edge over the simple reallocate and copy solutions and yours would have a slight edge when it comes to traversal since it can just traverse the array in a straightforward sequential fashion (as well as straightforward random-access if you need it). I didn't actually implement mine for performance reasons. I implemented it to prevent pointers from being invalidated when you push things to the container (the actual thing is a memory allocator in C) and, again, in spite of achieving true constant-time push backs and pop backs, it's still barely any faster than the amortized constant-time solution involving reallocation and memory copying.
Say I've something like this: (predict the output)
void abc (char *s){
if(s[0]=='\0')
return;
abc(s+1);
abc(s+1);
printf(“%c “, s[0]);
}
It's not tough to solve, but I take too much time doing it and I've to redo such questions 2-3 times because I lose track of the recursion and values of variables(especially when there are 2-3 such recursive statements)
Is there any good method to use when one has to solve such questions?
The basic technique is to first start with a small input. Then try with one larger. Then try with one larger than that. For recursive functions, a pattern should emerge that lets you predict what the next one will look like given you know what the previous one looked like.
So, let's start with an empty string. Easy, nothing is printed.
input: ""
output:
Next is a string of length one. Almost as easy, the two recursive calls each do nothing (empty string case), and then the string's character is printed.
input: "z"
output: z
Next is a string of length two. Each of the recursive calls end up printing the second character (string of length one case), and then the first character is printed.
input: "yz"
output: zzy
So, let's try to predict what will happen for the string of length three case. What will happen is that the substring that excludes the first character gets worked on twice, then the first character is printed. That substring is the string of length two case. So:
input: "xyz"
output: zzyzzyx
So, it should be clear now how to derive the next output sequence given the current output sequence.
The easiest example for analyzing recursion is Fibonacci and Factorial function.
This will help you in analyzing recursive functions in a better manner. Whenever you lose track of recursive functions just recall these examples.
Take a stack of index cards of an appropriate size. Start tracing the initial call to the recursive function. When you get another call start a new index card and either put it in front of the first card or behind it (as appropriate). Sooner or later you will (unless you are tracing an infinite recursion) trace the execution of a call which does not make a recursive call, in which case copy the return value back to the card you came from.
It's probably a good idea to include 'go to card X' and 'came from card Y' on your cards.
In complicated situations you might find it useful to create more than one stack of cards to trace your function calls, oh why the heck, why not call them call stacks.
I have a couple questions about the stack. One thing I don't understand about the stack is the "pop" and "push" idea. Say I have integers a and b, with a above b on the stack. To access b, as I understand it, a would have to be popped off the stack to access b. So where is "a" stored when it is popped off the stack.
Also if stack memory is more efficient to access than heap memory, why isn't heap memory structured like the stack? Thanks.
So where is "a" stored when it is popped off the stack.
It depends. It goes where the program that's reading the stack decides. It may store the value, ignore it, print it, anything.
Also if stack memory is more efficient to access than heap memory, why
isn't heap memory structured like the stack?
A stack isn't more efficient to access than a heap is, it depends on the usage. The program's flow gets deeper and shallower just like a stack does. Local variables, arguments and return addresses are, in mainstream languages, stored in a stack structure because this kind of structure implements more easily the semantics of what we call a function's stack frame. A function can very efficiently access its own stack frame, but not necessarily its caller functions' stack frames, that is, the whole stack.
On the other hand, the heap would be inefficient if it were implemented that way, because it's expected for the heap to be able to access and possibly delete items anywhere, not just from its top/bottom.
I'm not an expert, but you can sort of think of this like the Tower of Hanoi puzzle. To access a lower disc, you "pop" discs above it and place them elsewhere - in this case, on other stacks, but in the case of programming it could be just a simple variable or pointer or anything. When you've got the item you need, then the other ones can be put back on the stack or moved elsewhere entirely.
Lets take your case scenario .
You have a stack with n elements on it, the last one is a, b is underneath.
pop operation returns the popped value, so if you want to access the second from the top being b, you could do:
var temp = stack.pop()
var b = stack.pop()
stack.push(temp)
However, stack would rarely be used this way. It is a LIFO queue and works best when accessed like a LIFO queue.
It seems you would rather need a collection with a random index based access.
That collection would probably be stored on the heap. Hope it clarified stack pop/push a little.
a is stored wherever you decide to store it. :-) You need to provide a variable in which to store the value at the top of the stack (a) when you remove it, then remove the next item (b) and store it in a different variable to use it, and then push the first value (a) back on the stack.
Picture an actual pile of dirty plates sitting on your counter to your left. You pick one up to wash it (pop it from the "dirty" stack), wash it, dry it, and put it on the top of the clean stack (push it) on your right.
If you want to reach the second plate from the top in either stack, you have to move the top one to get to it. So you pick it up (pop it), put it somewhere temporarily, pick up the next plate (pop it) and put it somewhere, and then put the first one you removed back on the pile (push it back on the stack).
If you can't picture it with plates, use an actual deck of playing cards (or baseball cards, or a stack of paper - anything you can neatly pile ("stack")) and put it on your desk at your left hand. Then perform the steps in my last paragraph, replacing the word "plate" with "card" and physically performing the steps.
So to access b, you declare a variable to store a in, pop a and save it in that variable, pop b into it's own variable, and then push a back onto the stack.