Heap(堆结构/优先队列)-Swift实现

特性

堆结构很像二叉树,堆也是一个近似树形结构,堆的每个节点也最多有左、右两个孩子,但是堆实质是存储在数组中的结构,所以他和二叉树只是近似的有某些共同的特性。

  • 第一特性,堆结构是存储在数组中的。
  • 堆通过 priority 优先事项进行排序,可以分为,max-priority(最高优先级)、和 max-priority(最低优先级)两种
  • 二叉搜索树中,有left<parent<right 的特性,但是在堆中,只是只能保证孩子一定是小于或大于父节点的。
  • 二叉树在存储的过程中需要更多的内存,因为节点需要存储指向父节点,子节点等的指针,但是堆不需要,他存储在数组中
  • 二叉树中的一些特别树,如红黑树,AVL树等,删除,插入,及搜索的多度都很快,但是,堆结构的搜索相对来说会慢很多,但是堆结构对于查找,最大或者最小等需求时,查找速度是很快的。
  • 堆有一个很重要的特性,就是只有在上一层完全满之后,才能到下一层来进行存储,不允许中间某个节点的子节点为空,因为是通过数组存储的嘛

前面说过堆结构实际存储在数组中,他具体的形式如下


树形结构

实际存储

从上面的图中可以推导出,一个重要的公式:
节点 node 的孩子在数组中的位置:
左孩子:index = 2 * i + 1
右孩子:index = 2 * i + 2
其中i为当前节点n在数组中的下标

插入及删除

堆结构的插入及删除操作的基本思路如下:

  • 插入: 插入时一定是从最后面开始插入,类似入队操作,插入后,用新节点和父节点比,如果优先级大于父节点,则和父节点交互位置后,继续和新的父节点比较,依次类推
  • 删除:删除通常为删除最大的节点,即根节点,过程为,先将根节点和最后面的节点互换位置后,在删除最后面的节点,即原来的根节点,然后用根节点和子节点向下进行比较,如果小于子节点,则互换位置,依次类推。
    时刻谨记,堆结构是存储在数组中的。

实现

struct Heap<Element> {
    ///存放元素的数组
    var elements: [Element]
    //比较两个元素大小的方法
    let priorityFunction: (Element, Element) -> Bool
    
    init(elements: [Element] = [], priorityFunction: @escaping (Element, Element) -> Bool) { // 1 // 2
        self.elements = elements
        self.priorityFunction = priorityFunction // 3
        buildHeap() // 4
    }
    
    mutating func buildHeap() {
        for index in (0 ..< count / 2).reversed() { // 5
            siftDown(index) // 6
        }
    }
    
    var isEmpty : Bool {
        return elements.isEmpty
    }
    
    var count : Int {
        return elements.count
    }
    
    func peek() -> Element? {
        return elements.first
    }
    func isRoot(_ index: Int) -> Bool {
        return (index == 0)
    }
    ///当前节点的左孩子
    func leftChildIndex(of index: Int) -> Int {
        return (2 * index) + 1
    }
    ///当前节点的右孩子
    func rightChildIndex(of index: Int) -> Int {
        return (2 * index) + 2
    }
    ///当前节点的父节点
    func parentIndex(of index: Int) -> Int {
        return (index - 1) / 2
    }

      //插入 
    mutating func equeue(_ element: Element) {
        elements.append(element)
        siftUP(elementAtIndex: elements.count - 1)
    }
    
   //删除
    mutating func dequeue() -> Element? {
        //判空
        guard !isEmpty else { return nil }
        
        //将第一个节点和最后一个节点交换位置
        swapElement(at: 0, with: count - 1)
        //删除原本的第一个,现在的最后一个
        elements.removeLast()
        
        guard !isEmpty else { return nil }
        //向下判断,新的节点是不是优先级最高的节点
        return nil
    }
}

private extension Heap {
    func isHigherPriority(firstIndex: Int, secondIndex: Int) -> Bool{
        return priorityFunction(elements[firstIndex], elements[secondIndex])
    }
    
    func highestPriorityIndex(of parentIndex: Int, and childIndex: Int) -> Int {
        guard childIndex < count && isHigherPriority(firstIndex: childIndex, secondIndex: parentIndex)
            else { return parentIndex }
        return childIndex
    }
    
    func highestPriorityIndex(for parent: Int) -> Int {
        return highestPriorityIndex(of: highestPriorityIndex(of: parent, and: leftChildIndex(of: parent)), and: rightChildIndex(of: parent))
    }
    
    mutating func swapElement(at firstIndex: Int, with secondIndex: Int) {
        guard firstIndex != secondIndex
            else { return }
        elements.swapAt(firstIndex, secondIndex)
    }
    
    mutating func siftDown(_ elementIndex: Int) {
        //从当前节点及子节点中找出优先级最高的节点
        let highestIndex = highestPriorityIndex(for: elementIndex)
        //如果当前节点就是优先级最高的节点,直接放回
        if highestIndex == elementIndex { return }
        //如果不是优先级最高的节点,和优先级最高的节点对换位置
        swapElement(at: elementIndex, with: highestIndex)
        //从新的节点开始递归的向下判断优先级
        siftDown(highestIndex)
    }
    
    mutating func siftUP(elementAtIndex: Int)  {
        //获得父节点的索引
        let parentIndex = self.parentIndex(of: elementAtIndex)
        //如果当前节点不是根节点,比较当前节点和父节点的优先级
        guard !isRoot(elementAtIndex), isHigherPriority(firstIndex: elementAtIndex, secondIndex: parentIndex) else {
            return
        }
        //如果当前节点的优先级高于父节点,兑换位置
        swapElement(at: elementAtIndex, with: parentIndex)
        
        //递归的从新的父节点开始向上比较
        siftUP(elementAtIndex: parentIndex)
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,501评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,673评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,610评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,939评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,668评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,004评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,001评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,173评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,705评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,426评论 3 359
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,656评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,139评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,833评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,247评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,580评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,371评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,621评论 2 380

推荐阅读更多精彩内容

  • 之前的文章中,我们有介绍过动态数组ArrayList,双向队列LinkedList,键值对集合HashMap,树集...
    Single_YAM阅读 4,029评论 1 14
  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,475评论 1 31
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,135评论 0 12
  • 0.目录 1.优先队列ADT 2.几种实现 3.二叉堆 4.d-堆 5.左式堆 6.斜堆 7.二项队列 8.斐波那...
    王侦阅读 3,134评论 1 2
  • 都说校园里的爱情像糖一样甜美,可我更感觉像酒一样清澈。 校园时光总是清纯美好却一去不复返,大大小小的事情沉在脑海里...
    沵花阅读 572评论 0 2