堆排序实现进行了太多比较

Heapsort implementation does too many comparisons

本文关键字:太多 比较 实现 堆排序      更新时间:2023-09-26

我刚刚实现了堆排序算法。我的目标是尽可能少地进行比较。我的实现是有效的,但所做的比较是我在网上找到的这个示例的两倍多:http://www.csse.monash.edu.au/~劳埃德/波浪AlgDS/排序/堆/

测试阵列为:6 2 3 1 5 2 5 1 6 7 4 3 6 7 3 5 6 8 7 5 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函数中。我用评论把所有的比较都做了标记。

我不知道如何改进这个功能,减少比较。所以这将是我的问题。

您所实现的看起来不像堆排序(更准确地说,您所使用的数据结构不是一个合适的二进制堆)。

sortNode函数总是对它的两个子函数进行递归调用,这导致始终遍历整个堆。这绝对不是堆的工作方式。因此,所有额外的比较都来自于不正确的算法(您的实现执行O(n ^ 2)比较,而正确的实现应该只执行O(n log n))。

如何修复?正如我在上面所说的,你所知道的数据结构看起来不像堆,所以我建议从头开始正确地实现堆(你可以阅读维基百科的文章,以确保你正确地理解它)。