Recursion ITI 1121 N. El Kadri. Reminders about recursion In your 1 st CS course (or its equivalent), you have seen how to use recursion to solve numerical.

Slides:



Advertisements
Similar presentations
Recursion Chapter 14. Overview Base case and general case of recursion. A recursion is a method that calls itself. That simplifies the problem. The simpler.
Advertisements

Introduction to Recursion and Recursive Algorithms
Back to Sorting – More efficient sorting algorithms.
The Singleton Pattern II Recursive Linked Structures.
Recursion, pt. 2: Thinking it Through. What is Recursion? Recursion is the idea of solving a problem in terms of solving a smaller instance of the same.
Recursion Ellen Walker CPSC 201 Data Structures Hiram College.
Factorial Recursion stack Binary Search Towers of Hanoi
LISTS & TREES Lecture 8 CS2110 – Fall List Overview 2  Purpose  Maintain an ordered set of elements (with possible duplication)  Common operations.
Searching and Sorting I 1 Searching and Sorting 1.
Recursion. Objectives At the conclusion of this lesson, students should be able to Explain what recursion is Design and write functions that use recursion.
ICS201 Lecture 20 : Searching King Fahd University of Petroleum & Minerals College of Computer Science & Engineering Information & Computer Science Department.
CS 106 Introduction to Computer Science I 03 / 07 / 2008 Instructor: Michael Eckmann.
1 Foundations of Software Design Fall 2002 Marti Hearst Lecture 20: Sorting.
Review of Recursion What is a Recursive Method? The need for Auxiliary (or Helper) Methods How Recursive Methods work Tracing of Recursive Methods.
Recursion. Recursive Solutions Recursion breaks a problem into smaller identical problems – mirror images so to speak. By continuing to do this, eventually.
1 Chapter 7 Recursion. 2 What Is Recursion? l Recursive call A method call in which the method being called is the same as the one making the call l Direct.
Recursion Chapter 7. Chapter 7: Recursion2 Chapter Objectives To understand how to think recursively To learn how to trace a recursive method To learn.
Recursion Chapter 7. Chapter 7: Recursion2 Chapter Objectives To understand how to think recursively To learn how to trace a recursive method To learn.
Searching Chapter Chapter Contents The Problem Searching an Unsorted Array Iterative Sequential Search Recursive Sequential Search Efficiency of.
CS 106 Introduction to Computer Science I 10 / 15 / 2007 Instructor: Michael Eckmann.
CHAPTER 10 Recursion. 2 Recursive Thinking Recursion is a programming technique in which a method can call itself to solve a problem A recursive definition.
CS 106 Introduction to Computer Science I 10 / 16 / 2006 Instructor: Michael Eckmann.
Sorting and Searching Arrays CSC 1401: Introduction to Programming with Java Week 12 – Lectures 1 & 2 Wanda M. Kunkle.
1 Divide and Conquer Binary Search Mergesort Recurrence Relations CSE Lecture 4 – Algorithms II.
Copyright © 2009 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Chapter 19: Recursion.
1 C++ Plus Data Structures Nell Dale Chapter 7 Programming with Recursion Slides by Sylvia Sorkin, Community College of Baltimore County - Essex Campus.
Department of Computer Science and Engineering, HKUST 1 HKUST Summer Programming Course 2008 Recursion.
Chapter 14: Recursion Starting Out with C++ Early Objects
Chapter 2 Recursion: The Mirrors CS Data Structures Mehmet H Gunes Modified from authors’ slides.
Stacks & Recursion. Stack pushpop LIFO list - only top element is visible top.
Chapter 13 Recursion. Topics Simple Recursion Recursion with a Return Value Recursion with Two Base Cases Binary Search Revisited Animation Using Recursion.
Recursion Chapter 7. Chapter Objectives  To understand how to think recursively  To learn how to trace a recursive method  To learn how to write recursive.
Recursion l Powerful Tool l Useful in simplifying a problem (hides details of a problem) l The ability of a function to call itself l A recursive call.
AITI Lecture 20 Trees, Binary Search Trees Adapted from MIT Course 1.00 Spring 2003 Lecture 28 and Tutorial Note 10 (Teachers: Please do not erase the.
COSC 2006 Data Structures I Recursion II
Analyzing Complexity of Lists OperationSorted Array Sorted Linked List Unsorted Array Unsorted Linked List Search( L, x ) O(logn) O( n ) O( n ) Insert(
Recursion Chapter 11. The Basics of Recursion: Outline Introduction to Recursion How Recursion Works Recursion versus Iteration Recursive Methods That.
Lecturer: Dr. AJ Bieszczad Chapter 11 COMP 150: Introduction to Object-Oriented Programming 11-1 l Basics of Recursion l Programming with Recursion Recursion.
SEARCHING UNIT II. Divide and Conquer The most well known algorithm design strategy: 1. Divide instance of problem into two or more smaller instances.
Suppose you have a problem involving N data points. Recursive solution of such problem is a follows: If the problem can be solved directly for N points.
224 3/30/98 CSE 143 Recursion [Sections 6.1, ]
Chapter 13 Recursion. Learning Objectives Recursive void Functions – Tracing recursive calls – Infinite recursion, overflows Recursive Functions that.
Searching. Linear (Sequential) Search Search an array or list by checking items one at a time. Linear search is usually very simple to implement, and.
LISTS Slides of K. Birman, Cornell University. List Overview 2  Purpose  Maintain an ordered collection of elements (with possible duplication)  Common.
CS 61B Data Structures and Programming Methodology July 28, 2008 David Sun.
CSC 211 Data Structures Lecture 13
© M. Gross, ETH Zürich, 2014 Informatik I für D-MAVT (FS 2014) Exercise 12 – Data Structures – Trees Sorting Algorithms.
Chapter 5 Searching and Sorting. Copyright © 2004 Pearson Addison-Wesley. All rights reserved.1-2 Chapter Objectives Examine the linear search and binary.
Chapter 11Java: an Introduction to Computer Science & Programming - Walter Savitch 1 Chapter 11 l Basics of Recursion l Programming with Recursion Recursion.
1 Searching and Sorting Searching algorithms with simple arrays Sorting algorithms with simple arrays –Selection Sort –Insertion Sort –Bubble Sort –Quick.
Copyright © 0 Pearson Education, Inc. Publishing as Pearson Addison-Wesley Starting Out with Java From Control Structures through Data Structures by Tony.
Chapter 9 Sorting. The efficiency of data handling can often be increased if the data are sorted according to some criteria of order. The first step is.
Iterators ITI 1121 N. El Kadri. Motivation Given a (singly) linked-list implementation of the interface List, defined as follows, public interface List.
8.1 8 Algorithms Foundations of Computer Science  Cengage Learning.
1 Recursive algorithms Recursive solution: solve a smaller version of the problem and combine the smaller solutions. Example: to find the largest element.
Chapter 111 Recursion Chapter Objectives become familiar with the idea of recursion learn to use recursion as a programming tool become familiar.
Course: Programming II - Abstract Data Types HeapsSlide Number 1 The ADT Heap So far we have seen the following sorting types : 1) Linked List sort by.
Recursion. Objectives At the conclusion of this lesson, students should be able to Explain what recursion is Design and write functions that use recursion.
CMPT 120 Topic: Searching – Part 2 and Intro to Time Complexity (Algorithm Analysis)
BINARY SEARCH CS16: Introduction to Data Structures & Algorithms Thursday February 12,
CS 116 Object Oriented Programming II Lecture 13 Acknowledgement: Contains materials provided by George Koutsogiannakis and Matt Bauer.
CSC 143 P 1 CSC 143 Recursion [Chapter 5]. CSC 143 P 2 Recursion  A recursive definition is one which is defined in terms of itself  Example:  Compound.
Recursion Powerful Tool
Recursion.
Recursion Version 1.0.
OBJECT ORIENTED PROGRAMMING II LECTURE 23 GEORGE KOUTSOGIANNAKIS
Binary Search one reason that we care about sorting is that it is much faster to search a sorted list compared to sorting an unsorted list the classic.
Applied Algorithms (Lecture 17) Recursion Fall-23
Search,Sort,Recursion.
ITI Introduction to Computing II Lab-12
Presentation transcript:

Recursion ITI 1121 N. El Kadri

Reminders about recursion In your 1 st CS course (or its equivalent), you have seen how to use recursion to solve numerical problems, such as calculating factorials, or locating a target value into a sorted array. We now consider recursive methods applied to linked lists.

The general idea behind recursion is to divide a problem into two or more (smaller) sub-problems and to combine the solutions of the sub-problems to solve the initial problem. The solution to the sub-problems can be obtained by the same strategy. For example, the following algorithm calculates the sum of the elements from position 0 to k of an array, t, where k is the size of t minus 1.

1. Let’s say that you can obtain the sum of the values from positions 1 to k by some mean and let’s call this result s. 2. How would you obtain the final result? s+t[0]. 3. How to obtain s ? The sum of all the elements of the sub-interval? Similarly, except this time the interval is smaller, 1 to (length - 1).

4. How should the initial call look like? sum(t, 0) It’s important that the size of the problem gets smaller with each successive call, otherwise this would create an infinite recursion (similar to infinite loops). To stop the recursion, there must be a size of problem such that the result can be computed directly (no recursive call). Let’s call those special cases where the result can be obtained directly the base cases, there is at least one but there can be more than one base case. What is the base case for the sum? Eventually, the interval contains only one value, the last position of this array, the sum simply returns this element.

Recursive Methods A recursive method therefore always obeys the following pattern: method( parameters ) { result; if ( test parameters for base case ) { // base case // calculate the result directly // i.e. no recursive call, recursion stop here } else { // general case // pre-processing; partitioning the data for example result = method( sub-set of the data ); // recursive call // post-processing; combining the results for example } return result; }

Base Case  The base case must be tested first, otherwise, this would cause an infinite recursion.  Certain programming languages (such as Lisp, Prolog or Haskell) have no loop control-structures and therefore recursion is the only way to create iterations.

Pitfall Never mix iterative control structures and recursion. Recursion is used in place of iterative control structures. If you find yourself writing something like: void foo( Node p ) { while (... ) {... } foo(p.next) } something is wrong.

Factorial public static int factorial( int n ) { if ( n<=1 ) return 1; else return n * factorial( n-1 ); }

The factorial method corresponds to the general pattern, the base case is tested first, and the result is computed directly without the need for a recursive call (recursion stops here!), the general case is always making the value of the parameter smaller so that eventually the base case will be applied.

Binary Search public static int binarySearch( int value, int[] array ) { return binSearch( value, array, 0, array.length-1 ); } private static int binSearch( int value, int[] array, int lo, int hi ) { if ( lo > hi ) return -1; int middle = ( lo + hi ) / 2; if ( value == array [middle ] ) return middle; if ( value < array[ middle ] ) return binarySearch( value, array, lo, middle - 1 ); else return binarySearch( value, array, middle+1, hi ); }

A binarySearch can be applied to search for a target value in a sorted array. This method is efficient because the size of the intervals of positions to search decreases by a factor of two with every successive call. Base case: an interval of size zero, no recursive call. General case: creates smaller and smaller intervals of values to look.

Recursive list processing - head + tail strategy Let’s develop a general strategy to solve list processing problems recursively. To develop this strategy, let’s consider calculating the size (length) of a list. Let’s divide the initial list in two parts, its head (the first element) and its tail (the rest of the list). By analogy with the method sum let’s say that we can obtain the size of the tail by some mean, let’s call this value s. If you knew s, what would be the size of the entire list? Answer: s+1 How to obtain s ? Well, s is the size of a list, and, furthermore, it’s a shorter one, we could apply (recursively) the method that we are developing.

The total length of the list will be s + 1.

The length of the list designated by head will be length of s + 1.

The length of the list designated by head is 0.

Implementation The methods presented in this lecture are instance methods of a class defined as follows: public class OrderedList { private static class Node { private Comparable value; private Node next; Node ( Comparable value, Node next ) { this.value = value; this.next = next; } private Node first; public OrderedList () { first = null; } // other methods... }

“head + tail” strategy Given a list l, apply the method recursively to the rest of the list (tail). In general, the result obtained by the recursive call is used in combination with the head to calculate a final result. This strategy does not solve all recursive list processing problems but it’s the first strategy you should try. For the method size, what would be the base case? For each recursive call, the list gets smaller and smaller, what is then the smallest list. The empty list! The empty list is a valid case, its length is zero, however, it has to be a base case because it has no tail! Because of that, for all methods that apply the “head + tail” strategy, the empty list cannot be part of the general case.

int size( Node p ) { if ( p == null ) return 0; else return 1 + size( p.next ); }  Can this method be called from outside of the class? How is it called initially?

No, it cannot be called from outside of the class because it needs to be given a reference to a node (which is an implementation detail and should be private to the class). We need an auxiliary method that initiates the process starting with the first element of the list: public int size() { return size( first ); }

Because, the recursive method cannot and should not be used from outside of the class, it should be declared private: public int size() { return size( head ); } private static int size( Node p ) { if ( p == null ) return 0; else return 1 + size( p.next ); } All our recursive methods will obey this pattern. They will all have a public part that call the recursive method, which is private. The public method initiates the first call to the recursive method with a reference to the first node.

The “head + tail” strategy is not the only way to solve this problem, we could have chosen to divide the list into two sublists of approximately the same size and sum their lengths. In fact, for a list of length n, there are n − 1 ways to separate this list into two sublists. The “head + tail” strategy is just a special case; however, no recursive call is made for the head. Given our list implementation, the “head + tail” strategy is simpler to apply. The strategy that consists in dividing the list into two sublists of approximately the same size can lead to more efficient algorithms, but that’s a subject left for CSI 2114 (data-structures), here, we’ll use the “head + tail” strategy.

findMax() Write a recursive method that finds the maximum value for a list of Comparable objects. The smallest valid list contains one element. The auxiliary method that initiates the search starting with the first value will be: public Object findMax() { if ( first == null ) throw new IllegalStateException(); return findMax( first ); } Let’s apply the “head+tail” strategy and consider the general case first. There will be a recursive call for the tail: Comparable result = (Comparable) findMax( p.next ); What does the result represent? Well, it’s the maximum value for the rest of the list.

What should be done next? Compare this value to the value of the head. if ( result.compareTo( p.value ) > 0 ) return result; else return p.value; What should be the base case? This process makes the list smaller and smaller, what is the smallest list to handle? It is not the empty list, we said the smallest valid list contains one element.

What value should be returned in that case? The value found at that node. if ( p.next == null ) return p.value; Let’s put everything together: public Object findMax() { if ( first == null ) throw new IllegalStateException(); return findMax( first ); } private Object findMax( Node p ) { if ( p.next == null ) return p.value; Comparable result = (Comparable) findMax( p.next ); if ( result.compareTo( p.value ) > 0 ) return result; else return p.value; }

indexOf ( Object obj ) The method indexOf returns the position of the first occurrence of obj in the list, the first element of the list is the position 0, the method returns -1 if the element is not found in the list. Following the “head + tail” strategy, the general case will involve a recursive call for the tail of the list: int result = indexOf( o, p.next ); What does the result represent? It’s the position of o in p.next.

What is the position of o with respect to p? if ( p.value.equals( o ) ) return 0; else if ( result == -1 ) return result; else return result + 1; }

What is the base case? The smallest list is the empty list, it cannot contain the value we’re looking for, we should return the special value -1, to indicate that a match could not be found. if ( p == null ) return -1;

Putting everything together: private int indexOf( Object o, Node p ) { if ( p == null ) return -1; int result = indexOf( o, p.next ); if ( p.value.equals( o ) ) return 0; if ( result == -1 ) return result; else return result + 1; }  Does this work? Yes, but it’s inefficient. Why?

The recursion should stop as soon as the first occurrence has been found. private int indexOf( Object o, Node p ) { if ( p == null ) return -1; if ( p.value.equals( o ) ) return 0; int result = indexOf( o, p.next ); if ( result == -1 ) return result; else return result + 1; }

contains( Object o ) The method contains returns true if the list contains the element o, i.e. there is a node such that value.equals( o ). The auxiliary will initiate the search from the first node. public boolean contains( Object o ) { return contains( o, first ); }

The signature of the recursive methods will be: private boolean contains( Object o, Node p ) {... } Let’s apply the head and tail strategy. The empty list has to be part of the base case, if list is empty it cannot contain the object, contains should return false: if ( p == null ) return false; The strategy suggests to call contains for the tail: boolean result = contains( o, p.next );

Contains is similar to indexOf, the method should stop as soon as the first occurrence has been found: private boolean contains( Object o, Node p ) { if ( p == null ) return false; else if ( p.value.equals( o ) ) return true; else return contains( o, p.next ); }

The methods we looked at considered only one element at a time but this does not need to be. Let’s consider the method isIncreasing. It returns true if each element of the list is equal to or greater than its predecessor. To solve this problem, we can scan the list and return false as soon a consecutive pair of elements has been found such that the predecessor is greater than its successor, if the end is reached this means the list is increasing.

public boolean isIncreasing() { }

Exercises For a singly linked list implement the following methods recursively: int indexOfLast( Object o ); returns the position of the last occurrence of o and -1 if the element cannot be found in the list. void addLast( Object o ); boolean equals( OrderedList other ); compares all the elements of this list to the elements of the other list; the lists are not necessarily of the same length.

The methods considered so far do not modify the structure of the list. In the case of methods that do not modify the list, such as indexOf and contains, the only consequence of unnecessary recursive calls is inefficiency. However, when the methods are allowed to change the structure of the list, such as remove below, the consequences of unnecessary recursive calls are severe. Let’s implement the method remove, that removes the first occurrence of an object.

Another difference is that the reference in the header of the list can be modified, this will be done by the helper method. Consider the method that initiates the removal of the object starting from the head of the list: public void remove( Object o ) { if ( head != null ) if ( head.value.equals( o ) ) head = head.next; else remove( o, head ); }

If the first node equals to the parameter o this element is removed and nothing else needs to be done. Otherwise, we proceed with the rest of the list. This is convenient, since the node to be removed is always the next one. This process will work because the auxiliary function has checked the first node and therefore the recursive method remove knows that the current element has been checked.

public void remove( Object o ) { if ( head != null ) if ( head.value.equals( o ) ) head = head.next; else remove( o, head ); } private void remove( Object o, Node p ) { if ( p.next != null ) if ( p.next.value.equals( o ) ) p.next = p.next.next; else remove( o, p.next ); }