你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

堆排序小demo

2021/12/17 14:25:45

前言

写了一个小例子,理解下创建堆和堆排序的过程。记录下,方便以后复习。直接上源码吧,注释比较详细。如有问题,希望各位大佬指正

import scala.collection.mutable.ArrayBuffer
import scala.util.control.Breaks.{break, breakable}

object Heap {

  def main(args: Array[String]): Unit = {
    // 任意创建一个数组
    val arr = Array(2,8)

    arr.foreach(x => print(x + " "))

    println()

    // 创建堆
    val heap = build_heap(arr)

    heap.foreach(x => print(x + " "))

    println()

    // 堆排序
    val sorted = heap_sort(heap)

    sorted.foreach(x => print(x + " "))
  }

  def build_heap(arr: Array[Int]):Array[Int] = {
    val heap = new ArrayBuffer[Int]()

    for(item <- arr) {
      // 添加元素至堆尾,此时有可能破坏堆的结构,会进行堆尾首元素进行上浮操作
      heap.append(item)
      // 堆尾元素上浮
      siftUp(heap, heap.size - 1)
    }
    heap.toArray
  }

  def heap_sort(heap: Array[Int]): Array[Int] = {
    // 新建一个数组,存储最终的有序结果
    val sortedArr = new ArrayBuffer[Int]()

    var lastItemIndex = heap.size - 1

    // 遍历堆
    while (lastItemIndex >= 0){
      // 每次取出堆顶元素
      sortedArr.append(heap(0))
      // 将堆顶元素调整至队首,此时有可能破坏堆的结构,需将堆顶元素进行下沉操作
      heap(0) = heap(lastItemIndex)
      // 将堆尾元素置位无用元素,用-1标记
      heap(lastItemIndex) = -1
      lastItemIndex = lastItemIndex - 1
      // 堆顶元素进行下沉
      siftDwon(heap, 0, lastItemIndex)
    }
    sortedArr.toArray
  }

  def siftUp(heap: ArrayBuffer[Int], index: Int) = {

    var k = index
    val item = heap(k)

    breakable {
      while (k > 0) {
        // 父节点
        val parent = (k - 1 ) >>> 1   // 完全二叉树父子节点索引关系: left child = 2 * parent + 1

        // 大顶堆。如果父节点大于待上浮元素,则不用上浮,跳出循环
        if(heap(parent) >= item)  break

        // 如果如果父节点小于待上浮元素,则父元素下沉
        heap(k) = heap(parent)

        // 调整待上浮位置,进行下一次上浮试探
        k = parent
      }
    }

    heap(k) = item
  }


  /*
  heap: 堆对象, 数组类型
  index: 进行下沉操作的元素的索引位置
  size: heap中实际存储的元素个数
   */
  def siftDwon(heap: Array[Int], index: Int, size: Int) = {

    var k = index
    val stop = size >>> 1
    val item = heap(index)

    breakable {
      while (k <= stop) {
        // 左子节点
        val left = 2 * k + 1
        // 右子节点
        val right = 2 * k + 2

        // 85~88行,获取左右子节点中较大的元素
        var c = left
        if(right <= size && heap(left) < heap(right)) {
          c = right
        }

        // 大顶堆。如果待下沉元素大于子节点,则不用下沉,跳出循环
        if(item >= heap(c)) break

        // 子节点中较大元素上浮
        heap(k) = heap(c)

        // 调整待下沉位置,进行下一次下沉试探
        k = c
      }
    }

    // 找到合适下沉位置
    heap(k) = item
  }
}