0% found this document useful (0 votes)
50 views

08a DivideAndConquer

The document discusses divide and conquer algorithms. It provides examples of divide and conquer problems including binary search, calculating Fibonacci numbers, and factorial calculations. It also discusses recursive binary search and mergesort algorithms. Mergesort works by recursively dividing an array in half and then merging the sorted halves.

Uploaded by

frankjamison
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
50 views

08a DivideAndConquer

The document discusses divide and conquer algorithms. It provides examples of divide and conquer problems including binary search, calculating Fibonacci numbers, and factorial calculations. It also discusses recursive binary search and mergesort algorithms. Mergesort works by recursively dividing an array in half and then merging the sorted halves.

Uploaded by

frankjamison
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 27

CHAPTER 5 Divide and Conquer

Introduction: Divide and Conquer Algorithms


The divide and conquer approach divides a problem into several smaller sub problems.

These sub problems are solved by some means.


The solutions of these sub problems are then combined to become a solution of the original problem. Very often each sub problem is also solved by the divide and conquer approach. Well-known divide and conquer algorithms include binary search, merge sort, and recurrence relationships

Ex: The Fibonacci numbers

Fib (5)

Fib (4)

Fib (3)

Fib (3)

Fib (2)

Fib (2)

Fib (1)

Fib (2)

Fib (1)

Ex: The example of factorial l

Ex: The example of

xy

Factorial (5)

35
3 *

Factorial (4)

34 33 32 31

Factorial (3)

Factorial (2)

Factorial (1)

Binary Search on 16 sorted elements

Search A[0] to A[7]

Search A[8] to A[15]

Search A[0] to A[3]

Search A[4] to A[7]

Search A[8] to A[11]

Search A[12] to A[15]

Search A[0] to A[1] Search A[2] to A[3]

Search A[4] to A[5] Search A[6] to A[7]

Search A[8] to A[9] Search A[10] to A[11]

Search A[12] to A[13] Search A[14] to A[5]

T(n) = 5 T(n) = 18 T( n/2 ) T(n) = 20+T( n/2 )

for n = 0 for n>0 and n is even for n> 0 and n is odd

Suppose n = 2k for some k>0. Obviously 2k is an even number, we get

T(2k) = 18 + T(2k-1) = 18 + (18 + T(2k-2)) = 18 + 18 + (18 + T(2k-3)) = .. = .. = 18k + T(2k-k) = 18k + T(20) = 18k + T(1)

Since T(1) is add number, the running time of T(1) is: T(1) = 20 + T(0) = 20 + 5 = 25 Therefore, T(2k) = 18k + 25

If n = 2k, then log n = log2k indicating that k = log n


Therefore,

T(n) = 18log n + 25

T(2k) = 18 + T(2k-1) T(2k) = 18 + (18 + T(2k-2) T(2k) = 18 + 18 + 18 + T(2k-3)

T(2k) = 18 + 18 + 18 + 18 + T(2k-4)

T(2k) = 18 + 18 + 18 + 18 + T(2k-4) + .+ 18 + T(2k-k) T(2k) = 18n + T(20) = = 18n + T(1) = 18k + 25 18logn + 25

Recursive Binary Search


- A little while ago, we saw how to search through a sorted array for a key using binary search - assume array sorted in increasing order

- This is an algorithm which is naturally stated recursively - First, recall the iterative solution

int binsearch( const vector< int>& A, int key)


{
int mid; // the middle point of the array int first= 0; // start of section in array int last= A.size()- 1; // end of section bool found = false; // assume not found to start while (first <= last && !found) { mid = (first+ last)/ 2; // roughly the middle if (A[ mid] == key) // found it - stop the loop found = true; else if (A[ mid] < key) // key is in the top half first = mid+ 1; else last = mid -1; // key must be in bottom half } // end while loop if (found) return mid; else return -1; // indicating that the key was not found }

Recursive Binary Search


- What's really happening in here? bisect the current range first... last at mid if A[mid] == key we are done if A[ mid] < key then key is in upper half of region first... last - continue by searching upper half mid+1... last if key < A[mid] then key is in lower half of array if it is in array at all - continue by searching lower half first...mid- 1

Recursive Binary Search - This is a recursive description


algorithm defined in terms of itself - Base Case: - if first > last we are finished - key is not found - otherwise, compute mid and compare A[mid] to key

- recursively search lower or upper half accordingly - return result of search

Recursive Binary Search


int binsearch (const vector< int>& A, int key int first, int last) { if (first> last) return -1; // key not found (null vector) else { int mid = (first+ last)/ 2; // define mid- point if (A[mid] == key) return mid; // found it if (A[mid] < key) // search upper half of region return binsearch( A, key, mid+1, last); else // search lower half of region return binsearch( A, key, first, mid-1); } }

Recursive Binary Search


- Consider the sorted array index: 0 1 2 3 4 5 6 7 value: 2 3 5 7 11 13 14 17
Start by calling binsearch( A, 11, 0, 7) - mid = 3 and A[3] < 11 // key in A[ 4]... A[ 7] call binsearch( A, 11,4,7) - mid = 5 and A[5] > 11 // key in A[ 4].. A[ 4] call binsearch( A, 11, 4, 4) - mid = 4 and A[4]==11 - Found it

Mergesort
- Sometimes a recursive solution is in fact the fastest solution - An important example of this is mergesort one of the fastest known "comparison based" sorting algorithms also extremely fast in practice

Mergesort
- Main idea break the array into two unsorted arrays of equal size sort each side recursively

merge the two sorted halves into a single sorted array

Mergesort
- For example, on array A = { 5, 9, 3, 13, 2, 6, 4, 1, 3, 7 } left = { 5, 9, 3, 13, 2 } right = { 6, 4, 1, 3, 7 } Recursively sort left to {2, 3, 5, 9, 13} Recursively sort right to {1, 3, 4, 6, 7} Merge the two sorted arrays into a single sorted array: { 1, 2, 3, 3, 4, 5, 6, 7, 9, 13 }

Mergesort: merging
- Aside from the recursion, the only operation which needs to be accomplished is to merge two sorted lists

- Idea: maintain a "pointer" into each sorted array at each step, append the smallest element pointed to onto the final array

Mergesort: merging
Step 1:

sort_ left = { 2 , 3, 5, 9, 13 } sorted_ right = { 1 , 3, 4, 6, 7 } sorted_ array = {}

Step 2:

sort_ left = { 2 , 3, 5, 9, 13 } sorted_ right = { 1, 3 , 4, 6, 7 } sorted_ array = { 1 }

Step 3: sort_ left = { 2, 3 , 5, 9, 13 } sorted_ right = { 1, 3 , 4, 6, 7 } sorted_ array = { 1, 2 }

Step 4: sort_ left = { 2, 3, 5 , 9, 13 } sorted_ right = { 1, 3 , 4, 6, 7 } sorted_ array = { 1, 2, 3 }

Step 5: sort_ left = { 2, 3, 5 , 9, 13 } sorted_ right = { 1, 3, 4 , 6, 7 } sorted_ array = { 1, 2, 3, 3 }

Step 6: sort_ left = { 2, 3, 5 , 9, 13 } sorted_ right = { 1, 3, 4, 6 , 7 } sorted_ array = { 1, 2, 3, 3, 4 }

Step 7: sort_ left = { 2, 3, 5, 9 , 13 } sorted_ right = { 1, 3, 4, 6 , 7 } sorted_ array = { 1, 2, 3, 3, 4, 5 }

Step 8: sort_ left = { 2, 3, 5, 9 , 13 } sorted_ right = { 1, 3, 4, 6, 7 } sorted_ array = { 1, 2, 3, 3, 4, 5, 6 }

Step 9: sort_ left = { 2, 3, 5, 9 , 13 } sorted_ right = { 1, 3, 4, 6, 7 } sorted_ array = { 1, 2, 3, 3, 4, 5, 6, 7 }

Step 10:

sort_ left = { 2, 3, 5, 9, 13 } sorted_ right = { 1, 3, 4, 6, 7 } sorted_ array = { 1, 2, 3, 3, 4, 5, 6, 7, 9 }

Step 11: sort_ left = { 2, 3, 5, 9, 13 } sorted_ right = { 1, 3, 4, 6, 7 } sorted_ array = { 1, 2, 3, 3, 4, 5, 6, 7, 9, 13 }

Mergesort: merging
vector< int> merge( const vector< int>& L, const vector< int>& R) { int Lptr= 0, Rptr= 0; vector< int> A; while (Lptr< L.size() || Rptr< R.size()) { if ((Lptr< L.size()) && ((Rptr>= R.size() || L[Lptr]<= R[Rptr]))) { A.push_back( L[Lptr]); Lptr++; } else { A.push_back(R[Rptr]); Rptr++; } } return A; }

Mergesort
- The

recursive mergesort routine itself is very simple


vector<int> mergesort(const vector<int>& A) { vector< int> left, right; if (A.size() <= 1) return A; for (int i= 0; i< A.size()/ 2; i++) left.push_back(A[ i]); for (i=A.size()/ 2; i< A.size(); i++) right.push_back(A[ i]); return merge(mergesort( left), mergesort( right)); }

- Again, follow the recursion

- We split the array into two

- And each half into two recursively

- And so on - eventually it gets split into N lists of a single element each

- For each split at the end, a pair of elements is merged

- Then we merge the pairs, and then the quartets

- And so on until one merge of two large lists at the end

- This is a by far faster than some other sorts like bubble sort or insertion sort algorithm

You might also like