Реализация Heapsort делает слишком много сравнений

Я только что реализовал алгоритм heapsort. Я стремлюсь к как можно меньшему количеству сравнений. Моя реализация работает, но делает в два раза больше сравнений, чем этот образец, который я нашел в Интернете: http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/

Тестовый массив: 6 2 3 1 5 4 2 5 1 6 7 4 3 2 6 7 3 2 5 6 1 8 7 5 4 3 2 1 5

Образец, который я нашел в Интернете, сделал 194 сравнения для сортировки этого массива, моя реализация сделала 570 сравнений.

Вот код:

$(document).ready(function(){

        init();

        });

    var comparisons = 0;

    var init = function()
    {    
        var dataSource = getTestData();

        var root = {value: dataSource[0]};

        //Building heap
        for(var i = 1; i < dataSource.length; i++)
            addChild(root, {value: dataSource[i]});

        console.log("--- Source: ---")
        console.log(JSON.stringify(dataSource));

        console.log("--- Input: ---")
        console.log(JSON.stringify(dataSource));

        console.log("--- Tree: ---")
        console.log(JSON.stringify(root, null, 4));
        console.log("Nodes: " + countChildren(root));

        var sortedArray = new Array();
        comparisons = 0;

        //Do sorting here
        while(countChildren(root) != 0){
            sortNode(root); //Sorting heap
            sortedArray.unshift(root.value); //Adding the biggest entry from the top of the heap to the output
            deleteNode(root); //Removing the top of the heap
        }

        console.log("--- Sorted Tree: ---")
        console.log(JSON.stringify(root, null, 4));
        console.log("Nodes: " + countChildren(root));

        console.log("--- Sorted: ---")
        console.log(JSON.stringify(sortedArray));

        console.log("!!! Comparisons: " + comparisons + " !!!");
    }

    var deleteNode = function(item)
    {
        if (item.left == null && item.right == null)
            item.value = null;
        else if (item.left != null) {
            item.value = item.left.value;

            if (countChildren(item.left) == 1)
                item.left = null;
            else
                deleteNode(item.left)
        }
        else if (item.right != null) {
            item.value = item.right.value;

            if (countChildren(item.right) == 1)
                item.right = null;
            else
                deleteNode(item.right)
        }
    }

    var sortNode = function(item)
    {
        if (item.left != null && item.right == null){
            sortNode(item.left);

            comparisons++;
            if (item.value < item.left.value) //Comparison
            {
                var tmp = item.value;
                item.value = item.left.value;
                item.left.value = tmp;
            }
        }else if (item.right != null && item.left == null){
            sortNode(item.right);

            comparisons++;
            if (item.value < item.right.value) //Comparison
            {
                var tmp = item.value;
                item.value = item.right.value;
                item.right.value = tmp;
            }
        }
        else if (item.right != null && item.left != null) {
            sortNode(item.right);
            sortNode(item.left);

            //Some more comparisons
            var left_bigger_right = (item.right.value <= item.left.value);        comparisons++;
            var left_bigger_this = (item.value < item.left.value);                comparisons++;
            var right_bigger_this = (item.value < item.right.value);              comparisons++;

            if ((left_bigger_this && left_bigger_right) || (right_bigger_this && left_bigger_right)) {
                var tmp = item.value;
                item.value = item.left.value;
                item.left.value = tmp;
            }
            else if (right_bigger_this && left_bigger_right == false){
                var tmp = item.value;
                item.value = item.right.value;
                item.right.value = tmp;
            }
        }
        else{ //No children
            //Nothing to do :)
        }
    }

    var addChild = function(item, toAdd)
    {
        if(item.left == null)
            item.left = toAdd;
        else if (item.right == null)
            item.right = toAdd;
        else{ //if(item.left != null && item.right != null)
            childrenCountLeft = countChildren(item.left);
            childrenCountRight = countChildren(item.right);

            if (childrenCountRight < childrenCountLeft)
                addChild(item.right, toAdd);
            else if (childrenCountLeft < childrenCountRight)
                addChild(item.left, toAdd);
            else //if (childrenCountLeft == childrenCountRight)
                addChild(item.left, toAdd);
        }
    }

    var countChildren = function(item){
        var children = 0;

        if (item.value != null)
            children += 1;

        if (item.left != null)
            children += countChildren(item.left);
        if (item.right != null)
            children += countChildren(item.right);

        return children;
    }

    var getTestData = function(){
        return new Array(6 ,2 ,3 ,1 ,5 ,4 ,2 ,5 ,1 ,6 ,7 ,4 ,3 ,2 ,6 ,7 ,3 ,2 ,5 ,6 ,1 ,8 ,7 ,5 ,4 ,3 ,2 ,1 ,5);
    }

Мой вопрос: в какой части мне не удалось реализовать алгоритм пирамидальной сортировки? Где ненужные сравнения?

Они должны быть в функции sortNode. Все сравнения я отметил комментариями.

Я не вижу, как я могу улучшить эту функцию, чтобы делать меньше сравнений. Так что это будет мой вопрос.


person Moritz Göckel    schedule 30.03.2015    source источник
comment
Часть, где вы делаете 3 сравнения, помеченные //Еще несколько сравнений, но затем потенциально используете только два из них, выглядит очевидным улучшением.   -  person Paul Hankin    schedule 30.03.2015
comment
Вы можете сравнить реализацию своего алгоритма с реализацией, размещенной на Rosetta Code, чтобы получить подсказки.   -  person    schedule 30.03.2015
comment
Не могли бы вы вкратце объяснить, что должна делать ваша функция sortNode? Я еще не сталкивался с такой вещью в сортировке кучи, но, возможно, она просто неправильно названа.   -  person Bergi    schedule 30.03.2015


Ответы (1)


То, что вы реализовали, не похоже на сортировку кучи (точнее, используемая вами структура данных не является правильной двоичной кучей).

Функции sortNode всегда делают рекурсивный вызов для обоих своих дочерних элементов, что приводит к постоянному обходу всей кучи. Это определенно не так, как должна работать куча. Таким образом, все дополнительные сравнения происходят из-за неправильного алгоритма (ваша реализация делает O(n ^ 2) сравнений, а правильная должна делать только O(n log n)).

Как исправить? Как я сказал выше, структура данных, которую вы знаете, не похожа на кучу, поэтому я бы рекомендовал правильно реализовать кучу с нуля (вы можете прочитать википедию статья, чтобы убедиться, что вы правильно ее поняли).

person kraskevich    schedule 30.03.2015