最大堆(优先队列)

最大堆(优先队列)

1.它是一颗完全二叉树

2.父节点值大小永远大于子节点的值

用途 用于排序和存储用过的情况


知识点1: 二叉树点顺序存储

知识点2.完全二叉树

知识点3.最大堆的实现原理

以下就是代码实现部分

/***************************************
作者: 未闻花语
版本: v1.1
最后修改时间: 2018/06/11
电话: 159****7727
功能&使用方法: 
 * 泛型最大堆(优先队列)
 * 1.需要先了解二叉树,完全二叉树
 * 2.了解树形结构的顺序存储
 * 
 * Update:
 *      新增了数目统计属性
 *      修改了Clear函数 数目统计没有归0的问题
 *      删除了多余的引入项
 * 
 * PS:最大堆的2个必要条件
 * 1.一颗完全二叉树
 * 2.所有父节点的值都大于子节点的值
 * 
 * 优点:
 * 1.排序速度极快
 * 2.结构较为简单,由于是完全二叉树,基本没有内存浪费
 * 
 * 
 * 缺点:
 * 
 * 适用:
 *    用于存储和排序共存的结构
 * 
 * 
 * 存在方法:
 * <0> -----> public Push ----- 入
 * <1> -----> public Pop  ----- 出
 * <2> -----> public Clear ---- 清空
 * 
 * 存在属性:
 *      <0> -----> Size ----- 个数
***************************************/
namespace MyHeap
{
    class MaxHeap<T>
    {
        //容量
        int m_capacity;
        //长度
        int m_size;
        //数据
        T[] m_data;
        /*比较函数
         * 参0 > 参1 -----> +1
         * 参0 = 参1 -----> +0
         * 参0 < 参1 -----> -1
         */
        public delegate int Judge(T _t0, T _t1);
        Judge func0;

        //无参构造
        public MaxHeap(Judge _judgeFunc)
        {
            m_size = 0;
            m_capacity = 4;
            m_data = new T[m_capacity];
            func0 = _judgeFunc;
        }

        //带参构造
        public MaxHeap(int _count, Judge _judgeFunc)
        {
            m_size = 0;
            m_capacity = (_count > 1) ? _count : 2;
            m_data = new T[m_capacity];
            func0 = _judgeFunc;
        }

        //扩容
        private void Expansion()
        {
            if (m_size + 1 == m_capacity)
            {
                int nCapacity = (int)(m_capacity * 1.5f);
                T[] nData = new T[nCapacity];
                for (int i = 1; i <= m_size; ++i)
                {
                    nData[i] = m_data[i];
                }

                m_capacity = nCapacity;
                m_data = nData;
            }
        }

        //入堆
        public void Push(T _data)
        {
            Expansion();

            //放入数据
            m_data[++m_size] = _data;

            //向上遍历交换
            int i1 = m_size; //子节点
            int i0 = m_size / 2; //父节点

            while (i1 > 1 && func0(m_data[i0], m_data[i1]) == -1)
            {
                //交换
                T temp = m_data[i0];
                m_data[i0] = m_data[i1];
                m_data[i1] = temp;

                i1 = i0;
                i0 /= 2;
            }
        }

        //出堆
        public T Pop()
        {
            if (m_size < 1)
                return default(T);

            T r = m_data[1];

            //根节点确实的第一次补位操作
            int p = 1;
            while (true)
            {
                int pLeft = 2 * p;
                int pRight = pLeft + 1;

                //有左有右
                if (pRight <= m_size)
                {
                    //比较左右大小
                    if (func0(m_data[pLeft], m_data[pRight]) >= 0)
                    {
                        m_data[p] = m_data[pLeft];
                        p = pLeft;
                    }
                    else
                    {
                        m_data[p] = m_data[pRight];
                        p = pRight;
                    }
                }
                //有左无右
                else if (pLeft <= m_size)
                {
                    m_data[p] = m_data[pLeft];
                    p = pLeft;
                    break;
                }
                //无左无右
                else
                    break;
            }

            //为了完全二叉树的第二次补位操作
            m_data[p] = m_data[m_size];
            while (p > 2 && func0(m_data[p / 2], m_data[p]) == -1)
            {
                //交换
                T temp = m_data[p / 2];
                m_data[p / 2] = m_data[p];
                m_data[p] = temp;

                p /= 2;
            }

            m_size--;

            return r;
        }

        //清空
        public void Clear()
        {
            while (m_size > 0)
            {
                Pop();
            }
            m_size = 0;
        }

        //存在元素个数
        public int Size
        {
            get { return m_size; }
        }
    }
}
View Code

以下是 入口点测试部分

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//包含命名空间 
using MyHeap;
namespace DataStructure
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] rNameSpace = new string[] {"蒋伟","吴悦","曾真","刘芸",
            "珠海妮","李小满","薛涵","邱伊雨","李晶晶","戴林江","代安东","黄挺",
                "陈政","叶小青","徐逸"};
            var team0 = new MaxHeap<Student>(JudgeFunc);
            team0.Push(new Student(25, "李晶晶", true));
            team0.Push(new Student(18, "薛涵", true));
            team0.Push(new Student(15, "代安东", false));
            team0.Push(new Student(23, "李小满", true));
            team0.Push(new Student(10, "蒋伟", false));

            for (int i = 0; i < 5; ++i)
                Console.WriteLine(team0.Pop().ToString());
            
        }
        static int JudgeFunc(Student _s0, Student _s1)
        {
            if (_s0.m_id == _s1.m_id)
                return 0;
            else if (_s0.m_id > _s1.m_id)
                return 1;
            else
                return -1;
        }

    }
    class Student
    {
        //学号
        public int m_id;
        //姓名
        public string m_name;
        //性别
        public bool m_isGirl;
        //构造
        public Student(int _id, string _name, bool _isGirl)
        {
            m_id = _id;
            m_name = _name;
            m_isGirl = _isGirl;
        }

        //重写打印函数
        public override string ToString()
        {
            return string.Format("{0}[{1}] - {2}", m_name, m_id, m_isGirl ? "Girl" : "Boy");
        }
    }
}
View Code

以下是输出结果

再进行一个与冒泡排序的速度比较

从上述比较看出 当排序数组为100W时 最大堆的速度是冒泡排序的2W3多倍,上述证明了再设计程序时选择数据结构的重要性!

附上比较算法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//包含命名空间 
using MyHeap;
//引用动态库

namespace DataStructure
{
    class Program
    {
        static void Main(string[] args)
        {
            //随机值
          
            const int NumCount = 1000000;
            int[] rNum = new int[NumCount];
            Random ra = new Random(6);
            for (int i = 0; i < NumCount; ++i)
                rNum[i] = ra.Next(0, NumCount);
            int startTick = System.Environment.TickCount;

            //最大堆排序
            var team0 = new MaxHeap<int>(JudgeFunc);
            for (int i = 0; i < NumCount; ++i)
                team0.Push(rNum[i]);

            //输出
            for (int i = 0; i < 20; ++i)
                Console.WriteLine(team0.Pop() + " , ");

            Console.WriteLine("MaxHeap排序 TotalTick -----> " + (System.Environment.TickCount - startTick));

            ////冒泡排序
            //for (int i = 0; i < NumCount; ++i)
            //{
            //    for (int j = NumCount - 1; j > i; --j)
            //    {
            //        if (rNum[i] < rNum[j])
            //        {
            //            var temp = rNum[i];
            //            rNum[i] = rNum[j];
            //            rNum[j] = temp;
            //        }
            //    }
            //}

            ////输出
            //for (int i = 0; i < 20; ++i)
            //    Console.WriteLine(rNum[i] + " , ");

            //Console.WriteLine("冒泡排序 TotalTick -----> " + (System.Environment.TickCount - startTick));


        }
        static int JudgeFunc(int _s0, int _s1)
        {
            if (_s0 == _s1)
                return 0;
            else if (_s0 > _s1)
                return 1;
            else
                return -1;
        }

    }
    class Student
    {
        //学号
        public int m_id;
        //姓名
        public string m_name;
        //性别
        public bool m_isGirl;
        //构造
        public Student(int _id, string _name, bool _isGirl)
        {
            m_id = _id;
            m_name = _name;
            m_isGirl = _isGirl;
        }

        //重写打印函数
        public override string ToString()
        {
            return string.Format("{0}[{1}] - {2}", m_name, m_id, m_isGirl ? "Girl" : "Boy");
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/jwv5/p/9068712.html