100% found this document useful (1 vote)
84 views

135 Week 11

This document discusses various recursive algorithms and searching algorithms. It covers objectives like writing recursive algorithms, linear and binary search, and sorting algorithms like selection sort, bubble sort, and quicksort. It provides examples of recursive algorithms like calculating factorials recursively, directory listing recursively, generating anagrams recursively, and solving the Towers of Hanoi puzzle recursively. It also discusses linear and binary searching of arrays.

Uploaded by

api-3725347
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
84 views

135 Week 11

This document discusses various recursive algorithms and searching algorithms. It covers objectives like writing recursive algorithms, linear and binary search, and sorting algorithms like selection sort, bubble sort, and quicksort. It provides examples of recursive algorithms like calculating factorials recursively, directory listing recursively, generating anagrams recursively, and solving the Towers of Hanoi puzzle recursively. It also discusses linear and binary searching of arrays.

Uploaded by

api-3725347
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 47

CSE 135

Week 11

By
Javed Siddique
Objectives
 After this week, you should be able to
 Write recursive algorithms.
 Decide when to use recursion.
 Perform linear and binary search algorithms on
small arrays.
 Determine whether a linear or binary search is
more effective for a given situation.
 Understand selection, bubble sort, and
(recursive) quicksort algorithms.
 Understand how these algorithms compare.
Recursive Algorithms
 Within a given method, we are allowed to
call other accessible methods.
 It is also possible to call the same method
from within the method itself.
 This is called a recursive call.
 For certain problems a recursive solution is
very natural and simple.
 It is possible to implement a recursive
algorithm without using recursion, but the
code can be more complex.
Example of Recursion
 The factorial of N is the product of the first N
positive integers:
N! = N * (N – 1) * (N – 2 ) * . . . * 2 *1
 This is useful for many situations, e.g.
 there are n! possible sequences of n objects
 there are n!(n-k)!/k! unique subsets of size k, from a
set of size n.
 The factorial of N can be defined recursively as
1 if N = 1
factorial( N ) =
N * factorial( N-1 ) otherwise
Recursive Method
 An recursive method is a method that contains a
statement (or statements) that makes a call to itself.
 Implementing the factorial of N recursively will result in the
following method.

public int factorial( int N ) {


Test to stop or
continue. if ( N == 1 ) {
End case: return 1;
recursion stops.
}
else {
Recursive case:
recursion continues.
return N * factorial( N-1 );
}
}
The details …
 As with any call, a recursive call results in the
creation of temporary workspace for the called
method and copying of parameters.
 Each call to a method results in the creation of a
separate workspace with new copies of variables
(local and formal parameters).
 When a recursive call ends, flow returns to the
caller.
Example factorial( 3 );

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else {
N 3
return N * factorial( N-1 );
}
}

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else { N 2
return N * factorial( N-1 );
}
}

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else { N 1
return N * factorial( N-1 );
}
}
6
Example factorial( 3 );

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else { 2 N 3
return N * factorial( N-1 );
}
}

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else { 1 N 2
return N * factorial( N-1 );
}
}

public int factorial( int N ) {


if ( N == 1 ) {
return 1;
} else { N 1
return N * factorial( N-1 );
}
}
Directory Listing
 List the names of all files in a given directory and
its subdirectories.
public void directoryListing(File dir) {

//assumption: dir represents a directory


String[] fileList = dir.list(); //get the contents
String dirPath = dir.getAbsolutePath();

for (int i = 0; i < fileList.length; i++) {


Test File file = new File(dirPath + "/" + fileList[i]);

if (file.isFile()) { //it's a file


End case System.out.println( file.getName() );

} else {
Recursive case directoryListing( file ); //it's a directory
} //so make a
} //recursive call
}
Anagram
 List all anagrams of a given word.

Word CAT

CTA
ATC
Anagrams
ACT
TCA
TAC
Anagram Solution
 The basic idea is to make recursive calls on a
sub-word after every rotation. Here’s how:
C A T Recursion
CAT
CTA
Rotate Left

A T C Recursion
ATC
ACT
Rotate Left

TCA
T C A Recursion
TAC
Anagram Method
public void anagram( String prefix, String suffix ) {
String newPrefix, newSuffix;
int numOfChars = suffix.length();

Test if (numOfChars == 1) {
//End case: print out one anagram
End case System.out.println( prefix + suffix );

} else {
for (int i = 1; i <= numOfChars; i++ ) {
newSuffix = suffix.substring(1, numOfChars);
newPrefix = prefix + suffix.charAt(0);
Recursive case anagram( newPrefix, newSuffix );
//recursive call
//rotate left to create a rearranged suffix
suffix = newSuffix + suffix.charAt(0);
}
}
}
Towers of Hanoi
 The goal of the Towers of Hanoi puzzle is to
move N disks from Peg 1 to Peg 3:
– Only one disk can
Start: be moved at a
time.
– A larger disk
Peg1 Peg2 Peg3
cannot be placed
on top of a
smaller disk.
Goal:

Peg1 Peg2 Peg3


Towers of Hanoi Solution

Peg1 Peg2 Peg3

Peg1 Peg2 Peg3


Peg1 Peg2 Peg3

Peg1 Peg2 Peg3


towersOfHanoi Method
public void towersOfHanoi(int N, //number of disks
int from, //origin peg
int to, //destination peg
int spare ){//"middle" peg

Test if ( N == 1 ) {

End case moveOne( from, to );

} else {
towersOfHanoi( N-1, from, spare, to );
Recursive case moveOne( from, to );
towersOfHanoi( N-1, spare, to, from );
}
}

private void moveOne( int from, int to ) {


System.out.println( from + " ---> " + to );
}
Searching
 When we maintain a collection of data, one of the
operations we need is a search routine to locate desired
data quickly.
 Here’s the problem statement:
Given a value X, return the index of X in the array, if such X exists.
Otherwise, return NOT_FOUND (-1). We assume there are no
duplicate entries in the array.
 We will count the number of comparisons the algorithms
make to analyze their performance.
 The ideal searching algorithm will make the least possible number
of comparisons to locate the desired data.
 Two separate performance analyses are normally done, one for
successful search and another for unsuccessful search.
Search Result
Unsuccessful Search: search( 45 ) NOT_FOUND

Successful Search: search( 12 ) 4

number
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
Linear Search
 Search the array from the first to the last
position in linear progression.
public int linearSearch ( int[] number, int searchValue ) {
int loc = 0;
while (loc < number.length && number[loc] != searchValue) {
loc++;
}
if (loc == number.length) { //Not found
return NOT_FOUND;
} else {
return loc; //Found, return the position
}
}
Linear Search Performance
 We analyze the successful and
unsuccessful searches separately.
 We count how many times the search value
is compared against the array elements.
 Successful Search
 Best Case – 1 comparison
 Worst Case – N comparisons (N – array size)

 Unsuccessful Search
 Best Case =
Worst Case – N comparisons
Binary Search
number

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

 If the array is sorted, then we can apply the


binary search technique.
 The basic idea is straightforward. First search the
value in the middle position. If X is less than this
value, then search the middle of the left half next.
If X is greater than this value, then search the
middle of the right half next. Continue in this
manner.
Sequence of Successful Search - 1
low high mid search( 44 )
#1 0 8 4
 low + high 
mid =  
 2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

low mid high

38 < 44 low = mid+1 = 5


Sequence of Successful Search - 2
low high mid search( 44 )
#1 0 8 4
#2 5 8 6  low + high 
mid =  
 2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

low mid high


high = mid-1=5 44 < 77
Sequence of Successful Search - 3
low high mid search( 44 )
#1 0 8 4
#2 5 8 6  low + high 
mid =  
#3 5 5 5  2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

Successful Search!! low high


44 == 44 mid
Sequence of Unsuccessful Search - 1
low high mid search( 45 )
#1 0 8 4
 low + high 
mid =  
 2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

low mid high

38 < 45 low = mid+1 = 5


Sequence of Unsuccessful Search - 2
low high mid search( 45 )
#1 0 8 4
#2 5 8 6  low + high 
mid =  
 2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

low mid high


high = mid-1=5 45 < 77
Sequence of Unsuccessful Search - 3
low high mid search( 45 )
#1 0 8 4
#2 5 8 6  low + high 
mid =  
#3 5 5 5  2

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

low high 44 < 45


mid low = mid+1 = 6
Sequence of Unsuccessful Search - 4
low high mid search( 45 )
#1 0 8 4
#2 5 8 6  low + high 
mid =  
#3 5 5 5  2
#4 6 5

0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90

Unsuccessful Search high low


no more elements to search low > high
Binary Search Routine
public int binarySearch (int[] number, int searchValue) {

int low = 0,
high = number.length - 1,
mid = (low + high) / 2;
while (low <= high && number[mid] != searchValue) {
if (number[mid] < searchValue) {
low = mid + 1;
} else { //number[mid] > searchValue
high = mid - 1;
}
mid = (low + high) / 2; //integer division will truncate
}
if (low > high) {
mid = NOT_FOUND;
}
return mid;
}
Binary Search Performance
 Successful Search
 Best Case – 1 comparison
 Worst Case – log2N comparisons
 Unsuccessful Search
 Best Case =
Worst Case – log2N comparisons
 Since the portion of an array to search is cut into
half after every comparison, we compute how
many times the array can be divided into halves.
 After K comparisons, there will be N/2K elements in
the list. We solve for K when N/2K = 1, deriving K =
log2N.
Comparing N and log2N Performance

Array Size Linear – N Binary – log2N


10 10 4
50 50 6
100 100 7
500 500 9
1000 1000 10
2000 2000 11
3000 3000 12
4000 4000 12
5000 5000 13
6000 6000 13
7000 7000 13
8000 8000 13
9000 9000 14
10000 10000 14
Sorting
 When we maintain a collection of data, many applications
call for rearranging the data in certain order, e.g. arranging
Person information in ascending order of age.
 Here’s the problem statement:

Given an array of N integer values, arrange the values into


ascending order.

 We will count the number of comparisons the algorithms


make to analyze their performance.
 The ideal sorting algorithm will make the least possible number of
comparisons to arrange data in a designated order.
 We will compare different sorting algorithms by analyzing
their worst-case performance.
Selection Sort
first min
1. Find the smallest
element in the list. 0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
1. Exchange the element
in the first position and
the smallest element. exchange
Now the smallest
element is in the first
position.
1. Repeat Step 1 and 2 0 1 2 3 4 5 6 7 8
with the list having one 5 17 23 90 12 44 38 84 77
less element (i.e., the
smallest element is
discarded from further sorted unsorted
processing).
This is the result of one pass.
Selection Sort Passes
Pass # 0 1 2 3 4 5 6 7 8

1 5 17 23 90 12 44 38 84 77
Result AFTER one
pass is completed.
sorted

2 5 12 23 90 17 44 38 84 77

3 5 12 17 90 23 44 38 84 77

7 5 12 17 23 38 44 77 84 90

8 5 12 17 23 38 44 77 84 90
Selection Sort Routine
public void selectionSort( int[] number )
{
int startIndex, minIndex, length, temp;
length = number.length;

for (startIndex = 0; startIndex <= length-2; startIndex++){


//each iteration of the for loop is one pass
minIndex = startIndex;

//find the smallest in this pass at position minIndex


for (i = startIndex+1; i <= length-1; i++) {
if (number[i] < number[minIndex]) minIndex = i;
}
//exchange number[startIndex] and number[minIndex]
temp = number[startIndex];
number[startIndex] = number[minIndex];
number[minIndex] = temp;
}
}
Selection Sort Performance
 We derive the total number
of comparisons by counting
the number of times the
inner loop is executed.
 For each execution of the
outer loop, the inner loop is
executed length – start
times.
 The variable length is the
size of the array. Replacing
length with N, the array
size, the sum is derived as…
Bubble Sort
 With the selection sort, we make one exchange
at the end of one pass.
 The bubble sort improves the performance by
making more than one exchange during its pass.
 By making multiple exchanges, we will be able to
move more elements toward their correct
positions using the same number of comparisons
as the selection sort makes.
 The key idea of the bubble sort is to make
pairwise comparisons and exchange the
positions of the pair if they are out of order.
One Pass of Bubble Sort
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77 17 5 23 12 44 90 38 84 77

exchange exchange

17 23 5 90 12 44 38 84 77 17 5 23 12 44 38 90 84 77

exchange exchange

17 5 23 90 12 44 38 84 77
17 5 23 12 44 38 84 90 77
exchange
ok exchange

17 5 23 12 90 44 38 84 77 17 5 23 12 44 38 84 77 90

exchange
The largest value 90 is at the end of
the list.
Bubble Sort Routine
public void bubbleSort(int[] number) {

int temp, bottom, i;


boolean exchanged = true;

bottom = number.length - 2;

while (exchanged) {
exchanged = false;

for (i = 0; i <= bottom; i++) {


if (number[i] > number[i+1]) {
temp = number[i]; //exchange
number[i] = number[i+1];
number[i+1] = temp;

exchanged = true; //exchange is made


}
}
bottom--;
}
}
Bubble Sort Performance
 In the worst case, the outer while loop
is executed N-1 times for carrying out
N-1 passes.
 For each execution of the outer loop,
the inner loop is executed bottom+1
times. The number of comparisons in
each successive pass is N-1, N-2, … ,
1. Summing these will result in the
total number of comparisons.
• So the performances of the bubble
sort and the selection sort are
approximately equivalent. However, on
the average, the bubble sort performs
much better than the selection sort
because it can finish the sorting
without doing all N-1 passes.
Quicksort
 To sort an array from index low to high, we
first select a pivot element p.
 Any element may be used for the pivot, but for
this example we will user number[low].
 Move all elements less than the pivot to the
first half of an array and all elements larger
than the pivot to the second half. Put the
pivot in the middle.
 Recursively apply quicksort on the two
halves.
Quicksort Partition

low high

p ...

Any element can be used partition


as a pivot. For simplicity, we
use number[low] as pivot. number[i]<p number[i]>p

Quicksort mid Quicksort


Method quicksort

public void quickSort( int[] number,


int low, int high ) {

if ( low < high ) {

int mid = partition( number, low, high );

quickSort( number, low, mid-1 );


quickSort( number, mid+1, high );
}
}
Quicksort Performance
 In the worst case, quicksort executes roughly the same
number of comparisons as the selection sort and bubble
sort.
 On average, we can expect a partition process to split the
array into two roughly equal subarrays.
When Not to Use Recursion
 When recursive algorithms are designed
carelessly, it can lead to very inefficient and
unacceptable solutions.
 For example, consider the following:

public int fibonacci( int N ) {

if (N == 0 || N == 1) {
return 1;

} else {
return fibonacci(N-1) + fibonacci(N-2);
}
}
Excessive Repetition
 Recursive Fibonacci ends up repeating the
same computation numerous times.
fibonacci(5)

fibonacci(4)+fibonacci(3)

fibonacci(2)+fibonacci(1)

fibonacci(1)+fibonacci(0)

fibonacci(3)+fibonacci(2)

fibonacci(1)+fibonacci(0)

fibonacci(2)+fibonacci(1)

fibonacci(1)+fibonacci(0)
Non-recursive Fibonacci
public int fibonacci( int N ) {

int fibN, fibN1, fibN2, cnt;

if (N == 0 || N == 1 ) {
return 1;

} else {

fibN1 = fibN2 = 1;
cnt = 2;
while ( cnt <= N ) {
fibN = fibN1 + fibN2; //get the next fib no.
fibN1 = fibN2;
fibN2 = fibN;
cnt ++;
}
return fibN;
}
}
When Not to Use Recursion

 In general, use recursion if


 A recursive solution is natural and easy to understand.
 A recursive solution does not result in excessive
duplicate computation.
 The equivalent iterative solution is too complex.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy