BST to Double-LinkedList Algorithm complexity? - algorithm

I am looking at the code to do this in CC150. One of its method is as follows, it does this by retrieving the tail of the left sub-tree.
public static BiNode convert(BiNode root) {
if (root == null) {
return null;
}
BiNode part1 = convert(root.node1);
BiNode part2 = convert(root.node2);
if (part1 != null) {
concat(getTail(part1), root);
}
if (part2 != null) {
concat(root, part2);
}
return part1 == null ? root : part1;
}
public static BiNode getTail(BiNode node) {
if (node == null) {
return null;
}
while (node.node2 != null) {
node = node.node2;
}
return node;
}
public static void concat(BiNode x, BiNode y) {
x.node2 = y;
y.node1 = x;
}
public class BiNode {
public BiNode node1;
public BiNode node2;
public int data;
public BiNode(int d) {
data = d;
}
}
What I don't understand is the Time Complexity the author gives in the book O(n^2). What I came up with is T(N) = 2*T(N/2) + O(N/2), O(N/2) is consumed by the getting tail reference because it needs to traverse a list length of O(N/2). So by Master Theorem, it should be O(NlogN). Did I do anything wrong? Thank you!

public static BiNode convert(BiNode root) {//worst case BST everything
if (root == null) { // on left branch (node1)
return null;
}
BiNode part1 = convert(root.node1);//Called n times
BiNode part2 = convert(root.node2);//Single call at beginning
if (part1 != null) {
concat(getTail(part1), root);// O(n) every recursive call
} // for worst case so 1 to n
// SEE BELOW
if (part2 != null) {
concat(root, part2);
}
return part1 == null ? root : part1;
}
public static BiNode getTail(BiNode node) {//O(n)
if (node == null) {
return null;
}
while (node.node2 != null) {
node = node.node2;
}
return node;
}
public static void concat(BiNode x, BiNode y) {//O(1)
x.node2 = y;
y.node1 = x;
}
SAMPLE TREE:
4
/
3
/
2
/
1
As you can see, in the worst case scenario (Big-Oh is not average case), the BST would be structured using only the node1 branch(es). Thus, the recursion would have a have to run getTail() with '1 + 2 + ... + N' problem sizes to complete the conversion.
Which is O(n^2)

Related

Is it necessary to distinguish 3 state fields when doing breadth first search

In the CTCI solution for checking to see if there is a route between two nodes, a 3 state enum is defined. However, it appears that what is really important is a binary state of (visited = true|false). Is this true? If not, why is it necessary to distinguish between 3 separate states?
public enum State {
Unvisited, Visited, Visiting;
}
public static boolean search(Graph g,Node start,Node end) {
LinkedList<Node> q = new LinkedList<Node>();
for (Node u : g.getNodes()) {
u.state = State.Unvisited;
}
start.state = State.Visiting;
q.add(start);
Node u;
while(!q.isEmpty()) {
u = q.removeFirst();
if (u != null) {
for (Node v : u.getAdjacent()) {
if (v.state == State.Unvisited) {
if (v == end) {
return true;
} else {
v.state = State.Visiting;
q.add(v);
}
}
}
u.state = State.Visited;
}
}
return false;
}

Modifying depth-first search

(source, destination) and it's type (tree, back, forward, cross)?
Here you go. Code in Java
import java.util.ArrayList;
import java.util.List;
class Node {
public String name;
public List<Node> connections = new ArrayList<>();
boolean visited = false;
Node(String name) {
this.name = name;
}
}
class DFS {
// Main part.
public static void search(Node root) {
if (root == null) {
return;
}
root.visited = true;
for (Node node : root.connections) {
if (!node.visited) {
// Print result.
System.out.println(root.name + "->" + node.name);
search(node);
}
}
}
}
public class App {
public static void main(String[] args) {
Node a = new Node("a");
Node b = new Node("b");
Node c = new Node("c");
Node d = new Node("d");
Node e = new Node("e");
a.connections.add(b);
b.connections.add(a);
b.connections.add(c);
b.connections.add(d);
c.connections.add(b);
c.connections.add(d);
d.connections.add(b);
d.connections.add(c);
d.connections.add(e);
DFS.search(d);
}
}
Nice question.
This is the solution based on the source you posted as comment.
IMPORTANT: There is an error on the start/end table, third row third column should be "end[u] < end[v]" instead of "end[u] > end[v]"
void main(G, s){
for each node v in G{
explored[v]=false
parent[v]=null
start[v]=end[v]=null
}
Global clock = 0
DFS(G, s, s)
}
void DFS(G, s, parent){
explored[s] = true;
parent[s] = parent
start[s]=clock
clock++
for each u=(s,v){
if(explored[v] == false){
DFS(G, v)
}
print (s + "-->" + v +"type: " + getType(s,v))
}
end[s]=clock
clock++
}
String getType(s, v){
if(start[s]<start[v]){
if(end[s]>end[v]) return "Tree edge"
else return "Forward edge"
else{
if(end[s]<end[v]) return "Back edge"
else return "Cross edge"
}
}

Big O complexities of my Huffman Algorithm

Can someone please tell me the Space and time Complexities, in Bog O notation, of this Huffman code with a little explanation. Would be very much appreciated, thanks. And please do mention the Big O of each method separately, would be great. Thanks.
package HuffmanProject;
import java.util.*;
class MyHCode {
public static void main(String[] args) {
String test = "My name is Zaryab Ali";
int[] FreqArray = new int[256];
for (char c : test.toCharArray()) {
FreqArray[c]++;
}
MyHTree tree = ImplementTree(FreqArray);
System.out.println("CHARACTER\tFREQUENCY\tBINARY EQUIVALEENT CODE");
PrintMyHCode(tree, new StringBuffer());
}
public static MyHTree ImplementTree(int[] FreqArray) {
PriorityQueue<MyHTree> trees = new PriorityQueue<MyHTree>();
for (int i = 0; i < FreqArray.length; i++) {
if (FreqArray[i] > 0) {
trees.offer(new MyHLeaf(FreqArray[i], (char) i));
}
}
while (trees.size() > 1) {
MyHTree FChild = trees.poll();
MyHTree SChild = trees.poll();
trees.offer(new MyHNode(FChild, SChild));
}
return trees.poll();
}
public static void PrintMyHCode(MyHTree tree, StringBuffer prefix) {
if (tree instanceof MyHLeaf) {
MyHLeaf leaf = (MyHLeaf) tree;
System.out.println(leaf.CharValue + "\t\t" + leaf.frequency + "\t\t" + prefix);
}
else if (tree instanceof MyHNode) {
MyHNode node = (MyHNode) tree;
prefix.append('0');
PrintMyHCode(node.left, prefix);
prefix.deleteCharAt(prefix.length() - 1);
prefix.append('1');
PrintMyHCode(node.right, prefix);
prefix.deleteCharAt(prefix.length() - 1);
}
}
}
abstract class MyHTree implements Comparable<MyHTree> {
public int frequency;
public MyHTree(int f) {
frequency = f;
}
public int compareTo(MyHTree tree) {
return frequency - tree.frequency;
}
}
class MyHLeaf extends MyHTree {
public char CharValue;
public MyHLeaf(int f, char v) {
super(f);
CharValue = v;
}
}
class MyHNode extends MyHTree {
public MyHTree left, right;
public MyHNode(MyHTree l, MyHTree r) {
super(l.frequency + r.frequency);
left = l;
right = r;
}
}
The PrintMyHCode() method iterates through the left & right subtrees until the leaft node is found. If there are n elements in the tree then the complexity of this method would be O(n).
The ImplementTree() method adds values in array to the tree and then it polls on their childs.
If there are n elements in the array:
1. The complexity of the for loop in this method will be O(n) as each elements is added to the tree directly
2. The complexity of while loop in this method will be O(logn) assuming that every node has atleast two children for it.
Hence, the total time complexity for ImplementTree() method in Big O notation would be O(nlogn).
Hope, this answer works for you.

Sentinel node in Binary Search Trees

I was wondering if in a way to avoid having to deal with the root as a special case in a Binary Search Tree I could use some sort of sentinel root node?
public void insert(int value) {
if (root == null) {
root = new Node(value);
++size;
} else {
Node node = root;
while (true) {
if (value < node.value) {
if (node.left == null) {
node.left = new Node(value);
++size;
return;
} else {
node = node.left;
}
} else if (value > node.value) {
if (node.right == null) {
node.right = new Node(value);
++size;
return;
} else {
node = node.right;
}
} else return;
}
}
}
For instance, in the insert() operation I have to treat the root node in a special way. In the delete() operation the same will happen, in fact, it will be way worse.
I've thought a bit regarding the issue but I couldn't come with any good solution. Is it because it is simply not possible or am I missing something?
The null node itself is the sentinel, but instead of using null, you can use an instance of a Node with a special flag (or a special subclass), which is effectively the null node. A Nil node makes sense, as that is actually a valid tree: empty!
And by using recursion you can get rid of the extra checks and new Node littered all over (which is what I presume is really bothering you).
Something like this:
class Node {
private Value v;
private boolean is_nil;
private Node left;
private Node right;
public void insert(Value v) {
if (this.is_nil) {
this.left = new Node(); // Nil node
this.right = new Node(); // Nil node
this.v = v;
this.is_nil = false;
return;
}
if (v > this.v) {
this.right.insert(v);
} else {
this.left.insert(v);
}
}
}
class Tree {
private Node root;
public Tree() {
root = new Node(); // Nil Node.
}
public void insert(Value v) {
root.insert(v);
}
}
If you don't want to use recursion, your while(true) is kind of a code smell.
Say we keep it as null, we can perhaps refactor it as.
public void insert(Value v) {
prev = null;
current = this.root;
boolean left_child = false;
while (current != null) {
prev = current;
if (v > current.v) {
current = current.right;
left_child = false;
} else {
current = current.left;
left_child = true;
}
}
current = new Node(v);
if (prev == null) {
this.root = current;
return;
}
if (left_child) {
prev.left = current;
} else {
prev.right = current;
}
}
The root will always be a special case. The root is the entry point to the binary search tree.
Inserting a sentinel root node means that you will have a root node that is built at the same time as the tree. Furthermore, the sentinel as you mean it will just decrease the balance of the tree (the BST will always be at the right/left of its root node).
The only way that pops in my mind to not manage the root node as a special case during insert/delete is to add empty leaf nodes. In this way you never have an empty tree, but instead a tree with an empty node.
During insert() you just replace the empty leaf node with a non-empty node and two new empty leafs (left and right).
During delete(), as a last step (if such operation is implemented as in here) you just empty the node (it becomes an empty leaf) and trim its existing leafs.
Keep in mind that if you implement it this way you will have more space occupied by empty leaf nodes than by nodes with meaningful data. So, this implementation has sense only if space is not an issue.
The code would look something like this:
public class BST {
private Node root;
public BST(){
root = new Node();
}
public void insert(int elem){
root.insert(elem);
}
public void delete(int elem){
root.delete(elem);
}
}
public class Node{
private static final int EMPTY_VALUE = /* your empty value */;
private int element;
private Node parent;
private Node left;
private Node right;
public Node(){
this(EMPTY_VALUE, null, null, null);
}
public Node(int elem, Node p, Node l, Node r){
element = elem;
parent = p;
left = l;
right = r;
}
public void insert(int elem){
Node thisNode = this;
// this cycle goes on until an empty node is found
while(thisNode.element != EMPTY_VALUE){
// follow the correct path for the insertion here
}
// insert new element here
// thisNode is an empty node at this point
thisNode.element = elem;
thisNode.left = new Node();
thisNode.right = new Node();
thisNode.left.parent = thisNode;
thisNode.right.parent = thisNode;
}
public void delete(int elem){
// manage delete here
}
}

Is it possible to design a tree where nodes have infinitely many children?

How can design a tree with lots (infinite number) of branches ?
Which data structure we should use to store child nodes ?
You can't actually store infinitely many children, since that won't fit into memory. However, you can store unboundedly many children - that is, you can make trees where each node can have any number of children with no fixed upper bound.
There are a few standard ways to do this. You could have each tree node store a list of all of its children (perhaps as a dynamic array or a linked list), which is often done with tries. For example, in C++, you might have something like this:
struct Node {
/* ... Data for the node goes here ... */
std::vector<Node*> children;
};
Alternatively, you could use the left-child/right-sibling representation, which represents a multiway tree as a binary tree. This is often used in priority queues like binomial heaps. For example:
struct Node {
/* ... data for the node ... */
Node* firstChild;
Node* nextSibling;
};
Hope this helps!
Yes! You can create a structure where children are materialized on demand (i.e. "lazy children"). In this case, the number of children can easily be functionally infinite.
Haskell is great for creating "functionally infinite" data structures, but since I don't know a whit of Haskell, here's a Python example instead:
class InfiniteTreeNode:
''' abstract base class for a tree node that has effectively infinite children '''
def __init__(self, data):
self.data = data
def getChild(self, n):
raise NotImplementedError
class PrimeSumNode(InfiniteTreeNode):
def getChild(self, n):
prime = getNthPrime(n) # hypothetical function to get the nth prime number
return PrimeSumNode(self.data + prime)
prime_root = PrimeSumNode(0)
print prime_root.getChild(3).getChild(4).data # would print 18: the 4th prime is 7 and the 5th prime is 11
Now, if you were to do a search of PrimeSumNode down to a depth of 2, you could find all the numbers that are sums of two primes (and if you can prove that this contains all even integers, you can win a big mathematical prize!).
Something like this
Node {
public String name;
Node n[];
}
Add nodes like so
public Node[] add_subnode(Node n[]) {
for (int i=0; i<n.length; i++) {
n[i] = new Node();
p("\n Enter name: ");
n[i].name = sc.next();
p("\n How many children for "+n[i].name+"?");
int children = sc.nextInt();
if (children > 0) {
Node x[] = new Node[children];
n[i].n = add_subnode(x);
}
}
return n;
}
Full working code:
class People {
private Scanner sc;
public People(Scanner sc) {
this.sc = sc;
}
public void main_thing() {
Node head = new Node();
head.name = "Head";
p("\n How many nodes do you want to add to Head: ");
int nodes = sc.nextInt();
head.n = new Node[nodes];
Node[] n = add_subnode(head.n);
print_nodes(head.n);
}
public Node[] add_subnode(Node n[]) {
for (int i=0; i<n.length; i++) {
n[i] = new Node();
p("\n Enter name: ");
n[i].name = sc.next();
p("\n How many children for "+n[i].name+"?");
int children = sc.nextInt();
if (children > 0) {
Node x[] = new Node[children];
n[i].n = add_subnode(x);
}
}
return n;
}
public void print_nodes(Node n[]) {
if (n!=null && n.length > 0) {
for (int i=0; i<n.length; i++) {
p("\n "+n[i].name);
print_nodes(n[i].n);
}
}
}
public static void p(String msg) {
System.out.print(msg);
}
}
class Node {
public String name;
Node n[];
}
I recommend you to use a Node class with a left child Node and right child Node and a parent Node.
public class Node
{
Node<T> parent;
Node<T> leftChild;
Node<T> rightChild;
T value;
Node(T val)
{
value = val;
leftChild = new Node<T>();
leftChild.parent = this;
rightChild = new Node<T>();
rightChild.parent = this;
}
You can set grand father and uncle and sibling like this.
Node<T> grandParent()
{
if(this.parent.parent != null)
{
return this.parent.parent;
}
else
return null;
}
Node<T> uncle()
{
if(this.grandParent() != null)
{
if(this.parent == this.grandParent().rightChild)
{
return this.grandParent().leftChild;
}
else
{
return this.grandParent().rightChild;
}
}
else
return null;
}
Node<T> sibling()
{
if(this.parent != null)
{
if(this == this.parent.rightChild)
{
return this.parent.leftChild;
}
else
{
return this.parent.rightChild;
}
}
else
return null;
}
And is impossible to have infinite child, at least you have infinite memory.
good luck !
Hope this will help you.

Resources