自定义堆二进制树实现 - 随机节点删除

时间:2018-01-08 03:15:36

标签: c# algorithm tree heap binary-heap

我一直在尝试解决this问题。如果将它与堆二进制树的ADT定义进行比较,则问题语句是自定义堆二进制树的位。在堆二进制树中,你总是执行deleteMax / deletetMin,具体取决于堆树的类型,但是在这里他们希望你删除一个特定的节点而不是这个问题。

我的解决方案是在我删除一个叶子节点的值时只发生一个测试用例:

这是我在编写Heap类时所做的努力。虽然源代码很大,但您可能希望专注于我遇到问题的DeleteSpecificValueFromHeap方法。

我使用数组实现了堆二进制树(C#中的List由数组支持)。考虑数组中堆二进制树的当前状态:

-1 12 5 13 20 6 7

堆二进制树看起来像这样:

        -1
    /        \
   12         5
  /   \     /    \
13    20    6     7

现在我想要删除值为13的节点。这种情况在我的堆二进制树中失败了。你能指出如何解决它吗? DeleteSpecificValueFromHeap方法是目前正在努力的方法。

public class Heap
{
    List<int> items;

    public int Root
    {
        get { return items[0]; }
    }

    public Heap()
    {
        items = new List<int>();
    }

    public void Insert(int item)
    {
        items.Add(item);

        if (items.Count <= 1)
            return;

        var i = items.Count - 1;

        while (i > 0)
        {
            var parentNodeValue = items[(i - 1) / 2];
            var newNodeValue = items[i];

            if (newNodeValue < parentNodeValue)
            {
                //we need to percolate up this node. so swap it with parent.
                items[(i - 1) / 2] = newNodeValue;
                items[i] = parentNodeValue;
                //update the position of newly inserted node after swapping
                i = (i - 1) / 2;
            }
            else
                break;
        }
    }

    public void DeleteSpecificValueFromHeap(int val)
    {
        for (var i = 0; i < items.Count; i++)
        {
            if (items[i] == val)
            {
                items[i] = items[items.Count - 1];
                items.RemoveAt(items.Count - 1);
                //reheapify : percolate down this node ith position

                var leftChildIndex = (i * 2) + 1;
                var rightChildIndex = (i * 2) + 2;



                while (leftChildIndex <= items.Count - 1) //chilren are there in the array.
                {
                    //child nodes of node at ith position
                    var child1Value = items[leftChildIndex];

                    if (rightChildIndex <= items.Count - 1)
                    {
                        var child2Value = items[rightChildIndex];
                        var currentNodeValue = items[i];
                        if (child1Value < child2Value)
                        {
                            //swap ith node with child 1
                            items[i] = child1Value;
                            items[leftChildIndex] = currentNodeValue;
                            i = leftChildIndex;
                        }
                        else
                        {
                            items[i] = child2Value;
                            items[rightChildIndex] = currentNodeValue;
                            i = rightChildIndex;
                        }
                    }
                    else
                    {
                        //case of only one child
                        var currentNodeValue = items[i];
                        items[i] = child1Value;
                        items[leftChildIndex] = currentNodeValue;
                        i = leftChildIndex;
                    }
                    leftChildIndex = (i * 2) + 1;
                    rightChildIndex = (i * 2) + 2;
                }
                break;
            }
        }

    }

更新

我根据@ Raudel的推荐更改了我的DeleteSpecificValueFromHeap方法,之后我在帖子中提到的测试用例现在还可以,但链接上的测试用例#9仍然失败。我真的很抱歉没有能够提供输入案例,因为它有.1百万输入,这是不可能放在这里。我现在需要一只鹰眼,如果还有什么不对的话,他可以干我的代码并帮助我吗?

public void DeleteSpecificValueFromHeap(int val)
        {
            for (var i = 0; i < items.Count; i++)
            {
                if (items[i] == val)
                {
                    items[i] = items[items.Count - 1];

                    if (i == items.Count - 1)
                    {
                        //you are deleting the right most leaf node at the lowest level
                        //so nothing needs to be done apart from deleting the node.
                        items.RemoveAt(items.Count - 1);
                        break;
                    }

                    items.RemoveAt(items.Count - 1);

                    if (i == 0)
                        //it is the root node. The only option is to percolate down.
                        PercolateDownTheNode(i);
                    else
                    {
                        var parentNodeValue = items[(i - 1) / 2];
                        if (items[i] < parentNodeValue)
                            PercolateUpTheNode(i);
                        else
                            PercolateDownTheNode(i);
                    }

                    break;
                }
            }

        }

        private void PercolateDownTheNode(int i)
        {
            //reheapify : percolate down this node ith position
            var leftChildIndex = (i * 2) + 1;
            var rightChildIndex = (i * 2) + 2;

            while (leftChildIndex <= items.Count - 1) //chilren are there in the array.
            {
                //child nodes of node at ith position
                var child1Value = items[leftChildIndex];

                if (rightChildIndex <= items.Count - 1)
                {
                    var child2Value = items[rightChildIndex];
                    var currentNodeValue = items[i];
                    if (child1Value < child2Value)
                    {
                        //swap ith node with child 1
                        items[i] = child1Value;
                        items[leftChildIndex] = currentNodeValue;
                        i = leftChildIndex;
                    }
                    else
                    {
                        items[i] = child2Value;
                        items[rightChildIndex] = currentNodeValue;
                        i = rightChildIndex;
                    }
                }
                else
                {
                    //case of only one child
                    var currentNodeValue = items[i];
                    items[i] = child1Value;
                    items[leftChildIndex] = currentNodeValue;
                    i = leftChildIndex;
                }
                leftChildIndex = (i * 2) + 1;
                rightChildIndex = (i * 2) + 2;
            }
        }

        private void PercolateUpTheNode(int i)
        {
            while (i > 0)
            {
                var parentNodeValue = items[(i - 1) / 2];
                var newNodeValue = items[i];

                if (newNodeValue < parentNodeValue)
                {
                    //we need to percolate up this node. so swap it with parent.
                    items[(i - 1) / 2] = newNodeValue;
                    items[i] = parentNodeValue;
                    //update the position of newly inserted node after swapping
                    i = (i - 1) / 2;
                }
                else
                    break;
            }
        }

1 个答案:

答案 0 :(得分:4)

问题在于,当您在任何位置移除但最后一个位置时,右侧的所有元素都向左移动一个位置,这可能最终在堆中停止成为堆。 从下面的3个步骤开始,你正在做前两个,但你没有正确地做第三个。

1, Delete the value from the array but do not remove it (this creates a "hole" and the tree is no longer "complete") 

2. Replace the deleted value with the "fartest right node" on the lowest level of the heap.
//you can see the first two steps like a swap

3. Heapify (fix the heap)://but you have two possible cases now

     if ( newValue < its parent node )
        Do an UPHeap
     else
        Do a DownHeap

在第3步中,您将与父母进行比较,并告诉您该怎么做,无论是上升还是下降。为此,我建议您分别为UpHeap和DownHeap创建方法,因为您将不止一次使用它们,代码将变得更加清晰。

我还应该指出你是通过循环找到值,这使得每次删除都是O(n)。正如您在问题陈述中所看到的,他们可以向您询问最多1e5的问题。这可能会超出时间限制(TLE),具体取决于阵列的大小。作为一个拇指规则,对于几乎所有在线评判者来说,解决问题的预期时间约为1秒。因此,对于大小为1e5的数组,您将不得不等待更长时间,这会使您认为应该有更好的东西,这是真的。

问题在于,您可以跟踪堆中值的位置。您可以将其保存在HashTable<int, int>(例如)中,这样您就可以要求给定值来获取堆内的位置。通过这种方式,您可以避免循环获取堆内的位置,但是只要在堆中移动该值,就必须更新它。为了更新它,你必须在UpHeap和DownHeap方法中添加几行,并且每次向UP / DOWN移动值时,都会更新HashTable中交换元素的位置。

<强>更新

我拿了你的代码并改变了一些东西,然后我上网并接受了问题,现在你可以确定这是有效的。 我认为错误发生在DownHeap方法中,这是我真正改变的唯一方法。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ContestLibrary
{
    public class Heap
    {
        List<int> items;

        public int Root
        {
            get { return items[0]; }
        }

        public Heap()
        {
            items = new List<int>();
        }

        public int GetMin()
        {
            if(items.Count == 0)
                throw new Exception("Empty Heap");
            return items[0];
        }

        public void Insert(int item)
        {
            items.Add(item);
            PercolateUpTheNode(items.Count - 1);
        }

        public void DeleteSpecificValueFromHeap(int val)
        {
            for (var i = 0; i < items.Count; i++)
            {
                if (items[i] == val)
                {
                    items[i] = items[items.Count - 1];
                    items.RemoveAt(items.Count - 1);

                    if (i == items.Count)
                        return;//cause you deleted the right most node

                    var parentNodeValue = items[(i - 1) / 2];

                    if (items[i] < parentNodeValue)
                        PercolateUpTheNode(i);
                    else
                        PercolateDownTheNode(i);

                    return;
                }
            }
        }

        private void PercolateDownTheNode(int i)
        {
            while (i < items.Count / 2) {
                //get the min child first
                int minChildIndex = 2 * i + 1;
                if (minChildIndex < items.Count - 1 && items[minChildIndex] > items[minChildIndex + 1]) {
                    minChildIndex++;
                }

                if (items[i] <= items[minChildIndex])
                    return;//I'm smaller than the minimum of my children

                //swap
                int temp = items[i];
                items[i] = items[minChildIndex];
                items[minChildIndex] = temp;

                i = minChildIndex;
            }
        }

        private int ParentIndex(int i)
        {
            return (i - 1) / 2;
        }

        private void PercolateUpTheNode(int i)
        {
            while (i > 0)
            {
                var parentValue = items[ParentIndex(i)];
                var currentValue = items[i];

                if (currentValue < parentValue)//swap
                {
                    items[ParentIndex(i)] = currentValue;
                    items[i] = parentValue;
                    i = ParentIndex(i);
                }
                else
                    return;
            }
        }
    }

    public class Problem
    {

        static void Main(string[] args)
        {
            Heap heap = new Heap();
            int q = int.Parse(Console.ReadLine());
            while (q-->0)
            {
                var line = Console.ReadLine().Split();
                int type = int.Parse(line[0]);
                switch (type)
                {
                        case 1:
                            heap.Insert(int.Parse(line[1]));
                        break;
                        case 2:
                            heap.DeleteSpecificValueFromHeap(int.Parse(line[1]));
                        break;
                        default:
                            Console.WriteLine(heap.GetMin());
                        break;
                }
            }
        }
    }
}