简体   繁体   中英

How can I implement the function “divide” in a functional Divide-And-Conquer Java algorithm?

I am trying to implement a functional version of the Quicksort algorithm. My professor asked me to keep this as the signature:

public static <T, R> List<T> myQuickSort(Function<List<T>, Boolean> trivial, 
        Function<List<T>, T> solve, Function<List<T>, R> divide, 
        Function<T, List<R>> combine, List<T> input)

I created an auxiliary class named Pair, which goes like this:

public class Pair {

List<Integer> first; 
List<Integer> second; 

Pair(List<Integer> f, List<Integer> s) {
    first = f; 
    second = s; 
}
public static Pair div(List<Integer> input) {
    int pivot = (int) input.get(0); 
    List<Integer> a = new ArrayList<Integer>();
    List<Integer> b = new ArrayList<Integer>();
    for(int i=1; i<input.size(); i++) {
        if(input.get(i) < pivot) {
            a.add(input.get(i)); 
        } else {
            b.add(input.get(i)); 
        }
    }
    return new Pair(a, b); 
}

} I am almost done, but I cannot figure out how to work recursively on a single partition of my input array. I tried to act like this:

if(trivial.apply(input)) {
        solve.apply(input); 
    } else {
        output = myQuickSort(trivial, solve, divide, combine, 
                (List<T>) divide.apply(input).first); 
        output.add(input.get(0)); 
        output.addAll(myQuickSort(trivial, solve, divide, combine, 
                (List<T>) divide.apply(input).second)); 
        return output; 
    }
    return output;

But I am now stuck. Can anyone of you please tell where I am wrong and/or how can I implement my solution better? Here is also the main, if it can help:

Function<List<Integer>, Boolean> trivial = (a) -> a.size()==1; 
    Function<List<Integer>, Pair> divide = (a) -> Pair.div(input); 
    Function<Pair, List<Integer>> combine = 
            (a) -> Stream.concat(a.first.stream(), a.second.stream()).
            collect(Collectors.toList());
    Function<List<Integer>, Integer> solve = (a) -> a.get(0); 
    ArrayList<Integer> output = myQuickSort(trivial, solve, divide, combine, input);

Brings up lots of questions. So all I can do is chip away at the problem.

  1. I assume in your myQuickSort signature that the type of combine has the R and T swapped. If I'm right, I find this signature strange as I explain below.

  2. The "solve.apply()" line in your code does not do anything with the return. Since you don't use the return of the call to that function, I can't determine what the purpose of "solve" is. Perhaps you can clarify. As a result, your last "return output" does not return anything. In my discussion below, I ignore "solve".

  3. Your implementation of "combine" in main uses streams unnecessarily. List.addAll() would do the trick.

  4. You don't use the combine function in your implementation.

My questions concern the signature that your professor gave you. It seems to me that R is an object that represents a list divided into two. R would include the first and second list, and also the "pivot" as you call it. Combine then takes an R and combines the three parts into a single list. Since the implementations of R, divide, and combine are done by the caller of myQuickSort, they would handle the comparisons of the objects being sorted (Integers in your implementation), so that myQuickSort does not have to know about the type of the objects being sorted.

If I'm right, then it seems to me that your implementation of R, which you call Pair, should store the pivot. Your implementation of combine would then combine first, pivot, and second. Either divide or combine would call myQuickSort: doesn't matter which. You have divide doing it, which is fine.

myQuickSort then would then be:

if (trivial(input))
    return input;    // Remember I am ignoring solve()
else {
    R r = divide.apply(input);
    return combine.apply(r);
}

So then the implementation of either divide or combine--divide in your case--provides the recursion by calling myQuickSort for each of the two halves of the divided list.

If I'm right on the above, this is a strange signature. The provided functions have to have knowledge of the QuickSort implementation. This would not be the case if R was defined as having methods from which myQuickSort could manipulate the divided arrays. This would mean that, rather than simply using R in the static method signature, it would use "R extends DivObject" where DivObject is an interface (or class) that defines the methods that myQuickSort can call. I would have DivObject have a getFirst() method and a getSecond() method, which return the corresponding lists so that myQuickSort can call itself on those two lists. It would also need methods, like setFirst() and setSecond(), to put the sorted lists back into R so that combine can combine them (since combine only takes R as a parameter).

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM