简体   繁体   中英

How can I write QuickSort with event-driven comparisons?

I'm writing a web app that asks the user a series of questions which are just subjective comparisons of two values. They pick the one that is greater, and it poses the next comparison required for the sort. The goal is to sort the 58 items, and display the ordered list.

I want to use QuickSort, since 80% of the time it will require fewer comparisons than merge sort, which requires 342 comparisons. (I wrote a program that ran 5,000,000 simulations of sorting 58-item arrays, and found that 342 was the 80th percentile for the number of comparisons required by QS.) Since this is being done in JavaScript, I need the algorithm to wait for an event each time a comparison is required, and then, when the event is triggered, continue the algorithm. I'm having a hard time wrapping my mind around what this would have to look like.

I'm pretty sure in order to implement QS this way--unless there's some clever method with JavaScript closures I'm not thinking of--I can't do it recursively. I found an iterative implementation of QS , and am trying to figure out how I can split it up so that an event trigger will continue the algorithm from where it left off.

I would post what code I have, but honestly, I think given how jumbled and disjointed it is, it would do more harm than good. I'd love a full implementation in JavaScript, but even pseudo-code would be helpful.

I figured it out using JavaScript Promises and recursion. It's a pretty clean solution, I think.

class QS {
    constructor(array) {
        this._array = array;
        this._comparisons = 0;
    }

    run() {
        new Promise(r => {
            return this.runQS(0, this._array.length - 1, r);
        }).then(() => {
            console.log(this._array);
            console.log(this._comparisons);
        });
    }

    runQS(low, high, resolve, part) {
        if (low < high) {
            return new Promise(r => {
                this.partition(low, high, r);
            }).then(p => {
                return new Promise(r => {
                    this.runQS(low, p - 1, r, p);
                });
            }).then(p => {
                return new Promise(r => {
                    this.runQS(p + 1, high, r, p);
                });
            }).then(() => {
                resolve(part);
            });
        } else {
            resolve(part);
        }
    }

    partition(low, high, res) {
        let self = this,
            i = low - 1,
            j = low,
            partProm = Promise.resolve();

        function inner(i, j, doSwap) {
            if (doSwap) {
                i++;
                self.swap(i, j);
            }

            j++;
            if (j < high) {
                addThen(i, j);
            } else {
                i++;
                self.swap(i, high);
                res(i);
            }
        }

        function addThen(i, j) {
            partProm = partProm.then(() => {
                self._comparisons++;
                return new Promise(r => {
                    promptUser(self._array, j, high, r);
                });
            }).then(s => {
                inner(i, j, s);
            });
        }

        if (low < high) {
            addThen(i, j);
        }
    }

    swap(i, j) {
        let temp = this._array[i];
        this._array[i] = this._array[j];
        this._array[j] = temp;
    }
}

My placeholder for promptUser() is just:

function promptUser(a, i, j, resolve) {
    setTimeout(() => {
        console.log('Comparing ' + a[i] + ' against ' + a[j]);
        resolve(a[i] < a[j]);
    }, 10);
}

It's not too difficult to pass resolve into a function that's triggered by a button's onclick .

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