Key Assignment Draft: Include session changes
The first two Individual Projects used linked lists for stacks, queues, and hashing implementations. With this task, searching performance with linked lists will be addressed.
Part 1: Your tasks for this assignment are the following:
- Discuss the use of a binary tree when searching for keys in an array.
- Discuss the use of a binary tree when searching for keys in a linked list.
Part 2: Complete the following program:
- Describe a linked list structure to support binary searching.
- Create pseudo code to describe a binary search with this linked list variation.
it will be inserted into section 4: searching
1 IT265 – 2003B Data Structures using Java document shell Demetricus Dixon 8/26/20 Contents Abstract2 Section 1: Lists, Stacks, and Queues3 Section 1.1: Stacks3 Section 1.2: Queues5 Section 2: Heaps and Trees7 Section 3: Sorting Algorithms10 PART 1.10 PART 2.11 Section 4: Searching12 Section 5: Recursion TBD13 Conclusion14 References15 Abstract Section 1: Lists, Stacks, and Queues Let the linked list node be represented by the ‘Node’. Each ‘Node’ has a data item ‘Data’ of data type T and a ‘NextRef’ attribute pointing to the next node in the linked list. class Node{ T Data;// The data item of a node. Node NextRef;// link to the next Node in the list } We will implement stacks and queues using linked lists. The sub-sections below has the details of the implementations. Section 1.1: Stacks Assume a ‘Head’ Node exists with the NextRef attribute pointing to the first node in the stack or being null if the stack is empty. Given below is the pseudo code for the following 3 stack methods : push(item), pop( ) and display( ). Note that in the Java style pseudocodes given below, the top of the stack is the Node pointed to by ‘head’. void push(T item) { // create a new 'Node' called newnode // with item as the 'Data' and null as its 'NextRef' Node newnode = new Node(item, null); if(head==null) // stack is empty head = newnode;// set the newnode as the ‘head’ else // stack is not empty { // push to the top of the stack newnode.nextRef = head; head = newnode;// the newnode is the new head } } T pop()// Note that the return type is T - the data type of ‘Data’ { if(head==null) // stack is empty return null; // stack non-empty. Return the 'Data' of the head // and set the head to head.nextRef T topData = head.Data; head = head.nextRef; return topData; } void display()// The display function prints the stack from top to bottom { if(head==null) // stack is empty { print("empty stack!"); return; } // Set a pointer 'curr' to the head Node curr = head; while(curr!=null) { print(curr.Data); // print curr's data curr = curr.nextRef; // advance curr to the next Node } } Section 1.2: Queues Assume ‘front’ and ‘rear’ nodes exist with the ‘NextRef’ attributes pointing to the first and last nodes of the queue or being null if the queue is empty. This section describes the Java style pseudo codes for the following 3 queue methods: enqueue(item) dequeue( ) display( ) void enqueue(T item)// T is the datatype of the item { // create a new 'Node' called ‘newnode’ // with item as the 'Data' and null as its 'NextRef' Node newnode = new Node(item, null); if(front==null) // queue is empty. front = rear = null { front = newnode;// set the newnode as the ‘front’ rear = newnode;// set the newnode as the ‘rear’ } else // queue is not empty { rear.nextRef = newnode;// enqueue to the rear of the queue rear = newnode;// the newnode is the new rear } } T dequeue()// Note that the return type is T - the data type of ‘Data’ { if(front==null) // queue is empty return null; // queue non-empty. Return the 'Data' of the head // and set the front to front.nextRef T frontData = front.Data; front = front.nextRef; return frontData; } void display()// The display function prints the queue front to rear { if(front==null) // queue is empty { print("empty queue!"); return; } // Set a pointer 'curr' to the head Node curr = front; while(curr!=null) { print(curr.Data); // print curr's data curr = curr.nextRef; // advance curr to the next Node } } Section 2: Heaps and Trees Assume that the hashtable is a simple array of size 8, with indices 0..7. Numeric keys are mapped by a hash function that gives the mod(8,n) value for any key n, yielding the hashtable index for that key. For instance, the hashtable index for 100 is (100 mod 8) = 4. A Hashtable entry is null unless a key exists with that index as its hashed index; if so, the Hashtable entry points to the first node of a linked list of keys with that hash index. The last node on this linked list has a null reference for the next referenced node. Like in the previous section, assume the occurrence of a linked list node is represented by the object “Node” and its “Data” and “NextRef” attributes. class Node{ int Data;// The data item of a node. Node NextRef;// link to the next Node in the list } First, let us look at the structure of our hashtable. The hashtable is an array of size 8, each element of which is a ‘Node’, initialized to null. Node array[8]; // The hashtable of size 8. // Initially, the hashtable is empty. for(int i=0;i<8;i++) array[i] = null; given below are the java style pseudocodes for the insert(int) and remove(int) operations for the hash table. void insert(int element) // insert element into the hash table { int index = element%8; // find the index the element must be put into // create a 'node' with 'element' as the 'data' node newnode = new node(element, null); // node.nextref = null if(array[index]==null) // this is the first element with that index { array[index] = newnode; return; } /* there is already a linked list at array[index]. add 'newnode' to that linked list. we could add the 'newnode' anywhere in the list. here, we add it at the head. */ newnode.nextref = array[index]; array[index] = newnode; } the insert(element) function first finds the index by doing the computation element % 8. then, if the array[index] does not hold a linked list, it adds a node with element as its data, and null as its nextref at array[index]. if array[index] contains a linked list, it simply adds the new node at the head of that linked list. now, let us look at the remove(element) operation. void remove(int element) { int index = element%8; // find the hash index // the 'element', if it exists in the hash table must // be in the linked list at array[index]. the rest of the // function is the standard technique to delete a node from a linked list node head = array[index]; node prev = null;// a reference to the previous node to ‘head’ while(head!=null)// traverse the list { if(head.data == element) // this is the node to be deleted. break; // head refers to the node to be deleted. prev = head;// set ‘prev’ to what head is now head = head.nextref;// advance the head } if(head==null) // ‘element’ not found in the linked list print("element does not exist"); else if(prev==null) // 'element' is in the first node in the linked list { array[index] = head.nextref; print("element deleted"); } else // 'element' exists, but not at the head of the linked list { prev.nextref = head.nextref; print("element deleted"); } } the remove function first finds the hash index ‘index’ of the element. then, the element, if it exists in the hash table, must be somewhere in the linked list at array[index]. the while loop traverses the list until the element is found, or the list has been fully traversed. the if-else if-else block takes care of the three cases - element not found, element found as the very first node, element found somewhere else in the list. notice that the insert function inserts an element even if another copy of it exists. it is quite easy to modify the insert function to first search for the element, and then report that it exists instead of inserting it. section 3: sorting algorithms part 1. 1.all the sorting algorithms have a common goal, which is, to output a sorted list, but each algorithms has its advantages and disadvantages .the differences in sorting algorithms which are considered while choosing an algorithm are: · the approach must be easy to understand · the sorting technique must be stable. it means that numbers with same value must not be swapped. · running time: the running time describes how many operations an algorithm must carry out before it completes. the algorithm which takes less time must be used. · space complexity: it specifies how much space must be allocated to run an algorithm. · efficiency · format of input list must also be considered. for example., if the array is already sorted bubble sort must be used since it takes o(1) time in this case. 2. · since the algorithm must not be complex, quicksort is only used for large data sets though it is fast, whereas, though bubble sort is slow it is usually used because it array[i]="null;" given="" below="" are="" the="" java="" style="" pseudocodes="" for="" the="" insert(int)="" and="" remove(int)="" operations="" for="" the="" hash="" table.="" void="" insert(int="" element)="" insert="" element="" into="" the="" hash="" table="" {="" int="" index="element%8;" find="" the="" index="" the="" element="" must="" be="" put="" into="" create="" a="" 'node'="" with="" 'element'="" as="" the="" 'data'="" node="" newnode="new" node(element,="" null);="" node.nextref="null" if(array[index]="=null)" this="" is="" the="" first="" element="" with="" that="" index="" {="" array[index]="newNode;" return;="" }="" *="" there="" is="" already="" a="" linked="" list="" at="" array[index].="" add="" 'newnode'="" to="" that="" linked="" list.="" we="" could="" add="" the="" 'newnode'="" anywhere="" in="" the="" list.="" here,="" we="" add="" it="" at="" the="" head.="" */="" newnode.nextref="array[index];" array[index]="newNode;" }="" the="" insert(element)="" function="" first="" finds="" the="" index="" by="" doing="" the="" computation="" element="" %="" 8.="" then,="" if="" the="" array[index]="" does="" not="" hold="" a="" linked="" list,="" it="" adds="" a="" node="" with="" element="" as="" its="" data,="" and="" null="" as="" its="" nextref="" at="" array[index].="" if="" array[index]="" contains="" a="" linked="" list,="" it="" simply="" adds="" the="" new="" node="" at="" the="" head="" of="" that="" linked="" list.="" now,="" let="" us="" look="" at="" the="" remove(element)="" operation.="" void="" remove(int="" element)="" {="" int="" index="element%8;" find="" the="" hash="" index="" the="" 'element',="" if="" it="" exists="" in="" the="" hash="" table="" must="" be="" in="" the="" linked="" list="" at="" array[index].="" the="" rest="" of="" the="" function="" is="" the="" standard="" technique="" to="" delete="" a="" node="" from="" a="" linked="" list="" node="" head="array[index];" node="" prev="null;" a="" reference="" to="" the="" previous="" node="" to="" ‘head’="" while(head!="null)" traverse="" the="" list="" {="" if(head.data="=" element)="" this="" is="" the="" node="" to="" be="" deleted.="" break;="" head="" refers="" to="" the="" node="" to="" be="" deleted.="" prev="head;" set="" ‘prev’="" to="" what="" head="" is="" now="" head="head.nextRef;" advance="" the="" head="" }="" if(head="=null)" ‘element’="" not="" found="" in="" the="" linked="" list="" print("element="" does="" not="" exist");="" else="" if(prev="=null)" 'element'="" is="" in="" the="" first="" node="" in="" the="" linked="" list="" {="" array[index]="head.nextRef;" print("element="" deleted");="" }="" else="" 'element'="" exists,="" but="" not="" at="" the="" head="" of="" the="" linked="" list="" {="" prev.nextref="head.nextRef;" print("element="" deleted");="" }="" }="" the="" remove="" function="" first="" finds="" the="" hash="" index="" ‘index’="" of="" the="" element.="" then,="" the="" element,="" if="" it="" exists="" in="" the="" hash="" table,="" must="" be="" somewhere="" in="" the="" linked="" list="" at="" array[index].="" the="" while="" loop="" traverses="" the="" list="" until="" the="" element="" is="" found,="" or="" the="" list="" has="" been="" fully="" traversed.="" the="" if-else="" if-else="" block="" takes="" care="" of="" the="" three="" cases="" -="" element="" not="" found,="" element="" found="" as="" the="" very="" first="" node,="" element="" found="" somewhere="" else="" in="" the="" list.="" notice="" that="" the="" insert="" function="" inserts="" an="" element="" even="" if="" another="" copy="" of="" it="" exists.="" it="" is="" quite="" easy="" to="" modify="" the="" insert="" function="" to="" first="" search="" for="" the="" element,="" and="" then="" report="" that="" it="" exists="" instead="" of="" inserting="" it.="" section="" 3:="" sorting="" algorithms="" part="" 1.="" 1.all="" the="" sorting="" algorithms="" have="" a="" common="" goal,="" which="" is,="" to="" output="" a="" sorted="" list,="" but="" each="" algorithms="" has="" its="" advantages="" and="" disadvantages="" .the="" differences="" in="" sorting="" algorithms="" which="" are="" considered="" while="" choosing="" an="" algorithm="" are:="" ·="" the="" approach="" must="" be="" easy="" to="" understand="" ·="" the="" sorting="" technique="" must="" be="" stable.="" it="" means="" that="" numbers="" with="" same="" value="" must="" not="" be="" swapped.="" ·="" running="" time:="" the="" running="" time="" describes="" how="" many="" operations="" an="" algorithm="" must="" carry="" out="" before="" it="" completes.="" the="" algorithm="" which="" takes="" less="" time="" must="" be="" used.="" ·="" space="" complexity:="" it="" specifies="" how="" much="" space="" must="" be="" allocated="" to="" run="" an="" algorithm.="" ·="" efficiency="" ·="" format="" of="" input="" list="" must="" also="" be="" considered.="" for="" example.,="" if="" the="" array="" is="" already="" sorted="" bubble="" sort="" must="" be="" used="" since="" it="" takes="" o(1)="" time="" in="" this="" case.="" 2.="" ·="" since="" the="" algorithm="" must="" not="" be="" complex,="" quicksort="" is="" only="" used="" for="" large="" data="" sets="" though="" it="" is="" fast,="" whereas,="" though="" bubble="" sort="" is="" slow="" it="" is="" usually="" used="" because="">8;i++) array[i] = null; given below are the java style pseudocodes for the insert(int) and remove(int) operations for the hash table. void insert(int element) // insert element into the hash table { int index = element%8; // find the index the element must be put into // create a 'node' with 'element' as the 'data' node newnode = new node(element, null); // node.nextref = null if(array[index]==null) // this is the first element with that index { array[index] = newnode; return; } /* there is already a linked list at array[index]. add 'newnode' to that linked list. we could add the 'newnode' anywhere in the list. here, we add it at the head. */ newnode.nextref = array[index]; array[index] = newnode; } the insert(element) function first finds the index by doing the computation element % 8. then, if the array[index] does not hold a linked list, it adds a node with element as its data, and null as its nextref at array[index]. if array[index] contains a linked list, it simply adds the new node at the head of that linked list. now, let us look at the remove(element) operation. void remove(int element) { int index = element%8; // find the hash index // the 'element', if it exists in the hash table must // be in the linked list at array[index]. the rest of the // function is the standard technique to delete a node from a linked list node head = array[index]; node prev = null;// a reference to the previous node to ‘head’ while(head!=null)// traverse the list { if(head.data == element) // this is the node to be deleted. break; // head refers to the node to be deleted. prev = head;// set ‘prev’ to what head is now head = head.nextref;// advance the head } if(head==null) // ‘element’ not found in the linked list print("element does not exist"); else if(prev==null) // 'element' is in the first node in the linked list { array[index] = head.nextref; print("element deleted"); } else // 'element' exists, but not at the head of the linked list { prev.nextref = head.nextref; print("element deleted"); } } the remove function first finds the hash index ‘index’ of the element. then, the element, if it exists in the hash table, must be somewhere in the linked list at array[index]. the while loop traverses the list until the element is found, or the list has been fully traversed. the if-else if-else block takes care of the three cases - element not found, element found as the very first node, element found somewhere else in the list. notice that the insert function inserts an element even if another copy of it exists. it is quite easy to modify the insert function to first search for the element, and then report that it exists instead of inserting it. section 3: sorting algorithms part 1. 1.all the sorting algorithms have a common goal, which is, to output a sorted list, but each algorithms has its advantages and disadvantages .the differences in sorting algorithms which are considered while choosing an algorithm are: · the approach must be easy to understand · the sorting technique must be stable. it means that numbers with same value must not be swapped. · running time: the running time describes how many operations an algorithm must carry out before it completes. the algorithm which takes less time must be used. · space complexity: it specifies how much space must be allocated to run an algorithm. · efficiency · format of input list must also be considered. for example., if the array is already sorted bubble sort must be used since it takes o(1) time in this case. 2. · since the algorithm must not be complex, quicksort is only used for large data sets though it is fast, whereas, though bubble sort is slow it is usually used because it>