30.堆排序

  1. 30.排序-堆排序
    1. 30.1 知识点
      1. 堆排序基本原理
      2. 代码实现
        1. class语句块类 主函数外
        2. 主函数内
      3. 总结
    2. 30.2 知识点代码

30.排序-堆排序


30.1 知识点

堆排序基本原理

  • 构建二叉树和大堆顶规则

    • 构建二叉树。
    • 大堆顶调整。
    • 堆顶往后方。
    • 不停变堆顶。
  • 关键规则

    • 最大非叶子节点:数组长度/2 - 1。
  • 父节点和叶子节点

    • 父节点为i。
    • 左节点为2i + 1。
    • 右节点为2i + 2。
  • 套路写法

    • 3个函数:
      • 1个堆顶比较。
      • 1个构建大堆顶。
      • 1个堆排序。

代码实现

class语句块类 主函数外

// 第一步:实现父节点和左右节点比较
static void HeapCompare(int[] array, int nowFatherIndex, int arrayLength)
{
    // 通过传入的索引计算得到对应的左右叶子节点的索引
    int leftSonIndex = 2 * nowFatherIndex + 1;
    int rightSonIndex = 2 * nowFatherIndex + 2;
    // 可能会溢出数组的索引,需要判断

    // 用于记录较大数的索引
    int biggerIndex = nowFatherIndex;

    // 比较左节点,不能溢出
    if (leftSonIndex < arrayLength && array[leftSonIndex] > array[biggerIndex])
    {
        // 认为目前最大的是左节点,记录索引
        biggerIndex = leftSonIndex;
    }

    // 比较右节点,不能溢出
    if (rightSonIndex < arrayLength && array[rightSonIndex] > array[biggerIndex])
    {
        biggerIndex = rightSonIndex;
    }

    // 如果比较过后,发现最大索引发生变化了,就需要换位置了
    if (biggerIndex != nowFatherIndex)
    {
        // 交换逻辑
        int temp = array[nowFatherIndex];
        array[nowFatherIndex] = array[biggerIndex];
        array[biggerIndex] = temp;

        // 通过递归,看是否影响了叶子节点他们的三角关系,biggerIndex就是换了位置的子节点
        HeapCompare(array, biggerIndex, arrayLength);
    }
}

// 第二步:构建大堆顶
static void BuildBigHeap(int[] array)
{
    // 从最大的非叶子节点索引开始,不停往前构建大堆顶
    for (int i = array.Length / 2 - 1; i >= 0; i--)
    {
        HeapCompare(array, i, array.Length);
    }
}

// 第三步:结合大堆顶和节点比较,实现堆排序,把堆顶不停往后移动
static void HeapSort(int[] array)
{
    // 初始化构建大堆顶
    BuildBigHeap(array);
    // 执行过后,最大的数肯定就在最上层

    // 每一轮确定一个位置
    for (int i = array.Length - 1; i > 0; i--)
    {
        // 直接把堆顶端的数放到最后一个位置即可
        int temp = array[0];
        array[0] = array[i];
        array[i] = temp;

        // 重新进行大堆顶调整
        HeapCompare(array, 0, i);
    }
}

主函数内

// 创建无序数组
int[] arr = new int[] { 8, 7, 1, 5, 4, 2, 6, 3, 9 };

// 调用堆排序
HeapSort(arr);

// 打印输出
for (int i = 0; i < arr.Length; i++)
{
    Console.WriteLine(arr[i]);
    // 1
    // 2
    // 3
    // 4
    // 5
    // 6
    // 7
    // 8
    // 9
}

总结

  • 基本原理

    • 构建二叉树。
    • 大堆顶调整。
    • 堆顶往后方。
    • 不停变堆顶。
  • 套路写法

    • 3个函数:
      • 1个堆顶比较。
      • 1个构建大堆顶。
      • 1个堆排序。
  • 重要规则

    • 最大非叶子节点索引:数组长度/2 - 1。
  • 父节点和叶子节点索引

    • 父节点为i。
    • 左节点为2i + 1。
    • 右节点为2i + 2。
  • 注意

    • 堆是一类特殊的树。
    • 堆的通用特点是父节点大于或小于所有子节点。
    • 我们并没有真正地把数组变成堆,只是利用了堆的特点来解决排序问题。

30.2 知识点代码

using System;

namespace Lesson29_堆排序
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("堆排序");

            //主函数内

            #region 知识点二 代码实现

            //创建无序数组
            int[] arr = new int[] { 8, 7, 1, 5, 4, 2, 6, 3, 9 };

            //调用堆排序
            HeapSort(arr);

            //打印输出
            for (int i = 0; i < arr.Length; i++)
            {
                Console.WriteLine(arr[i]);
                //1
                //2
                //3
                //4
                //5
                //6
                //7
                //8
                //9
            }

            #endregion
        }

        #region 知识点一 堆排序基本原理
        //构建二叉树和大堆顶规则
        //大堆顶调整
        //堆顶往后方
        //不停变堆顶

        //关键规则
        //最大非叶子节点:
        //数组长度/2 - 1

        //父节点和叶子节点:
        //父节点为i 
        //左节点2i+1
        //右节点2i+2
        #endregion

        //class语句块类 主函数外

        #region 知识点二 代码实现

        //第一步:实现父节点和左右节点比较

        /// <summary>
        /// 父节点和左右节点比较函数
        /// </summary>
        /// <param name="array">需要排序的数组</param>
        /// <param name="nowFatherIndex">当前作为父节点的索引</param>
        /// <param name="arrayLength">有多少位置没有确定</param>
        static void HeapCompare(int[] array, int nowFatherIndex, int arrayLength)
        {
            //通过传入的索引 计算可得得 它对应的左右叶子节点的索引
            int leftSonIndex = 2 * nowFatherIndex + 1;
            int rightSonIndex = 2 * nowFatherIndex + 2;
            //可能算出来的会溢出数组的索引 我们一会再判断

            //用于记录较大数的索引
            int biggerIndex = nowFatherIndex;

            //先比左 再比右

            //比较左节点 不能溢出 
            if (leftSonIndex < arrayLength && array[leftSonIndex] > array[biggerIndex])
            {
                //认为目前最大的是左节点 记录索引
                biggerIndex = leftSonIndex;
            }

            //比较右节点 不能溢出
            if (rightSonIndex < arrayLength && array[rightSonIndex] > array[biggerIndex])
            {
                biggerIndex = rightSonIndex;
            }

            //如果比较过后 发现最大索引发生变化了 那就以为这要换位置了
            if (biggerIndex != nowFatherIndex)
            {
                //交换逻辑
                int temp = array[nowFatherIndex];
                array[nowFatherIndex] = array[biggerIndex];
                array[biggerIndex] = temp;

                //通过递归 看是否影响了叶子节点他们的三角关系 biggerIndex就是换了位置的子节点
                HeapCompare(array, biggerIndex, arrayLength);
            }
        }

        //第二步:构建大堆顶
        static void BuildBigHeap(int[] array)
        {
            //从最大的非叶子节点索引 开始 不停的往前 去构建大堆顶
            for (int i = array.Length / 2 - 1; i >= 0; i--)
            {
                HeapCompare(array, i, array.Length);
            }
        }

        //第三步:结合大堆顶和节点比较 实现堆排序 把堆顶不停往后移动
        static void HeapSort(int[] array)
        {
            //初始化构建大堆顶
            BuildBigHeap(array);
            //执行过后 最大的数肯定就在最上层

            //每一轮确定一个位置
            for (int i = array.Length - 1; i > 0; i--)
            {
                //直接把 堆顶端的数 放到最后一个位置即可
                int temp = array[0];
                array[0] = array[i];
                array[i] = temp;

                //重新进行大堆顶调整
                HeapCompare(array, 0, i);
            }
        }

        #endregion

        #region 知识点三 总结
        //基本原理
        //构建二叉树
        //大堆顶调整
        //堆顶往后方
        //不停变堆顶

        //套路写法
        //3个函数
        //1个堆顶比较
        //1个构建大堆顶
        //1个堆排序

        //重要规则
        //最大非叶子节点索引:
        //数组长度/2 - 1

        //父节点和叶子节点索引:
        //父节点为i 
        //左节点2i+1
        //右节点2i-1

        //注意:
        //堆是一类特殊的树
        //堆的通用特点就是父节点会大于或小于所有子节点
        //我们并没有真正的把数组变成堆
        //只是利用了堆的特点来解决排序问题
        #endregion
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏