In the heap data structure, do the two equations
left = 2i+1
right = 2i+2
apply on any given heap? if not, in what condition they shall be efficient?
According to D.S. Malik "A heap is a list in which each element contains a key, such that the key in the element at position k in the list is at least as large as the key in the element at position 2k + 1 (if it exists) and 2k + 2 (if it exists)."
You should also note the element's position in the list: "in general, for the node k,which is the k-1th element of the list, its left child is the 2kth (if it exists) element of the list, which is at position 2k-1 in the list, and the right child is the 2k + 1st (if it exists) element of the list, which is at position 2k in the list."
When studying I found it helpful to actually input some indexes and make sure you get the element you were hoping. Best of luck.
It depends on where the root is. If the root is at index 0 in the array, then your equations are correct. The left node is at index 2i + 1 and the right node is at index 2i + 2.
Many examples have the root at index 1 in the array. In that case, the left child is at index 2i, and the right node is at index 2i + 1.
Related
What is the index of a child node in a heap in the array represenatation of a heap.
In my lecture notes and in this article
it is given as 2k and/or 2k+1
But in an array indexes start from 0,not 1 right?
Therefore shouldn't the children of node k be 2k+1 and/or 2k+2
Well as per going from general convention that the array indices usually start from 0.So in this case the root is assigned the 0th node.Then the child nodes are considered as 2k+1 and 2k+2;
But,even in the article they have clearly mentioned that
The root of the tree A[1] and given index i of a node, the indices of
its parent, left child and right child can be computed
PARENT (i)
return floor(i/2)
LEFT (i)
return 2i
RIGHT (i)
return 2i + 1
Hence,going as per the article,it should be 2k and 2k+1. Had the root been considered as index 0 of the array, then the indices of child nodes will be 2k+1 and 2k+2;
If the indices start from 0 it is 2k+1 and/or 2k+2.
If they start from 1, it is 2k and 2k + 1.
Why in double link list complexity of get(index) is equal O(n) not O(1)? Why it isn't like in array O(1) ? Is it because we have to traverse through previous nodes to get one ?
This is by definition. As you suspected, to get to the i-th element in the list, all previous items must be traversed.
As an exercise, implement a linked list for yourself.
Yes, having to "traverse through previous nodes to get one" is exactly it.
In a linked list, to find element # n, you would use something like:
def getNodeNum (n):
node = head
while n > 0 and node != NULL:
n = n - 1
node = node.next
return node
The reason an array is O(1) is because all the elements are laid out in contiguous memory. To get the address of element 42, you simply multiply 42 by the element size and then add the array base. This has the same cost for element number 3 as it does for element number 999.
You can't do that with a list because the elements are not necessarily contiguous in memory, hence you have to walk the list to find the one you desire. Thus the cost for finding element number 3 is actually far less than the cost for finding element number 999.
One of my friends had the following interview question, and neither of us are quite sure what the correct answer is. Does anyone have an idea about how to approach this?
Given an unbalanced binary tree, describe an algorithm to select a node at random such that each node has an equal probability of being selected.
You can do it with a single pass of the tree. The algorithm is the same as with a list.
When you see the first item in the tree, you set it as the selected item.
When you see the second item, you pick a random number in the range (0,2]. If it's 1, then the new item becomes the selected item. Otherwise you skip that item.
For each node you see, you increase the count, and with probability 1/count, you select it. So at the 101st node, you pick a random number in the range (0,101]. If it's 100, that node is the new selected node.
When you're done traversing the tree, return the selected node. The operation is O(n) in time, with n being the number of nodes in the tree, and O(1) in space. No preprocessing required.
We can do this recursively in one parse by selecting the random node while parsing the tree and counting the number of nodes in left and right sub tree. At every step in recursion, we return the number of nodes at the root and a random node selected uniformly randomly from nodes in sub tree rooted at root.
Let's say number of nodes in left sub tree is n_l and number of nodes in right sub tree is n_r. Also, randomly selected node from left and right subtree be R_l and R_r respectively. Then, select a uniform random number in [0,1] and select R_l with probability n_l/(n_l+n_r+1) or select root with probability 1/(n_l+n_r+1) or select R_r with probability n_r/(n_l+n_r+1).
Note
If you're only doing a single query, and you don't already have a count at each node, the best time complexity you can get is O(n), so the depth-first-search approach would be the best one.
For repeated queries, the best option depends on the given constraints
(the fastest per-query approach is using a supplementary array).
Supplementary array
O(n) space, O(n) preprocessing, O(1) insert / remove, O(1) query
Have a supplementary array containing all the nodes.
Also have each node store its own index (so you can remove it from the array in O(1) - the way to do this would be to swap it with the last element in the array, update the index of the node that was at the last index appropriately and decrease the size of the array (removing the last element).
To get a random node, simply generate a random index in the array.
Per-node count
Modified tree (O(n) space), N/A (or O(n)) preprocessing, O(depth) insert / remove, O(depth) query
Let each node contain the number of elements in its subtree.
When generating a random node, go left or right based on the value of a random number generated and the counts of the left or right subtrees.
// note that subtreeCount = leftCount + rightCount + 1
val = getRandomNumber(subtreeCount)
if val = 0
return this node
else if val <= leftCount
go left
else
go right
Depth-first-search
O(depth) space, O(1) preprocessing, O(1) insert / remove, O(n) query
Count the number of nodes in the tree (if you don't already have the count).
Generate a random number between 0 and the number of nodes.
Simply do a depth-first-search through the tree and stop when you've processed the desired number of nodes.
This presumes a node doesn't have a parent member - having this will make this O(1) space.
I implemented #jim-mischel's algorithm in C# and it works great:
private void SelectRandomNode(ref int count, Node curNode, ref Node selectedNode)
{
foreach( var childNode in curNode.Children )
{
++count;
if( random.Next(count) == count - 1 )
selectedNode = childNode;
SelectRandomNode(ref count, childNode, ref selectedNode);
}
}
Call it like this:
var count = 1;
Node selected = root;
SelectRandomNode(ref count, root, ref selected);
I found this in book:
Design a data structure for maintaining dynamic sequence x_1, x_2,... , x_n
which provides operations:
Insert(a,i) - inserts a as x_i, indexes from i+1 to n go up by 1
Delete(i) - deletes x_i, indexes from i+1 to n go down by 1
Find(i) - returns element x_i
Sum_even() - returns sum of the elements with even indexes
The sequence is dynamic so I want to use AVL tree to keep it. So I think I can use standard trick for find, delete and insert - just keep in each node size of sub tree rooted in this node. Then I can easily navigate the tree to find ith element in O(log n) time. Inorder gives me then: x_1, x_2,..., x_n.
But for Sum_even() I probably should have also sum of elements with even and odd indexes in each node. Although it's difficult to update while inserting or deleting. How should it work, can anyone help?
You don't have to store size of subtree in a node. AVL tree stores only difference in heights of left and right subtree: -1, 0 or +1.
In order to easily maintain sum_even you need to store sum_even and sum_odd for each node. It will be the sum of values on even/odd indexes for subtree.
So each node will have variables:
difference difference in height of left and right subtrees
value
parity parity of subtree size (0 or 1)
sum_even
sum_odd
For inserts and deletes use standard algorithm http://en.wikipedia.org/wiki/AVL_tree.
But for each affected node (nodes on the way up to root and rotated nodes) update values:
parity := (left.parity + right.parity + 1) % 2
if left.parity == 0
sum_even := left.sum_even + right.sum_odd
sum_odd := left.sum_odd + right.sum_even + value
else
sum_even := left.sum_even + right.sum_even + value
sum_odd := left.sum_odd + right.sum_odd
I'm trying to find the best algorithm for
converting an "ordinary" linked list
into an `ideal skip list`
.
Where the definition of an ideal skip list is that in the first level we'll have all
the elements , in the level above - half of them , the one after - quarter of them ... and so on .
I'm thinking about O(n) run-time where involving throwing a coin for each node in
the original linked-list , whether or not for a specific node , should I go up or not , and create another duplicate node for the current node upstairs ...
Eventually this algorithm would produce O(n) , is there any better algorithm ?
Regards
I am assuming the linked list is sorted - otherwise it cannot be done in comparison based algorithm, since you need to sort it in Omega(nlogn)
Iterate on the "highest level" of the list, and add a "link up node" every second node.
Repeat until the highest level has only one node.
The idea is to generate a new list, half the size of the original, which is linked to the original in every 2nd link, and then recursively invoke on the smaller list, until you reach a list of size 1.
You will end up with lists of size 1,2,4,...,n/2 linked to each other.
pseudo code:
makeSkipList(list):
if (list == null || list.next == null): //stop clause - a list of size 1
return
//root is the next level list, which will have n/2 elements.
root <- new link node
root.linkedNode <- list //linked node is linking "down" in the skip list.
root.next <- null //next is linking "right" in the skip list.
lastLinkNode <- root
i <- 1
//we create a link every second element
for each node in list, exlude the first element:
if (i++ %2 == 0): //for every 2nd element, create a link node.
lastLinkNode.next <- new link node
lastLinkNode <- lastLinkNode.next //setting the "down" field to the element in the list
lastLinkNode.linkedNode <- node
lastLinkNode.next <- null
makeSkipList(root) //recursively invoke on the new list, which is of size n/2.
Complexity is O(n) since the algorithm complexity can be described as T(n) = n + T(n/2), thus you get T(n) = n + n/2 + n/4 + ... -> 2n
It is easy to see it cannot be done better then O(n), because at the very least you will have to add at least one node in the second half of the original list, and getting there is itself O(n)