简体   繁体   中英

Low performance of the heap. O(n) instead of O(log n)

I have a problem with a performance of the heap. I don't know which part of my code effects the performance of this data structure. For instance inserting/deleting 9999 elements took 12000 ms. Heap has to do it in less than 100ms (in theory). And normal list did it in 20000 ms. Can you point some bad habits that I' ve used in my code ?

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

namespace ConsoleApplication1
{
    class Heap
    {
        int[] items;
        int size = 1;
        public Heap()
        {
            items = new int[size]; 
        }
        public Heap(int size)
        {
            items = new int[size];
        }
        public void AddOnLast(int value)
    {
        int i = items.Length + 1;
        Array.Resize(ref items, i);
        items[items.Length-1] = value;
    }
    // Deleting item of specific index from the array/ using LINQ
    public void RemoveAt(int indexToRemove)
    {
         items = items.Where((source, indexOfElements) => indexOfElements != indexToRemove).ToArray();
    }
    /*Method which push up the elment after adding it on the last place
    k - last index/ when k=0, element is on the top of the heap
    p - parent index/ item - value of element/ parent - value of parent */
    private void PushUp()
    {
        int k = items.Length - 1;
        while (k > 0)
        {
            int p = (k - 1) / 2;
            int item = items[k];
            int parent = items[p];
            if (item > parent)
            {
                //Swap of elements
                items[k] = parent;
                items[p] = item;
                k = p;
            }
            else
            {
                break;
            }
        }
    }

    // Method which put element in correct place of the heap
    public void insert(int value)
    {
        AddOnLast(value);
        PushUp();
    }

    /* After deleting item from the heap we have to push down element to check if heap structure is correct
    k - index of first element, top of the heap/ l - left "child" index / r - right child index
    max is max value - either right or left child  */
    private void PushDown()
    {
        int k = 0;
        int l = 2 * k + 1;
        while (l < items.Length)
        {
            int max = 1;
            int r = l + 1;
            if (r < items.Length)
            {
                if (items[r] > items[l])
                {
                    //max become right child
                    max++;
                }
            }
            if (items[k] < items[max])
            {
                //swap of 2 values, prevous element and next element are swapped
                int temp = items[k];
                items[k] = items[max];
                items[max] = temp;
            }
            else
            {
                break;
            }
        }
    }

    //Deleting item from the top of the heap
    public void delete()
    {
            if (items.Length == 0)
            {
                Console.Write("No elements to delete");
            }
               else if (items.Length == 1)
                {
                    RemoveAt(0);
                }
                else
                {
                    int hold = items[0];// we hold the first element
                items[0] = items[items.Length - 1]; // last element become first
                RemoveAt(items.Length - 1); // we delete last element, and check if last element which is now on the top of the heap is on the right place
                PushDown();
                }  
    }

}

Your code actually have more serious problems than just speed (that is caused by the resizing of array on every insert).

  • RemoveAt - Your implementation breaks the heap. You should insert there item from the end and PushDown/PushUp it. Aside from this, how is anybody supposed to know the index to delete?
  • PushDown does not work, You do not change variable "l". And the way You work with "max" index is not correct.
  • delete is fine, but You give no option to get that value. Rather begin with Pop.
  • What is the purpose of public AddOnLast? You should not provide methods that break the heap.
  • no argument checking is just a detail

Have a look at this

class Heap
        {
            int[] items;
            int size = 0;
            public Heap() : this(16)
            {
            }
            public Heap(int initSize)
            {
                if (initSize < 1)
                {
                    throw new ArgumentException("Size must be greater than 0");
                }
                items = new int[initSize];
            }


            /*Method which push up the elment after adding it on the last place
            k - last index/ when k=0, element is on the top of the heap
            p - parent index/ item - value of element/ parent - value of parent */
            private void PushUp()
            {
                int k = size - 1;
                while (k > 0)
                {
                    int p = (k - 1) / 2;
                    int item = items[k];
                    int parent = items[p];
                    if (item > parent)
                    {
                        //Swap of elements
                        items[k] = parent;
                        items[p] = item;
                        k = p;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            // Method which put element in correct place of the heap
            public void Insert(int value)
            {
                if (items.Length <= size)
                {
                    Array.Resize(ref items, items.Length * 2);
                }
                items[size++] = value;
                PushUp();
            }

            public bool IsEmpty()
            {
                return size == 0;
            }

            /* After deleting item from the heap we have to push down element to check if heap structure is correct
            k - index of first element, top of the heap/ l - left "child" index / r - right child index
            max is max value - either right or left child  */
            private void PushDown(int k)
            {
                int l = 2 * k + 1;
                while (l < size)
                {
                    int max = l;
                    int r = l + 1;
                    if (r < size)
                    {
                        if (items[r] > items[l])
                        {
                            //max become right child
                            max = r;
                        }
                    }
                    if (items[k] < items[max])
                    {
                        //swap of 2 values, prevous element and next element are swapped
                        int temp = items[k];
                        items[k] = items[max];
                        items[max] = temp;
                    }
                    else
                    {
                        break;
                    }
                    k = max;
                    l = 2 * k + 1;
                }
            }

            //Deleting item from the top of the heap
            public int Pop()
            {
                if (size == 0)
                {
                    throw new DataException("No elements to delete");
                }
                else if (size == 1)
                {
                    return items[--size];
                }
                else
                {
                    int ret = items[0];
                    items[0] = items[--size];
                    PushDown(0);
                    return ret;
                }
            }
        }

A few things that maybe have low performance:

Array.Resize
Your implementation of RemoveAt
Your implementation of insert

Not sure I understand what you need to achieve, but it seems you can replace your Heap class with a simple List<int>

RemoveAt -> List.RemoveAt
AddOnLast -> List.Add
Insert-> List.Insert(0, yourValue) //(If I understand correctly what you need)
delete -> List.removeAt(0) // or maybe List.removeAt(list.Length - 1)

And i trust Microsoft(and Mono coders) writing code for a List much better than I could write: I don't want to implement something that someone else has already implemented

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