CIS 265 Notes Part 2
CIS 265 Notes Part 2
Linked List
Head and Tail nodes greatly easy the logic behind the linked list
operations. Every list has a head and tail dummy node to start with
and this reduces the number of possible list states by 3. All for
the cost of two extra data nodes in memory utilization
Wrapper Class the node of a list can be the data item itself
modified to have next (and maybe prev) references. Or, the node can
be a class itself designed to be nothing more than a few references.
In this case a private class to the data structure with just exposed
field variables and no methods is acceptable
Double Ended List is one where the data structure itself maintains
a reference to the fist and the last list elements. Therefore,
insertion can occur at either end.
Circular Where the last element points back to the first, like a
snake swallowing its own tail. Note that a circular list may need
only a head (or tail) and not both, depending on how it is coded
Add which adds an item to the list. When adding a node B, you
have to be sure that A -> C turns into A -> B -> C. Adding at either
end can be O(1), but adding in the middle needs a traversal and is
thus O(n)
Remove which deletes an item from the list. When removing a node
B, you have to be sure that A -> B -> C turns into A -> B -> C.
Note, that since you must be on B to know what to delete, you will
need a trailing reference as well if the list is not doubly linked.
This requires a traversal in the same situations as add
Find which looks though the list to find if the item exists or not.
Find always requires a traversal so is O(n) regardless of list set up
Code Examples)
// linkList2.java
// demonstrates linked list
// to run this program: C>java LinkList2App
////////////////////////////////////////////////////////////////
class Link
{
public int iData; // data item (key)
public double dData; // data item
public Link next; // next link in list
////////////////////////////////////////////////////////////////
4
class LinkList
{
private Link first; // ref to first link on list
////////////////////////////////////////////////////////////////
class LinkList2App
{
public static void main(String[] args)
{
LinkList theList = new LinkList(); // make list
Code Example)
/ linkStack.java
// demonstrates a stack implemented as a list
// to run this program: C>java LinkStackApp
////////////////////////////////////////////////////////////////
class Link
7
{
public long dData; // data item
public Link next; // next link in list
////////////////////////////////////////////////////////////////
class LinkList
{
private Link first; // ref to first item on list
////////////////////////////////////////////////////////////////
class LinkStack
{
private LinkList theList;
////////////////////////////////////////////////////////////////
class LinkStackApp
9
{
public static void main(String[] args)
{
LinkStack theStack = new LinkStack(); // make stack
Code Example)
// linkQueue.java
// demonstrates queue implemented as double-ended list
// to run this program: C>java LinkQueueApp
////////////////////////////////////////////////////////////////
class Link
{
public long dData; // data item
public Link next; // next link in list
////////////////////////////////////////////////////////////////
10
class FirstLastList
{
private Link first; // ref to first item
private Link last; // ref to last item
////////////////////////////////////////////////////////////////
class LinkQueue
{
private FirstLastList theList;
////////////////////////////////////////////////////////////////
class LinkQueueApp
12
{
public static void main(String[] args)
{
LinkQueue theQueue = new LinkQueue();
theQueue.insert(20); // insert items
theQueue.insert(40);
Code Example)
// interIterator.java
// demonstrates iterators on a linked listListIterator
// to run this program: C>java InterIterApp import java.io.*;
// for I/O
////////////////////////////////////////////////////////////////
class Link
{
public long dData; // data item
public Link next; // next link in list
////////////////////////////////////////////////////////////////
class LinkList
{
private Link first; // ref to first item on list
System.out.println("");
}
} // end class LinkList
////////////////////////////////////////////////////////////////
class ListIterator
{
private Link current; // current link
private Link previous; // previous link
private LinkList ourList; // our linked list
////////////////////////////////////////////////////////////////
class InterIterApp
{
public static void main(String[] args) throws IOException
{
LinkList theList = new LinkList(); // new list
ListIterator iter1 = theList.getIterator(); // new iter
long value;
while(true)
{
17
System.out.print("Enter first letter of show, reset, ");
System.out.print("next, get, before, after, delete: ");
System.out.flush();
int choice = getChar(); // get user's option
switch(choice)
{
case 's': // show list
if( !theList.isEmpty() )
theList.displayList();
else
System.out.println("List is empty");
break;
case 'r': // reset (to first)
iter1.reset();
break;
case 'n': // advance to next item
if( !theList.isEmpty() && !iter1.atEnd() )
iter1.nextLink();
else
System.out.println("Can't go to next link");
break;
case 'g': // get current item
if( !theList.isEmpty() )
{
value = iter1.getCurrent().dData;
System.out.println("Returned " + value);
}
else
System.out.println("List is empty");
break;
case 'b': // insert before current
System.out.print("Enter value to insert: ");
System.out.flush();
value = getInt();
iter1.insertBefore(value);
break;
case 'a': // insert after current
System.out.print("Enter value to insert: ");
System.out.flush();
value = getInt();
iter1.insertAfter(value);
break;
case 'd': // delete current item
if( !theList.isEmpty() )
{
value = iter1.deleteCurrent();
System.out.println("Deleted " + value);
18
}
else
System.out.println("Can't delete");
break;
default:
System.out.println("Invalid entry");
} // end switch
} // end while
} // end main()
Code Example)
// doublyLinked.java
// demonstrates doubly-linked list
// to run this program: C>java DoublyLinkedApp
////////////////////////////////////////////////////////////////
class Link
{
public long dData; // data item
public Link next; // next link in list
public Link previous; // previous link in list
19
public Link(long d) // constructor
{
dData = d;
}
////////////////////////////////////////////////////////////////
class DoublyLinkedList
{
private Link first; // ref to first item
private Link last; // ref to last item
if(current == null)
{
return false; // didn't find it
}
Link newLink = new Link(dd); // make new link
////////////////////////////////////////////////////////////////
class DoublyLinkedApp
{
public static void main(String[] args)
{ // make a new list
DoublyLinkedList theList = new DoublyLinkedList();
Recursion
General Case the normal smaller caller case where the function
calls a “slightly smaller version” of itself, rather than return
Code Example)
25
public static int Factorial(int number)
{
if ( number == 0 )
{
return 1;
}
else
{
return( number * Factorial(number –1) );
}
}
Code Example)
// triangle.java
// evaluates triangular numbers
// to run this program: C>java TriangleApp
import java.io.*;
////////////////////////////////////////////////////////////////
class TriangleApp
{
static int theNumber;
Code Example)
// anagram.java
// creates anagrams
// to run this program: C>java AnagramApp
import java.io.*;
////////////////////////////////////////////////////////////////
class AnagramApp
{
static int size;
static int count;
static char[] arrChar = new char[100];
How to Debug Recursion you can hand walk you code, and/or using
System.out.println or some other method is usually helpful. Note
that where you place the System.out.println will make a big
difference in the data you receive back
Removing Recursion
30
Removing Recursion In general recursion can be removed by a
combination of two means
Code Examples)
class ordArray
{
private long[] a; // ref to array a
private int nElems; // number of data items
////////////////////////////////////////////////////////////////
32
class BinarySearchApp
{
public static void main(String[] args)
{
int maxSize = 100; // array size
ordArray arr; // reference to array
arr = new ordArray(maxSize); // create the array
Code Example)
while (true)
{
curIn = (lowerBound + upperBound ) / 2;
Code Example)
// stackTriangle.java
// evaluates triangular numbers, stack replaces recursion
// to run this program: C>java StackTriangleApp
import java.io.*; // for I/O
////////////////////////////////////////////////////////////////
class Params // parameters to save on stack
{
public int n;
public int returnAddress;
////////////////////////////////////////////////////////////////
class StackX
{
private int maxSize; // size of StackX array
private Params[] stackArray;
private int top; // top of stack
////////////////////////////////////////////////////////////////
class StackTriangleApp
{
static int theNumber;
static int theAnswer;
static StackX theStack;
static int codePart;
static Params theseParams;
Merge Sort
Divide and Conquer is an approach that tries to cut our data in half
at each step. If you can achieve a divide and conquer that is worth
O(log N) instead of O(N), so it is a worthwhile goal. We have
already seen binary search as our first divide and conquer algorithm.
Now lets look at Mergesort
Mergesort works by breaking the data down into sections of two and
then sorting each section. Then it works its way back up again
sorting sections of 4… then 8… etcetera, until there is only the
original section of data left.
Not In Place mergesort uses extra memory to work with in the form o
the temporary array we write to before copying back to the original
array
Code Example)
// mergeSort.java
// demonstrates recursive merge sort
// to run this program: C>java MergeSortApp
////////////////////////////////////////////////////////////////
class DArray
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
////////////////////////////////////////////////////////////////
class MergeSortApp
{
public static void main(String[] args)
{
int maxSize = 100; // array size
DArray arr; // reference to array
arr = new DArray(maxSize); // create the array
Shell Sort
Shell Sort works very much like insertion sort except for an
optimization in how we choose which values to sort. Essentially, we
sort on intervals instead of all at once. So we might sort all
elements separated by 5 giving sorts on: {0,5,10,…}, {1,6,11,…},
{2,7,12,…}, {3,8,13,…}, {4,9,14,…}. Then sort by 3s and then finally
by 1s. The idea is that if we mix up the numbers so that they are
approximately sorted, then we will save on our swaps and compares
because the numbers do not have to move very far
Not in Order as the intervals should well and truly shake things up
with the mini-sorts moving all relatively similar values around
unpredictably
Evaluation O(N log N2) overall, with worst cases of O(N2). The
outer loop sill needs loops summing N total iterations, composed of
the mini–interval loops. The inner loop is reduced on average
because less swapping and comparison is occurring since numbers are
roughly sorted already
Source Code)
// shellSort.java
41
// demonstrates shell sort
// to run this program: C>java ShellSortApp
//--------------------------------------------------------------
class ArraySh
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//--------------------------------------------------------------
public ArraySh(int max) // constructor
{
theArray = new long[max]; // create the array
nElems = 0; // no items yet
}
//--------------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//--------------------------------------------------------------
public void display() // displays array contents
{
System.out.print("A=");
for(int j=0; j<nElems; j++) // for each element,
{
System.out.print(theArray[j] + " "); // display it
}
System.out.println("");
}
//--------------------------------------------------------------
public void shellSort()
{
int inner, outer;
long temp;
h = (h-1) / 3; // decrease h
} // end while(h>0)
} // end shellSort()
////////////////////////////////////////////////////////////////
class ShellSortApp
{
public static void main(String[] args)
{
int maxSize = 10; // array size
ArraySh arr;
arr = new ArraySh(maxSize); // create the array
Partitioning
43
Partitioning is an algorithm that quicksort will use to help sort its
data. The idea behind a partitioning to divide data into halves
based on some pivot value. Ideally, we end up with an array with
approximately half the data representing values less than the pivot
value, and the other half the values greater than the pivot. The
partition uses two pointers that pincher in from either end of the
data and swaps between them when the left pointer finds a value
greater than pivot and the right value finds one less than pivot. The
partition routine is complete when these two pointers meet
Mid Point, choose the midpoint of the array and use the
value stored there as the pivot
Not In Order pivot is not in order as the way the pincher moves in
word from both ends means that an early value that is swapped from eh
left will end up later in the later half of the array than the next
equal value on the left.
Source Code)
while(true)
{
while( leftPtr < right && theArray[++leftPtr] < pivot )
{ // empty loop
}
Quick Sort
45
Quick Sort is arguably the fastest general purpose sort. It uses
partition to divide up the data and then Quicksorts each of the two
resulting sections of data. Quicksort is a very sensitive algorithm
in that both very small changes to the code can cause it to break in
odd circumstances and in that the data order and the partition method
need to work well together
Code Example)
// quickSort1.java
// demonstrates simple version of quick sort
// to run this program: C>java QuickSort1App
////////////////////////////////////////////////////////////////
class ArrayIns
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//--------------------------------------------------------------
46
public ArrayIns(int max) // constructor
{
theArray = new long[max]; // create the array
nElems = 0; // no items yet
}
//--------------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//--------------------------------------------------------------
public void display() // displays array contents
{
System.out.print("A=");
for(int j=0; j<nElems; j++) // for each element,
{
System.out.print(theArray[j] + " "); // display it
System.out.println("");
}
}
//--------------------------------------------------------------
public void quickSort()
{
recQuickSort(0, nElems-1);
}
//--------------------------------------------------------------
public void recQuickSort(int left, int right)
{
if(right-left <= 0) // if size <= 1,
{
return; // already sorted
}
else // size is 2 or larger
{
long pivot = theArray[right]; // rightmost item
}
// partition range
int partition = partitionIt(left, right, pivot);
recQuickSort(left, partition-1); // sort left side
recQuickSort(partition+1, right); // sort right side
}
} // end recQuickSort()
47
//--------------------------------------------------------------
public int partitionIt(int left, int right, long pivot)
{
int leftPtr = left - 1; // right of first elem
int rightPtr = right + 1; // left of pivot
while(true)
{
while( leftPtr < right && theArray[++leftPtr] < pivot )
{ // empty loop
}
//--------------------------------------------------------------
public void swap(int dex1, int dex2) // swap two elements
{
long temp = theArray[dex1]; // A into temp
theArray[dex1] = theArray[dex2]; // B into A
theArray[dex2] = temp; // temp into B
} // end swap(
////////////////////////////////////////////////////////////////
class QuickSort1App
{
public static void main(String[] args)
{
int maxSize = 16; // array size
ArrayIns arr;
48
arr = new ArrayIns(maxSize); // create array
Radix Sort
In Order the radix sort should also preserve the order of equal
values, but you have to be careful because you can code it otherwise
Evaluation O(MN) overall. We know that the outer loop runs once
for each digit/character. If the M is equal to the N, then this is
an N squared algorithm. However, M is usually much less than N, so
much so that the algorithm can behave like O(N) for larger Ns with
non-unique keys. Comparisons and swaps are both at most N at each M
level
Source Code)
/*
* @!RadixSortAlgorithm.java
* By: Alvin Raj
* Date: 12 August 2002
* Algorithm adapted from: C++ Data Structures, Nell Dale
* Additional Comments added by Jason Harrison <[email protected]>
*
* Algorithm comments from
* http://ciips.ee.uwa.edu.au/~morris/Year2/PLDS210/radixsort.html
*
*/
arrayPos = 0;
// Put values into queues according to radix
50
// least significant digit first, then second,...
// first pass sorts on least significant digit
for (int j = 0; j < a.length; j++)
{
Q[getRadix(a[j],i)].enqueue(a[j]);
pause(-1,j); // JH
}
// Collect the queues and put them back into the array
// queues contain partially sorted lists after first
// pass -- moving to next significant digit will
// maintain this ordering.
for (int j = 0; j < Q.length; j++)
{
while(!Q[j].isEmpty())
{
a[arrayPos] = Q[j].dequeue();
arrayPos++;
}
pause(-1,arrayPos);
}
}
}
}
intNode()
{
next = null;
prev = null;
value = 0; //sets value to zero if no value is explicitly given
}
//Constructor
LinkedQueue()
{
start = null;
end = null;
length = 0;
}
A Radix Exchange Sort is the same order, but much faster because it
uses a bit level partitioning scheme to exchange values in the array.
In essence, the inner loop it looks at a given bit of the numbers
with two pointers moving towards the center exactly like pivot. Each
1 in the bottom half is marked to swap with each 0 from the top half.
When a swap occurs, the whole numbers are moved. The outer loop
simply partitions the two sections and then calls radix sort(lower
half), radix sort(upper half) just like quick sort did. Stpign case
is when we sort on the least significant bit.
Evaluation O(MN) overall for the same reasons as the basic radix
exchange. Here M is the bit depth of the numbers to sort which is
larger than the decimal radii, but everything is much, much faster
with no instantiations, in place, fewer method calls, etc
Source Code)
TBD In Class
Binary Trees
54
Binary Trees in all their forms present a very useful data structure
that allows O(log N) searches, insertions and deletions, while being
fully dynamic in nature. To discuss the tree structure we need to
understand the terminology associated with a tree
Tree Terminology
· Root the original parent node of all other child nodes. Or the
parent node of a subtree
· Parent the node that a child node directly comes from, the
child(ren) is a descendant of the parent
· Ancestors the set of nodes that lay between a node and the root
· Sub Tree a given node (root or otherwise) and all its descendents
· Binary Tree is a root node with up to two children. Each child can
have up to two children, etcetera
· Balanced Tree a tree with approximately the same depth for all of
its leaves. Maintains the “logarithmic” speed of the tree, without
this trees could turn into a linear access structure
· Full Binary Tree a tree with all possible nodes filled at each
level. Such a tree is considered balanced
· Find use the binary search tree property (children to the left are
less / right are greater) to search for a node in at most O(log N)
time - in a balanced tree. If you end up at a node that does not
contain the value you are looking for, and has no applicable children
to traverse, then the tree does not contain the value
· Insert use the same approach as above to fall to the lowest level
of the tree and insert the new leaf node attached to this node
· Delete must ensure that the tree maintains its’ binary search tree
status. First we find a node as above. This algorithm can be
recursive or non-recursive, although non-recursive is actually not
any harder to code (and may not even need a stack). There are three
cases for deleting a node
57
· Tree Traversal means walking the tree or visiting each node in some
order. Note that you may well visit each node more than once, but
usually you will only ‘process’ each node once, such as is typically
done for output of the tree. There are three standard ways to walk a
tree
Pre Order print the current node. Then try to go left and
print if able. If not, try and go right and print if able.
Failing that back up a level and try those again until all
nodes have been visited at least once (but only
processed/printed once)
Code Example)
// tree.java
// demonstrates binary tree
// to run this program: C>java TreeApp
import java.io.*;
import java.util.*; // for Stack class
////////////////////////////////////////////////////////////////
class Node
{
public int iData; // data item (key)
public double dData; // data item
public Node leftChild; // this node's left child
public Node rightChild; // this node's right child
////////////////////////////////////////////////////////////////
class Tree
{
private Node root; // first node of tree
// -------------------------------------------------------------
public Tree() // constructor
{
root = null; // no nodes in tree yet
}
// -------------------------------------------------------------
public Node find(int key) // find node with given key
{ // (assumes non-empty tree)
Node current = root; // start at root
while(current.iData != key) // while no match,
{
if(key < current.iData) // go left?
59
current = current.leftChild;
else // or go right?
current = current.rightChild;
if(current == null) // if no child,
return null; // didn't find it
}
return current; // found it
} // end find()
// -------------------------------------------------------------
public void insert(int id, double dd)
{
Node newNode = new Node(); // make new node
newNode.iData = id; // insert data
newNode.dData = dd;
if(root==null) // no node in root
root = newNode;
else // root occupied
{
Node current = root; // start at root
Node parent;
while(true) // (exits internally)
{
parent = current;
if(id < current.iData) // go left?
{
current = current.leftChild;
if(current == null) // if end of the line,
{ // insert on left
parent.leftChild = newNode;
return;
}
} // end if go left
else // or go right?
{
current = current.rightChild;
if(current == null) // if end of the line
{ // insert on right
parent.rightChild = newNode;
return;
}
} // end else go right
} // end while
} // end else not root
} // end insert()
// -------------------------------------------------------------
public boolean delete(int key) // delete node with given key
60
{ // (assumes non-empty list)
Node current = root;
Node parent = root;
boolean isLeftChild = true;
// -------------------------------------------------------------
// returns node with next-highest value after delNode
// goes to right child, then right child's left descendents
private Node getSuccessor(Node delNode)
{
Node successorParent = delNode;
Node successor = delNode;
Node current = delNode.rightChild; // go to right child
while(current != null) // until no more
{ // left children,
successorParent = successor;
successor = current;
current = current.leftChild; // go to left child
}
// if successor not
if(successor != delNode.rightChild) // right child,
{ // make connections
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
}
return successor;
}
62
// -------------------------------------------------------------
public void traverse(int traverseType)
{
switch(traverseType)
{
case 1: System.out.print("\nPreorder traversal: ");
preOrder(root);
break;
case 2: System.out.print("\nInorder traversal: ");
inOrder(root);
break;
case 3: System.out.print("\nPostorder traversal: ");
postOrder(root);
break;
}
System.out.println();
}
// -------------------------------------------------------------
private void preOrder(Node localRoot)
{
if(localRoot != null)
{
System.out.print(localRoot.iData + " ");
preOrder(localRoot.leftChild);
preOrder(localRoot.rightChild);
}
}
// -------------------------------------------------------------
private void inOrder(Node localRoot)
{
if(localRoot != null)
{
inOrder(localRoot.leftChild);
System.out.print(localRoot.iData + " ");
inOrder(localRoot.rightChild);
}
}
// -------------------------------------------------------------
private void postOrder(Node localRoot)
{
if(localRoot != null)
{
postOrder(localRoot.leftChild);
postOrder(localRoot.rightChild);
63
System.out.print(localRoot.iData + " ");
}
}
// -------------------------------------------------------------
public void displayTree()
{
Stack globalStack = new Stack();
globalStack.push(root);
int nBlanks = 32;
boolean isRowEmpty = false;
System.out.println(
"......................................................");
while(isRowEmpty==false)
{
Stack localStack = new Stack();
isRowEmpty = true;
while(globalStack.isEmpty()==false)
{
Node temp = (Node)globalStack.pop();
if(temp != null)
{
System.out.print(temp.iData);
localStack.push(temp.leftChild);
localStack.push(temp.rightChild);
if(temp.leftChild != null ||
temp.rightChild != null)
isRowEmpty = false;
}
else
{
System.out.print("--");
localStack.push(null);
localStack.push(null);
}
for(int j=0; j<nBlanks*2-2; j++)
System.out.print(' ');
} // end while globalStack not empty
System.out.println();
nBlanks /= 2;
while(localStack.isEmpty()==false)
globalStack.push( localStack.pop() );
} // end while isRowEmpty is false
64
System.out.println(
"......................................................");
} // end displayTree()
////////////////////////////////////////////////////////////////
class TreeApp
{
public static void main(String[] args) throws IOException
{
int value;
Tree theTree = new Tree();
theTree.insert(50, 1.5);
theTree.insert(25, 1.2);
theTree.insert(75, 1.7);
theTree.insert(12, 1.5);
theTree.insert(37, 1.2);
theTree.insert(43, 1.7);
theTree.insert(30, 1.5);
theTree.insert(33, 1.2);
theTree.insert(87, 1.7);
theTree.insert(93, 1.5);
theTree.insert(97, 1.5);
while(true)
{
System.out.print("Enter first letter of show, ");
System.out.print("insert, find, delete, or traverse: ");
int choice = getChar();
switch(choice)
{
case 's':
theTree.displayTree();
break;
case 'i':
System.out.print("Enter value to insert: ");
value = getInt();
theTree.insert(value, value + 0.9);
break;
case 'f':
System.out.print("Enter value to find: ");
value = getInt();
Node found = theTree.find(value);
if(found != null)
65
{
System.out.print("Found: ");
found.displayNode();
System.out.print("\n");
}
else
System.out.print("Could not find ");
System.out.print(value + '\n');
break;
case 'd':
System.out.print("Enter value to delete: ");
value = getInt();
boolean didDelete = theTree.delete(value);
if(didDelete)
System.out.print("Deleted " + value + '\n');
else
System.out.print("Could not delete ");
System.out.print(value + '\n');
break;
case 't':
System.out.print("Enter type 1, 2 or 3: ");
value = getInt();
theTree.traverse(value);
break;
default:
System.out.print("Invalid entry\n");
} // end switch
} // end while
} // end main()
// -------------------------------------------------------------
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
// -------------------------------------------------------------
public static char getChar() throws IOException
{
String s = getString();
return s.charAt(0);
}
66
//-------------------------------------------------------------
public static int getInt() throws IOException
{
String s = getString();
return Integer.parseInt(s);
}
Optimizing Binary Trees for the tree operations can take several
approaches, from removing recursion to implementing the tree with an
array
· Saving Deleted Node whenever you need to inset anode, first check
the list of deleted nodes. If one does exist, then reuse it. This
saves two costs, first no garbage collection for a deleted node
occurs. Second, no instantiation of the new node occurs in this
case. Obviously this a fairly good affect on trees that are actively
being added to or deleted from
· Trees Using Arrays are another speedup. In this case we trade the
extra storage for a full tree. This means we possibly pay array
resizing costs as well. In turn, we can traverse or search the tree
very quickly compared to linked lists and may gain some cache
locality benefits as well
Huffman Code
Red-Black Trees
· Each node in the tree is ‘colored’. There are a few rules about
this coloration process
T is the tree
p is parent
Example Code)
RedBlackInsert(T, x)
{
TreeInsert(T,x)
color(x) = Red
while( x != root(T) && color(p(x)) == Red )
{
if ( p(x) == left(p(p(x)) )
71
{
y <- right(p(p(x)))
if ( color(y) == Red )
{
color(p(x)) = Black
color(y) = Black
color(p(p(x))) = Red
x = p(p(x))
}
else
{
if ( x == right(p(x)) )
{
x = p(x)
RotateLeft(T,x)
}
color(p(x)) = Black
color(p(p(x))) = Red
RotateRight(T, p(p(x)))
}
}
else // p(x) != left(p(p(x))
{
// this is the same as above but swap right and left
}
} // end while
color(root(T)) = Black
}
Example Code)
RedBlackDelete(T,z)
{
if ( left(z) == nil(T) || right(z) == nil(T) )
{
y = z
}
else
{
y = TreeSuccessor(z)
}
if ( left(y) != nil(T) )
72
{
x = left(y)
}
else
{
x = right(y)
}
p(x) = p(y)
if ( p(y) == nil(T) )
{
root(T) = x
}
else
{
if ( y == left(p(y)) )
{
left(p(x)) = x
}
else
{
right(p(y)) = x
}
}
if ( y != z )
{
key(z) = key(y)
} // note if y has other fields, copy them too
if ( color(y) == Black )
{
RBDeleteFixup(T,x)
}
}
RBDeleteFixup(T,x)
{
while ( x != root(T) && color(x) == Black )
{
if ( x == left(p(x)) )
{
w = right(p(x))
if ( color(w) = Black )
{
color(p(x)) = Red
RotateLeft(T,p(x))
w = right(p(x))
73
}
if ( color(left(w)) == Black && color (right(w)) == Black )
{
color(w) = Red
x = parent(x)
}
else
{
if ( color(right(w)) == Black )
{
color(left(w)) = Black
color(w) = Red
RotateRight(T,w)
w = right(p(x))
}
color(w) = color(p(x))
color(p(x)) = Black
color (right(w)) = Black
RotateLeft(T,p(x))
x = root(T)
}
}
else // x != left(p(x))
{
// same code as if portion, but switch right and left
} // end ifs about
} // end while
color(x) <--Black
}