# The Singleton Pattern II Recursive Linked Structures.

## Presentation on theme: "The Singleton Pattern II Recursive Linked Structures."— Presentation transcript:

The Singleton Pattern II Recursive Linked Structures

Recursive Data Structures A recursive data structure is either a basic value, such as null, or contains a value of the same type of data structure The OneWayNode class is a good example

public class OneWayNode{ public String value; public OneWayNode next; public OneWayNode(String s, OneWayNode n){ value = s; next = n; } The OneWayNode Class OneWayNode head = new OneWayNode("A", new OneWayNode("B", null)); “A”“A”“B”“B” head

Recursive Data Structures A linked structure is either –empty ( null ) or –contains a data value followed by another linked structure ( next ) of exactly the same form

Recursive Algorithms When processing a linked structure –Handle the case of empty ( null ) –Do something with the data value and then “ recurse ” with the linked structure ( next ) of exactly the same form –Package the algorithms as static methods in a toolbox class (like Math )

if the linked structure is not empty process the value process the rest of the linked structure Some Recursive Patterns: Traversals Note that there is an if statement rather than a while loop The process just quits when the base case (the end of the structure) is reached

static void traverse(OneWayNode n){ if (n != null){ process(n.value) traverse(n.next); } Some Recursive Patterns: Traversals “A”“A”“B”“B” head n

static void traverse(OneWayNode n){ if (n != null){ process(n.value) traverse(n.next); } Some Recursive Patterns: Traversals “A”“A”“B”“B” head n n

static void traverse(OneWayNode n){ if (n != null){ process(n.value) traverse(n.next); } Some Recursive Patterns: Traversals “A”“A”“B”“B” head n n n

if the linked structure is empty return false else if the target equals the value return true else return the result of searching the rest of the linked structure Some Recursive Patterns: Search

static boolean contains(OneWayNode n, String target){ if (n == null) return false; else if (target.equals(n.value)) return true; else return contains(n.next, target); } Some Recursive Patterns: Search “A”“A”“B”“B” head

check preconditions if i == 0 return the value else return the result of getting from the rest of the linked structure at i - 1 Some Recursive Patterns: get(i)

static String get(OneWayNode n, int i){ if (i = length(n) throw new IllegalArgumentException( "Index out of range"); if (i == 0) return n.value; else return get(n.next, i - 1); } Some Recursive Patterns: get(i) “A”“A”“B”“B” head

if the linked structure is empty return 0 else return 1 + the length of the rest of the linked structure Some Recursive Patterns: length

static int length(OneWayNode n){ if (n == null) return 0; else return 1 + length(n.next); } Some Recursive Patterns: length “A”“A”“B”“B” head

if the linked structure is empty return null else return a new node whose value is the value and whose next is the result of copying the rest of the linked structure Some Recursive Patterns: Copy the Structure

static OneWayNode copy(OneWayNode n){ if (n == null) return null; else return new OneWayNode(n.value, copy(n.next)); } Some Recursive Patterns: Copy the Structure “A”“A”“B”“B” head

Recursion and Data Structures Recursive algorithms can be used to “ back up ” through a data structure, such as a string or a linked structure The calls move forward, and the real work is done after each call returns or “ backs up ”

if the linked structure is not empty process the rest of the linked structure process the value Some Recursive Patterns: Right to Left Traversals Note we just invert the order of the two statements within the if statement As the recursion unwinds from the end, each value is processed

Some Recursive Patterns: Right to Left Traversals “A”“A”“B”“B” head static void backtrack(OneWayNode n){ if (n != null){ backtrack(n.next); process(n.value); } n

Some Recursive Patterns: Right to Left Traversals “A”“A”“B”“B” head static void backtrack(OneWayNode n){ if (n != null){ backtrack(n.next); process(n.value); } n n

Some Recursive Patterns: Right to Left Traversals “A”“A”“B”“B” head static void backtrack(OneWayNode n){ if (n != null){ backtrack(n.next); process(n.value); } n n n

Some Recursive Patterns: Right to Left Traversals “A”“A”“B”“B” head static void backtrack(OneWayNode n){ if (n != null){ backtrack(n.next); process(n.value); } n n

Some Recursive Patterns: Right to Left Traversals “A”“A”“B”“B” head static void backtrack(OneWayNode n){ if (n != null){ backtrack(n.next); process(n.value); } n

static void reverseOutput(OneWayNode n){ if (n != null){ reverseOutput(n.next); System.out.println(n.value); } Some Recursive Patterns: Output in Reverse “A”“A”“B”“B” head

Problems With Current Version Uses static methods for all of the processing, not very object-oriented Uses null for the case of an empty structure, so we must check for null with if statements in every method OneWayNode n = new OneWayNode("Ken", null); int myLength = NodeMethods.length(n);

Problems With Current Version A linked structure should be able to compute its own length But a recursive implementation of this would be pretty hard OneWayNode n = new OneWayNode("Ken", null); int myLength = n.length();

Our View of the Current Version A linked structure is either –empty ( null ) or –contains a data value followed by another linked structure ( next ) of exactly the same form

A Better View A linked structure is either –empty (an empty node) or –compound (a compound node that contains a data value and another linked structure)

From Data to Algorithms Two concrete classes of nodes that implement the same interface The empty node ’ s methods handle the base cases The compound node ’ s methods handle the recursive ones Use polymorphism to make the choices (no explicit if statements in the code!)

From Data to Algorithms OneWayNode CompoundNodeEmptyNode

public interface OneWayNode{ public void traverse(Visitor v); public void backwardTraverse(Visitor v); public int length(); public OneWayNode copy(); public OneWayNode reverse(); public OneWayNode reverse(OneWayNode result); } The OneWayNode Interface A Visitor is an object that runs a method visit to process each value during a traversal

public interface Visitor{ public void visit(String s); } Creating and Using a Visitor A Visitor is an object that runs a method visit to process each value during a traversal Visitor v = new Visitor(){ public void visit(String s){ System.out.print(s + " "); } }; aOneWayNode.traverse(v); aOneWayNode.backwardTraverse(v);

Node Classes, Etc. EmptyNode – The class of an empty node CompoundNode – The class of compound nodes EmptyNode.THE_EMPTY_NODE – The single instance used for any empty node

public class NodeTester{ public static void main (String[] args){ String[] strings = {"Bill", "Mary", "Sam", "Sue"}; OneWayNode n1 = EmptyNode.THE_EMPTY_NODE; for (int i = 0; i < strings.length; i++) n1 = new CompoundNode(strings[i], n1); Visitor v = new Visitor(){ public void visit(String s){ System.out.print(s + " "); } }; System.out.println(n1.length()); System.out.print("["); n1.traverse(v); System.out.println("]"); System.out.print("["); n1.backwardTraverse(v); System.out.println("]"); OneWayNode n2 = n1.copy(); OneWayNode n3 = n1.reverse(); } Using the New Structure

public interface OneWayNode{ public void traverse(Visitor v); public void backwardTraverse(Visitor v); public int length(); public OneWayNode copy(); public OneWayNode reverse(); public OneWayNode reverse(OneWayNode result); } The OneWayNode Interface

public class EmptyNode implements OneWayNode{ public void traverse(Visitor v){return;} public void backwardTraverse(Visitor v){return;} public int length(){return 0;} public OneWayNode copy(){return THE_EMPTY_NODE;} public OneWayNode reverse(){ return THE_EMPTY_NODE; } public OneWayNode reverse(OneWayNode result){ return THE_EMPTY_NODE; } static public final OneWayNode THE_EMPTY_NODE = new EmptyNode(); } The EmptyNode Class The singleton instance is created just once

public class CompoundNode implements OneWayNode{ private String value; private OneWayNode next; public CompoundNode(String value, OneWayNode next){ this.value = value; this.next = next; } public CompoundNode(){ this("", EmptyNode.THE_EMPTY_NODE); } public void traverse(Visitor v){ // NO IF STATEMENTS! v.visit(value); next.traverse(v); } public void backwardTraverse(Visitor v){ next.backwardTraverse(v); v.visit(value); } The CompoundNode Class

public int length(){ return 1 + next.length(); } public OneWayNode copy(){ return new CompoundNode(value, next.copy()); } public OneWayNode reverse(){ return reverse(EmptyNode.THE_EMPTY_NODE); } public OneWayNode reverse(OneWayNode result){ return next.reverse(new CompoundNode(value, result)); } The CompoundNode Class

Recursive Objects Instead of using explicit if statements to handle choices, try using polymorphism, which automatically handles them Divide the cases into different types of objects that obey the same interface (avoid using null for the simple case) Use simple objects for the base cases and recursive objects for the recursive ones