37
Laboratorio di Algoritmi e Strutture Dati Guido Fiorino [email protected] aa 2013-2014

Laboratorio di Algoritmi e Strutture Dati - DISCo · Write a Java method seqSearch that implements the divide-and-conquer approach to search a value in an unsorted ... Laboratorio

  • Upload
    lydung

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

Laboratorio di Algoritmi e Strutture Dati

Guido [email protected]

aa 2013-2014

Maximum in a sequence

Given a sequence 〈A1, . . . ,AN〉 find the maximum.The maximum is in the first or in the second half of the sequence. If thesequence has one element we immediately have the answer.We can proceed recursively following a divide-and-conquer approach:

Base case: if N = 1, then A1 is the maximum;

Recursion:1 recursively compute the maximum of the sequence 〈A1, . . . ,A N

2〉;

2 recursively compute the maximum of the sequence 〈A N2

+1, . . . ,AN〉;3 the maximum of 〈A1, . . . ,AN〉 is the maximum between the two values.

Exercise (method maximum)

write a method int maximum(int[] a, int l, int r) that finds themaximum in the array a spanning from l to r.

2

Average of a sequence

Given a sequence 〈A1, . . . ,AN〉 find the average.If we know the average of 〈A1, . . . ,A N

2〉 and 〈A N

2 +1, . . . ,AN〉 we can compute

the average of the whole sequence by appropriately combining the two values. Ifthe sequence has one element, then we immediately have the answer.We can proceed recursively following a divide-and-conquer approach:

Base case: if N = 1, then A1 is the average of the sequence;

Recursion:1 recursively compute the average of the sequence 〈A1, . . . ,A N

2〉, let Av1 be

the value;2 recursively compute the maximum of the sequence 〈A N

2+1, . . . ,AN〉, let Av2

be the value;

3 the average of 〈A1, . . . ,AN〉 is theAv1∗ N

2+Av2∗(N− N

2)

N

Exercise (method avg)

Write a method double avg(int[] a, int l, int r) that finds theaverage in the array a spanning from l to r.

3

Binary search

Binary search is the alternative to the sequential search applicable to a sortedsequence 〈A1, . . . ,AN〉, that is a sequence fulfilling the property: for everyi = 1, . . . ,N − 1, Ai ≤ Ai+1 holds.Binary search can be described as a divide-and-conquer algorithm.Given a sorted sequence 〈A1, . . . ,AN〉 and X , proceed recursively as follows:

Base case: if N = 0 (the given sequence is empty), then X is not in thesequence;

Recursion:1 if A N

2= X , then X is in the sequence;

2 if A N2> X , then the answer is the result of recursively searching X in the

sequence < A1, . . . ,A N2−1〉, else the answer is the result of recursively

searching X in the sequence < A N2

+1, . . . ,AN〉.

4

Binary search

Exercise (method binSearch0)

Write a Java method boolean binSearch0(int[] a, int X) that returnstrue if X occurs in the array a, false otherwise.

5

Binary search

public static boolean binSearch0(int[] a, int X){

//empty sequence

if (a.length == 0) return false;

//X is found

if (a[a.length/2] == X) return true;

//search for X in the first middle

if (a[a.length/2] > X){

int[] b = new int[a.length/2];

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

b[i]=a[i];

return binSearch0(b,X);

}

else { //search for X in second middle

int[] b = new int[a.length-a.length/2-1];

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

b[i]=a[i + a.length/2 + 1];

return binSearch0(b,X);

}

}

6

Binary search

Exercise (method binSearch)

Write a Java method

boolean binSearch(int[] a, int X, int left, int right)

that returns true if X occurs in the array a spanning from left to right ,false otherwise.

7

Binary search

//searches for X between a[left] to a[right]

boolean binSearch(int[] a, int X, int left, int right){

//empty sequence

if (left > right) return false;

int middle = (left+right)/2;

//X is found

if (a[ middle ] == X) return true;

//search for X in the first middle

if (a[ middle ] > X)

return binSearch(a, X, left, middle-1);

else

return binSearch(a, X, middle+1, right);

}

8

Searching in an unsorted sequence

The searching in an unsorted sequence can be described by means of adivide-and-conquer approach.Given a sequence 〈A1, . . . ,AN〉 and X , proceed recursively as follows:

Base case: if N = 0 (the given sequence is empty), then X is not in thesequence;

Recursion:1 if A N

2= X , then X is in the sequence;

2 proceed recursively, searching X in < A1, . . . ,A N2−1〉. If X is found, then X

is in the given sequence;3 proceed recursively, searching X in < A N

2+1, . . . ,AN〉. If X is found, then X

is in the given sequence.

Exercise (method seqSearch)

Write a Java method seqSearch that implements the divide-and-conquerapproach to search a value in an unsorted sequence. The method seqSearch

returns a boolean value. You are free to choose the formal parameters ofseqSearch.

9

Palindromes

A sequence 〈A1, . . . ,AN〉 that reads the same backward as forward ispalindrome.This definition can be stated recursively as follows:

a sequence 〈A1, . . . ,AN〉 is palindrome if A1 = AN and 〈A2, . . . ,AN−1〉 ispalindrome.

What about the base case? We have two base cases: the empty sequence andthe sequence with one element.

Definition (palindrome)

A sequence 〈A1, . . . ,AN〉 is palindrome iff

the sequence is empty (equivalently N = 0);

the sequence contains one element (equivalently n = 1);

A1 = An and the sequence 〈A2, . . . ,AN−1〉 is palindrome.

10

Palindromes

Exercise (method isPalindrome)

Write a method boolean isPalindrome(char[] a, int l, int r) thatreturns true if the array a that spans between l and r is palindrome, falseotherwise.

11

Reverse of an array

The reverse of a sequence 〈A1, . . . ,AN〉, denoted as rev(〈A1, . . . ,AN〉), is thesequence 〈AN , . . . ,A1〉.The definition emphasizes that function rev returns a particular permutation ofa given sequence. Thus function rev maps sequences into sequences.Function rev has an easy recursive definition:

rev(〈A1, . . . ,AN〉) =

{〈A1〉 if N = 1〈 rev(〈A2, . . . ,An〉) ,A1 〉 otherwise

Exercise (method rev)

Write a method int[] rev(int[]a,int i, int n) that returns the reverseof the array a that spans from i to n.

12

Filling a matrix

Exercise (method fillMatrix)

Use the divide-and-conquer approach to write a Java method fillMatrix that fills ofbinary digits a matrix M of 2n rows and n columns, so that at M contains the binaryrepresentation of the first n integers. Example, with n = 3,

M =

0 0 00 0 10 1 00 1 11 0 01 0 11 1 01 1 1

You are free to decide the parameters and the returning value of fillMatrix.

Possible signatures are

int[][] fillMatrix(int c) that returns a matrix with 2c rows and c columns

void fillMatrix(int[][] M, int rmin, int rmax, int cmin, int cmax)

that fills M spanning between the rows rmin and rmax and the columns cmin andcmax.

13

Maximum Contiguous Subsequence Sum

Given a sequence containing (possibly negative) integers, 〈A1,A2, . . . ,AN〉, find

the maximum value of∑j

k=i Ak . The maximum contiguous subsequence sum iszero if all the integers are negative.The problem can be solved by a divide-and-conquer algorithm.We divide the input in two halves: 〈A1, . . . ,A N

2〉 and 〈A N

2 +1, . . . ,AN〉.The maximum contiguous subsequence sum can occur:

1 entirely in the first half;

2 entirely in the second half;

3 begins in the first half but ends in the second half. Note that in this casethe contiguous subsequence includes the last element of the first half andthe first element of the second half.

If we have the answers for each of the three possible cases we can provide theanswer for the sequence 〈A1, . . . ,AN〉 comparing them and returning thegreatest.

14

Maximum Contiguous Subsequence Sum

Given the sequence 〈A1, . . . ,AN〉, proceed recursively as follows:

Base case: if N = 1 choose the largest between zero and A1;

Recursion:1 recursively compute the maximum contiguous subsequence sum for

contiguous subsequences entirely residing in the fist half;2 recursively compute the maximum contiguous subsequence sum for

contiguous subsequences entirely residing in the second half;3 compute the maximum contiguous subsequence sum for contiguous

subsequences that begins in the first half and ends in the second half;4 the answer for A1, . . . ,AN is the maximum of the three values.

15

Maximum Contiguous Subsequence Sum

To solve Point 3:

1 for each element in the first half, calculate the sum of the contiguoussubsequence that ends at the rightmost item of the first half;

2 for each element in the second half, calculate the sum of the contiguoussubsequence that starts at the leftmost item of the second half.

The sum of maximum value found in each of previous points gives themaximum contiguous subsequence sum for a contiguous subsequence that spansbetween the two halves.

Exercise (method mcss)

Write a method int mcss(int[] a, int l, int r) that finds the maximumsum in the array a spanning from l to r by implementing thedivide-and-conquer approach given above.

16

MergeSort

Given a sequence 〈A1, . . . ,AN〉, proceed recursively following thedivide-and-conquer approach:

Base case: if N = 1, then, the sequence is sorted, return 〈A1〉;Recursion:

1 recursively sort 〈A1, . . . ,A N2〉;

2 recursively sort 〈A N2

+1, . . . ,AN〉;3 return the result of merging the two sorted subsequences obtained in the

previous steps.

The implementation of MergeSort depends on the implementation of Phase 3.

There are different techniques to perform the merge. The aim is to save time.

17

MergeSort - Bitonic mergeWe are given an array

A 2 4 6 0 3

we copy A in an extra array E with the elements of the second sequence reversed

E 2 4 6 3 0⇑ ⇑

Then we merge in A starting from the leftmost cell:

A 0

E 2 4 6 3 0⇑ ⇑

A 0 2

E 2 4 6 3 0⇑ ⇑

A 0 2 3

E 2 4 6 3 0⇑ ⇑

A 0 2 3 4

E 2 4 6 3 0⇑⇑

A 0 2 3 4 6

E 2 4 6 3 0⇑ ⇑

18

MergeSort - Implementation of the bitonic merge

//Bitonic merge. Ends with a[left..right] sorted

static void merge(int[] a, int left, int m, int right){

int numberOfCells = right-left+1, i, j;

int[] extra = new int[numberOfCells];

//copy a[left]..a[m] in extra[0]..extra[m-left]

//in a future version we would like to save this copy

for(i = left; i <= m ; i++){ extra[i-left] = a[i]; }

//copy a[right]..a[m+1] in extra[m+1-left]..extra[right-left]

//note the reverse order

for(j = right; j >= m+1; j--, i++) { extra[i-left] = a[j]; }

j = numberOfCells-1; //index of last cell of extra

i = 0; //index of first cell of extra

for(int k = 0; k < numberOfCells; k++){

if(extra[i]>extra[j]) {

a[k+left]=extra[j--];

}

else { a[k+left] = extra[i++]; }

}

} //end bitonicMerge

19

Bitonic merge - exercises

Exercise (Variants of the bitonic merge)

The code can be changed along this linespublic class MergeSort{

//better solution: use extra as member of the class

private static int[] extra;

public static void sort(int[] a){

extra = new int[a.length];

sort(a, 0, a.length-1);

}

//This method sorts a[left..right]

static private void sort(int[] a, int left, int right){

/* write the recursive code for the mergeSort */

}// end merge

// bitonic merge

static void merge(int[] a, int left, int m, int right){

/* write here the new code for the bitonic merge */

} //end bitonic merge

}//end class MergeSort

20

MergeSort - merge of two sources in a destination

public class MergeSort{private static int[] extra;/*merge s1[s1Left..s1Right] and s2[s2Left..s2Right] intodest starting from cell of index destLeft*/static void merge(int[] dest, int destLeft,

int[] s1, int s1Left, int s1Right,int[] s2, int s2Left, int s2Right

){

int q = (s1Right-s1Left+1) + (s2Right-s2Left+1); //data to copyint destRight = destLeft + q -1; //index of the last datafor(int i = destLeft; i <= destRight ; i++){

if (s1Left>s1Right){ // all data in s1 have been already copied in destdest[i] = s2[s2Left++];

}else if (s2Left>s2Right){ // all data in s2 have been already copied in dest

dest[i] = s1[s1Left++];}else if (s1[ s1Left ] < s2 [ s2Left ]){

dest[i] = s1[ s1Left++ ];}else dest[i] = s2[ s2Left++ ];

}} //end merge

Class continues in the next slide...

21

MergeSort - merge two sources in a destination

public static void sort(int[] a){

extra = new int[a.length];

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

{ extra[i] = a[i]; } //Important: copy a in extra. Why?

//sort the data in extra[0..a.length-1] and

//put the result in array a

sort(a, extra, 0, a.length-1);

//now array a is sorted

}

See next slide...

22

MergeSort - exchanging roles of source and destination

//sort data in source and put the result in dest

static private void sort(int[] dest, int[] source, int left, int right)

{

if (right - left > 0){ //the sequence has at least one element

int m=(left + right)/2;

//sort dest[left..m] and put the result in source[left..m]

//note the exchange between source and dest!

sort(source, dest, left, m);

//sort dest[m+1..right] and put the result in source[m+1..right];

sort(source, dest, m+1, right);

//merge source[left..m] and source[m+1..right] and put the result in

//dest, starting from left, in practice the result goes in dest[left..right]

merge(dest, left, source, left, m , source, m+1, right);

} //end if, note that the base case is implicit

}// end sort

}//end class

23

MergeSort - the copy of the source data in extra

Exercise (Question)

Can you explain why we have this code?

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

{ extra[i] = a[i]; } //Important: copy a in extra. Why?

Compare the previous version of MergeSort with the following one andremember that our aim is to save to copy data between extra and a as muchas possible.

24

MergeSort - saving the copy

public class MergeSort{

private static int[] extra;

/*

the role of the array a and the extra array as source of the data and

destination of the merging is continuously exchanged;

in this way we save the copy from a to extra that is performed

in the bitonic merge

*/

public static void sort(int[] a){

extra = new int[a.length];

/*

sort the data in extra[0..a.length-1] and put the result in a;

*/

sort(a, extra, 0, a.length-1);

}//end sort

Class MergeSort continues in the next slide

25

MergeSort - saving the copy

//sort data in source[left..right] and put the result in dest[left..right]

static void sort(int[] dest, int[] source, int left, int right)

{

if (right-left == 0){

/* Note that we have an explicit base case.

Compare with the previous version */

if (dest == extra) {

/*

dest == extra true means that source contains the data

and the assignment is required.

Note that this check is not present in the previous version.

*/

dest[right]=source[right];

}

return ;

} /* end base case */

Method static void sort(int[] dest, int[] source, int left, int right)

continues in the next slide

26

MergeSort - saving the copy

//recursive part

//the sequence has at least one element

int m=(left + right)/2;

//sort dest[left..m] and put the result in source[left..m]

sort(source, dest, left, m);

//sort dest[m+1..right] and put the result in source[m+1..right];

sort(source, dest, m+1, right);

//merge source[left..m] and source[m+1..right] and put the result in

//dest, starting from left, in practice the result goes in dest[left..right]

merge(dest, left, source, left, m , source, m+1, right);

}// end sort

static void merge(int[] dest, int destLeft,

int[] s1, int s1Left, int s1Right,

int[] s2, int s2Left, int s2Right

)

{

/* see the code given in a previous slide */

}

}// end class MergeSort

27

Listing all permutations of a sequence

There are N! permutations of a given a sequence 〈A1, . . . ,AN〉, where all theelements are distinct, that is for every Ai ,Aj of the sequence, Ai 6= Aj if i 6= j .We can describe the set of all permutations of 〈A1, . . . ,AN〉 by a recursivedefinition that follows a divide-and-conquer approach:

Base case: if N = 1, then 〈A1〉 is the only permutation;

Recursion:1 For i = 1, . . . ,N,

recursively compute the set Si of permutations of the sequence〈A1, . . . ,Ai−1,Ai+1, . . .AN〉;let Vi the set obtained by inserting Ai as first element of every sequence inSi , that is

Vi = {〈Ai ,B1, . . .BN−1〉 | 〈B1, . . .BN−1〉 ∈ Si};2 V = V1 ∪ · · · ∪ VN is the set of all permutations of 〈A1, . . . ,AN〉.

28

Listing all permutations of a sequence

Exercise

Use the description given in the previous page to write a Java method

int[][] listPerms(int[] seq)

returning a matrix containing the list of the permutations of seq. If you need,method listPerms can have more than one formal parameter.

29

Listing all permutations of a sequence

An alternative is:

Base case: if N = 1, then 〈A1〉 is the only permutation;

Recursion:1 recursively compute the set S of permutations of the sequence 〈A2, . . .AN〉;2 for every sequence 〈B1, . . . ,BN−1〉 in S , build

the N new subsequences

〈A1,B1, . . . ,BN−1〉,〈B1,A1,B2, . . . ,BN−1〉,...〈B1, . . . ,BN−1,A1〉.

This is the set V of the permutations of 〈A1, . . . ,AN〉, that is:

V = {〈B1, . . . ,Bj−1,A1,Bj , . . . ,BN−1〉 |〈B1, . . . ,Bj−1,Bj , . . . ,BN−1〉 ∈ S and for j = 1, . . . ,N − 1}

30

Listing all permutations of a sequence

Exercise

Use the description given in the previous page to write a Java method

int[][] listPermsTwo(int[] seq)

returning a matrix containing the list of the permutations of seq. If you need,method listPermsTwo can have more than one formal parameter.

31

Listing all permutations of a sequence

A further alternative to describe the listing of the permutations of a sequence〈A1, . . . ,AN〉 is by introducing a second sequence that we call the prefix.

The following description is parametrized on two sequences of elements that wecall Seq and Prefix .

To obtain the list of the permutations of 〈A1, . . . ,AN〉, we setSeq = 〈A1, . . . ,AN〉 and Prefix = 〈〉.

To make the description clearer, we emphasizes the formal parameters of theprocedure:

32

Listing all permutations of a sequence

ListPerm(Seq,Prefix)

Base case: if Seq contains one element, then the sequence obtained by theelements in Prefix followed by the element in Seq is a permutation〈A1, . . . ,AN〉 in the first call;

Recursion:1 For every element A in Seq

Let Seq′ be the result of erasing from Seq the element A;Let Prefix ′ = 〈A,B1, . . . ,Bu〉 where 〈B1, . . . ,Bu〉 is the sequence in Prefix ;ListPerm(Seq′,Prefix ′), in other words, proceed recursively on Seq′,Prefix ′.

In the following, the method listPerm provides a Java implementation of theprocedure.

33

Listing all permutations of a sequence

public static void listPerm(int[] a, int[] prefix) {

// base case

if (a.length == 1) {

System.out.print(a[0] + "; ");

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

System.out.print(prefix[i] + "; ");

}

System.out.println();

return;

}

The method listPerm(int[] a, int[] prefix) continues in the next slide...

34

Listing all permutations of a sequence

// recursive step

int[] seq1 = new int[a.length - 1]; //this is seq’

int[] prefix1 = new int[prefix.length + 1]; //this is prefix’

//build prefix’: copy prefix in prefix’

for(int i = 1; i < prefix1.length; i++) {

prefix1[i] = prefix[i-1];

}

for(int j = a.length - 1; j >= 0; j--) {

//complete prefix’: put an element of a as first element of prefix’

prefix1[0] = a[j];

//build seq’

for(int i = 0, k = 0; i < a.length; i++) {

if (i != j) {

seq1[k] = a[i];

k++;

}

}

perms(seq1, prefix1);

}

}//end listPerm

35

Listing all permutations of a sequence

Remark

Our implementation strictly follows the description. A better implementationcould be provided, possibly using some more formal parameters.

36

A collection of exercises on divide-and-conquer method

1 Use the technique divide-and-conquer to write a Java methodint count10(int[] a, int left, int right) that returns the frequency of thesequence 〈1, 0〉 in the array a spanning from left to right;

2 use the technique divide-and-conquer to write a Java methodint findBBA(char[] a, int left, int right) that returns the frequency of thestring ‘‘BBA’’ in the array a spanning from left to right;

3 use the technique divide-and-conquer, write a Java methodint countOOE(int[] a, int left, int right) that returns the number of times thatin the array a spanning from left to right an even number is immediately preceded bytwo odd numbers;

4 use the technique divide-and-conquer, write a Java methodint countTrueTrue(boolean[] a, int left, int right) that returns the frequencyof 〈true, true〉 in the array a spanning from left to right;

5 use the technique divide-and-conquer, write a Java methodboolean evenVowels(char[] a, int left, int right) that returns true if in thearray a spanning from left to right the number of vowels is even, false otherwise;

6 use the technique divide-and-conquer to write a Java methodint count1001(int[] a, int left, int right) that returns the frequency of thesequence 〈1, 0, 0, 1〉 in the array a spanning from left to right.

37