1.0 问题描述

实现数据结构:堆。

2.0 问题分析

  1. 堆一般使用数组来表示,其中某个节点下标i的两个子节点的下标为 2i+1 和 2i+2。堆是一棵完全二叉树。
  2. 堆有3种基本操作:创建,插入,删除。
  3. 这3种操作都需要通过“调整堆”的方式来实现。调整堆是指,对堆中的某个节点,若它的值和它所有子节点相比,不是最大/最小,那么就需要将最大/最小的元素和当前节点交换,这种操作成为“调整堆”。
  4. 创建可以用插入来实现(复杂度O(nlogn)),也可以使用数组直接转换成堆(复杂度O(n))。
  5. 假设数组长度为n,那么这个数组转成堆,需要从第一个有子节点的节点((n - 1)/2或(n - 2)/2开始,倒序“调整堆”,直到下标为0。
  6. 插入操作可以先将数据插入到数组尾部,然后依次调整该数据的父节点,父节点的父节点…直到根节点。
  7. 删除操作可以先将数组首尾节点互换,然后删除最后面的元素,最后再调整根节点即可。

3.0 代码实现

3.1使用swift实现

/** 堆类型* small 小根堆* big 大根堆*/
enum HeapType {case small, big
}/** 堆*/
class Heap<T: Comparable> {private var _arr: [T];private var _type: HeapType;/** 使用数组创建堆* @param {[T]} - arr 创建堆的数组* @param {HeapType} - type 堆类型*/init(arr: [T], type: HeapType = .big) {self._arr = arr;self._type = type;self._create();}/** 调整堆:调整以idx为顶的3元素子堆,若顶部元素不是最大/最小,则调整为最大/最小* @param {Int} - idx 表示调整以idx为顶的3元素子堆* @return {Bool} - 调整成功返回true,无需调整返回false*/@discardableResultprivate func _adjust(_ idx: Int) -> Bool{let maxAdjustIdx = Int(ceilf(Float(_arr.count) / 2) - 1);if idx > maxAdjustIdx || idx < 0{return false;}let leftIdx = 2 * idx + 1;let rightIdx = 2 * idx + 2;let top: T? = _arr[idx];let left: T? = leftIdx < _arr.count ? _arr[leftIdx]: nil;let right: T? = rightIdx < _arr.count ? _arr[rightIdx]: nil;if let t = top {if let l = left, let r = right {let v = self._type == .big ? max(t, l, r) : min(t, l, r);switch v {case l:swapArr(arr: &_arr, f: leftIdx, t: idx);_adjust(leftIdx);return true;case r:swapArr(arr: &_arr, f: rightIdx, t: idx);_adjust(rightIdx);return true;default:break;}}else if let l = left {let b = self._type == .big ? (l > t) : (l < t);if b {swapArr(arr: &_arr, f: leftIdx, t: idx);_adjust(leftIdx);return true;}}else if let r = right {let b = self._type == .big ? (r > t) : (r < t);if b {swapArr(arr: &_arr, f: rightIdx, t: idx);_adjust(rightIdx);return true;}}}return false;}/*** 创建堆,依次调整 n/2 -> 0 元素*/private func _create(){let maxAdjustIdx = Int(ceilf(Float(_arr.count) / 2) - 1);for i in stride(from: maxAdjustIdx, to: -1, by: -1){_adjust(i);}}/*** 弹出一个元素,移除顶部元素,然后令顶部元素等于最后一个元素,最后调整顶部元素* @return [T?] 返回顶部元素*/func pop() -> T? {let first = _arr.first;if first != nil {if _arr.count <= 1 {_arr.removeLast();}else{swapArr(arr: &_arr, f: 0, t: _arr.count - 1);_arr.removeLast();_adjust(0);}}return first;}/*** 插入一个元素,插入到尾部,依次调整该元素的顶部元素,直到无需调整或下标为0* @param {T} - t 插入的元素*/func push(_ t: T){_arr.append(t);var idx = Int(ceilf(Float(_arr.count - 1) / 2) - 1);while true {if !_adjust(idx){break;}if idx == 0{break;}idx = Int(ceilf(Float(idx) / 2) - 1);}}/*** 返回当前元素数量* @return {Int} 返回堆内元素数量*/func count() -> Int{return _arr.count;}
}

3.2使用js实现

//常量
const BigHeap = 1;
const SmallHeap = 2;//构造函数
function Heap(type, arr, compareFunction){this.type = type;this.arr = arr;this.compareFunction = compareFunction;this._createHeap(arr);
}//数组交换
Heap._swap = function(i,j,arr){let tmp = arr[i];arr[i] = arr[j];arr[j] = tmp;
}//比较值
Heap.prototype._compare = function(v1, v2){if(this.type == SmallHeap){if(v2 == null){return true;}else{if(this.compareFunction){return this.compareFunction(v1, v2) == -1;}else{return v1 < v2;}}}else{if(v2 == null){return true;}else{if(this.compareFunction){return this.compareFunction(v1, v2) == 1;}else{return v1 > v2;}}}
}//调整堆的第i个结点
Heap.prototype._adjustNode = function(i, arr){let leftChildIdx = 2 * i + 1;let leftValue = null;if(leftChildIdx < arr.length){leftValue = arr[leftChildIdx];}let rightChildIdx = 2 * i + 2;let rightValue = null;if(rightChildIdx < arr.length){rightValue = arr[rightChildIdx];}if(!leftValue && !rightValue){return;}let exchangeIdx = null;//左值存在并且大于根结点if(leftValue && this._compare(leftValue, arr[i])){//右值存在并且大于左结点if(rightValue && this._compare(rightValue, leftValue)){//右值交换exchangeIdx = rightChildIdx;}else{//左值交换exchangeIdx = leftChildIdx;}}else if(rightValue && this._compare(rightValue, leftValue)){//右值交换exchangeIdx = rightChildIdx;}if(exchangeIdx != null){Heap._swap(exchangeIdx, i, arr);//交换完毕后,新的子结点也需要调整this._adjustNode(exchangeIdx, arr);}
}//根据数组创建堆
Heap.prototype._createHeap = function(arr){let len = arr.length;if(len <= 1){return;}//最后一个非叶子结点let lastNonLeafIdx = Math.floor(len / 2) - 1;//依次调整每个结点for (let index = lastNonLeafIdx; index >= 0; index--) {this._adjustNode(index, arr);}
}//插入
Heap.prototype.insert = function(ele){this.arr.push(ele);let adjustIdx = this.arr.length;while(adjustIdx > 0){adjustIdx = Math.ceil(adjustIdx / 2) - 1;this._adjustNode(adjustIdx, this.arr);if(adjustIdx <= 0){break;}}
}//删除
Heap.prototype.remove = function(){if(this.arr.length <= 0){return null;}let value = this.arr[0];if(this.arr.length > 1){this.arr[0] = this.arr.pop();this._adjustNode(0, this.arr);}else{this.arr.pop();}return value;
}//是否为空
Heap.prototype.empty = function(){return this.arr.length == 0 || this.arr[0] == null;
}

4.0 复杂度分析

  1. 数组转堆的复杂度

    • 首先需要进行n/2次堆的调整
    • 每次调整堆最多可能需要 logn次的二次调整
      • 所以时间复杂度小于 O(nlogn)
    • 每次调整堆最少可能需要1次调整
      • 所以时间复杂度大于O(n/2)
    • 实际上,需要少量调整的操作数远远大于需要多次调整的操作。
      • 根据调整规则,设最高高度为h = logn
      • 根节点需要调整次数为:2^0*h
      • 第二层需要调整次数为:2^1*(h-1)
      • … …
      • 第logn层需要调整次数为:2^(h-1)*1
      • 总次数count=2^0h + 2^1(h-1) + 2^2*(h-2) + … + 2^(h-1)*(h-(h-1)) //使用错位相减
      • count2 = 2^1h + 2^2*(h-1) + 2^3*(h-2) + … + 2^h*(h-(h-1))
      • count = count2-count = 2^1 + 2^2 + … + 2^(h-1) + 2^h - 2^0h
      • count = 2^(h+1) - 2 - h
      • count = 2n - 2 - logn
      • 所以时间复杂度为O(n)
  2. 堆的插入:O(logn)
  3. 堆的删除:O(logn)

算法(4)数据结构:堆相关推荐

  1. 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)

    堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...

  2. 【实战笔记】Java 算法与数据结构-排序(选择、插入、冒泡、希尔、归并、快速、堆)

    文章目录 基础排序算法O(n^2) 选择排序 插入排序及优化 冒泡排序及优化 希尔排序及优化 高级排序算法O(nlogn) 归并排序及优化 快速排序及优化 堆和堆排序 排序算法总结 本文为慕课网实战课 ...

  3. 维基百科上的算法和数据结构链接很强大

    突然发现维基百科上的算法和数据结构比百度百科强多啦,图文并茂. 其实这个网站不错:http://www.sorting-algorithms.com 冒泡排序: bubble冒泡的意思 http:// ...

  4. GitHub标星3w+的项目,全面了解算法和数据结构知识

    作者 | 程序员小吴 来源 | 五分钟学算法(ID: CXYxiaowu) 导语:今天分享一个开源项目,里面汇总了程序员技术面试时需要了解的算法和数据结构知识,并且还提供了相应的代码,目前 GitHu ...

  5. noj数据结构稀疏矩阵的加法十字链表_一个算法毁了一款好游戏?算法和数据结构到底有多重要?...

    来源 | 异步 | 文末赠书 前段时间大火的国产游戏--<太吾绘卷>,由于创新的玩法和精良的制作一度广受好评,然而随着玩家游戏的深入和时长的积累,发现该游戏在玩的过程中游戏外的问题很多很多 ...

  6. 浅谈算法和数据结构: 五 优先级队列与堆排序

    原文:浅谈算法和数据结构: 五 优先级队列与堆排序 在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象.最简单的一个例子就是,在手机上玩游戏 ...

  7. java优先队列的入队函数,算法与数据结构番外(1):优先队列

    这是算法与数据结构番外系列的第一篇,这个系列未来的主要内容是补充一些与算法与数据结构相关的知识,这些知识比较零碎,同时也与正传关系密切,往往需要阅读了正传的相关内容以后,才能较好的理解这部分内容.如果 ...

  8. python数据结构推荐书-「算法与数据结构」从入门到进阶吐血整理推荐书单

    推荐一下「算法与数据结构」从入门到进阶的书单. 一.入门系列 这些书籍通过图片.打比方等通俗易懂的方法来讲述,让你能达到懂一些基础算法,线性表,堆栈,队列,树,图,DP算法,背包问题等,不要求会实现, ...

  9. 【算法与数据结构】堆排序是什么鬼?

    排序算法相必大家都见过很多种,例如快速排序.归并排序.冒泡排序等等.今天,我们就来简单讲讲堆排序. 在上一篇中,我们讲解了二叉堆,今天的堆排序算法主要就是依赖于二叉堆来完成的,不清楚二叉堆是什么鬼的, ...

  10. jdk7默认gc算法_JDK 7的算法和数据结构

    jdk7默认gc算法 在定期检查JDK中是否存在一种或另一种标准算法时,我决定进行这种索引. 有趣的是,为什么其中包含一些著名的数据结构或算法,而其他却没有? 此调查的格式仅涉及JDK的算法和数据结构 ...

最新文章

  1. 基于图机器学习的微生物网络关系预测算法研究
  2. 10个最常见的JS错误
  3. C语言Main函数到底有几种,你真的懂吗?
  4. java的事务类型及定义
  5. 访问修饰符作用范围由大到小是_9个java基础小知识
  6. 转同事博客一则,随感
  7. 动态获取textarea后面的p标签_HTML简单标签连起实现的小玩意:
  8. python操作haproxy配置文件实例
  9. ecshop 全站内页 显示最新文章
  10. 没有人愿意把自己放在被动的位置,一切皆是有原因的
  11. leetcode最大矩形_柱状图中的最大矩形
  12. s:radio 赋值取值和添加事件
  13. Linux下如何查看高CPU占用率线程 LINUX CPU利用率计算
  14. 差速转向机器人数学模型
  15. 酷狗音乐的临时缓存文件转换为MP3文件,java源码
  16. 伯克利校长全员邮件:上课先用 Zoom,还不行就发电报!
  17. 解决安卓手机H5页面input获得焦点时页面挤压或者底部上移
  18. 10款精选的后台管理系统,快来一起颤抖吧!
  19. Qt技巧:获取QTextEdit文本内容
  20. 赣南雪景美得像妖孽 让人窒息

热门文章

  1. 开源分布式Job系统,调度与业务分离-如何创建一个计划HttpJob任务
  2. 01 python爬虫
  3. [matlab]Monte Carlo模拟学习笔记
  4. 河南省第二届ACM程序设计大赛解题报告(置换群)
  5. MongoDB分布式原理以及read-preference和readConcern解决读写一致性问题
  6. jquery的live方法
  7. 关于层的挡隔问题的探讨
  8. Linux socket 网络编程 常用头文件
  9. 跨平台PHP调试器设计及使用方法——拾遗
  10. Python3中lambda表达式介绍