zoukankan      html  css  js  c++  java
  • 常用排序算法(四)堆排序

    1.简介

    堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序,

    二叉堆满足二个特性:

    1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

    2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)

    每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

    同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

    该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

    大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

    小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

    2.基本思想

    将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

    3.构造初始堆

    将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)

    a.假设给定无序序列结构如下

    b.此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。

    c.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。

    这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。

    此时,我们就将一个无需序列构造成了一个大顶堆。

    4.将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换

    a.将堆顶元素9和末尾元素4进行交换

    b.重新调整结构,使其继续满足堆定义

    c.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.

    后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序

    再简单总结下堆排序的基本思路:

      a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

      b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

      c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

    5.代码实现

    def heapSort(list: Array[Int]): Array[Int] = {
        for (i <- list.indices) {
          val len = list.length
          buildMaxHeapTree(list, len - 1 - i)
          //每次loop 后用array(0) 最大值跟最后一个元素互换 ,后面循环时候排序最后一个元素
          swapFunction(list, 0, len - 1 - i)
        }
        list
      }
    
      def buildMaxHeapTree(array: Array[Int], lastIndex: Int): Unit = {
        //根据最后一个节点计算父节点,最有一个节点可能是左节点 也可能是右节点
        //val parent = (lastIndex -1) / 2
        for (parent <- Range((lastIndex - 1) / 2, -1, -1) if lastIndex > 0) {
          //根据父节点计算左叶子节点
          val leftChild = 2 * parent + 1
          //定义一个变量等于leftchild 索引 ,用于跟父节点做swap 交换
          var swapChild = leftChild
          //根据左边节点计算右边节点
          val rightChild = leftChild + 1
          //判断如果右边节点存在,那么 比较左右几点大小
          if (rightChild <= lastIndex && array(leftChild) < array(rightChild)) {
            swapChild += 1
          }
          //判断父节点跟较大左右节点中的数据大小
          if (array(parent) < array(swapChild)) {
            swapFunction(array, parent, swapChild)
          }
        }
      }
    
      def swapFunction(array: Array[Int], parent: Int, child: Int): Unit = {
        val temp = array(parent)
        array(parent) = array(child)
        array(child) = temp
      }
    

      

  • 相关阅读:
    LightOJ 1094
    hdu 2586
    hdu 5234
    hdu 2955
    LightOJ 1030 数学期望
    poj 1273
    CodeIgniter学习笔记(十五)——CI中的Session
    CodeIgniter学习笔记(十四)——CI中的文件上传
    CodeIgniter学习笔记(十三)——CI中的分页
    CodeIgniter学习笔记(十二)——CI中的路由
  • 原文地址:https://www.cnblogs.com/feiyumo/p/9300198.html
Copyright © 2011-2022 走看看