算法(4)数据结构:堆
1.0 问题描述
实现数据结构:堆。
2.0 问题分析
- 堆一般使用数组来表示,其中某个节点下标i的两个子节点的下标为 2i+1 和 2i+2。堆是一棵完全二叉树。
- 堆有3种基本操作:创建,插入,删除。
- 这3种操作都需要通过“调整堆”的方式来实现。调整堆是指,对堆中的某个节点,若它的值和它所有子节点相比,不是最大/最小,那么就需要将最大/最小的元素和当前节点交换,这种操作成为“调整堆”。
- 创建可以用插入来实现(复杂度O(nlogn)),也可以使用数组直接转换成堆(复杂度O(n))。
- 假设数组长度为n,那么这个数组转成堆,需要从第一个有子节点的节点((n - 1)/2或(n - 2)/2开始,倒序“调整堆”,直到下标为0。
- 插入操作可以先将数据插入到数组尾部,然后依次调整该数据的父节点,父节点的父节点…直到根节点。
- 删除操作可以先将数组首尾节点互换,然后删除最后面的元素,最后再调整根节点即可。
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 复杂度分析
- 数组转堆的复杂度
- 首先需要进行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)
- 堆的插入:O(logn)
- 堆的删除:O(logn)
算法(4)数据结构:堆相关推荐
- 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority Queue)
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...
- 【实战笔记】Java 算法与数据结构-排序(选择、插入、冒泡、希尔、归并、快速、堆)
文章目录 基础排序算法O(n^2) 选择排序 插入排序及优化 冒泡排序及优化 希尔排序及优化 高级排序算法O(nlogn) 归并排序及优化 快速排序及优化 堆和堆排序 排序算法总结 本文为慕课网实战课 ...
- 维基百科上的算法和数据结构链接很强大
突然发现维基百科上的算法和数据结构比百度百科强多啦,图文并茂. 其实这个网站不错:http://www.sorting-algorithms.com 冒泡排序: bubble冒泡的意思 http:// ...
- GitHub标星3w+的项目,全面了解算法和数据结构知识
作者 | 程序员小吴 来源 | 五分钟学算法(ID: CXYxiaowu) 导语:今天分享一个开源项目,里面汇总了程序员技术面试时需要了解的算法和数据结构知识,并且还提供了相应的代码,目前 GitHu ...
- noj数据结构稀疏矩阵的加法十字链表_一个算法毁了一款好游戏?算法和数据结构到底有多重要?...
来源 | 异步 | 文末赠书 前段时间大火的国产游戏--<太吾绘卷>,由于创新的玩法和精良的制作一度广受好评,然而随着玩家游戏的深入和时长的积累,发现该游戏在玩的过程中游戏外的问题很多很多 ...
- 浅谈算法和数据结构: 五 优先级队列与堆排序
原文:浅谈算法和数据结构: 五 优先级队列与堆排序 在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象.最简单的一个例子就是,在手机上玩游戏 ...
- java优先队列的入队函数,算法与数据结构番外(1):优先队列
这是算法与数据结构番外系列的第一篇,这个系列未来的主要内容是补充一些与算法与数据结构相关的知识,这些知识比较零碎,同时也与正传关系密切,往往需要阅读了正传的相关内容以后,才能较好的理解这部分内容.如果 ...
- python数据结构推荐书-「算法与数据结构」从入门到进阶吐血整理推荐书单
推荐一下「算法与数据结构」从入门到进阶的书单. 一.入门系列 这些书籍通过图片.打比方等通俗易懂的方法来讲述,让你能达到懂一些基础算法,线性表,堆栈,队列,树,图,DP算法,背包问题等,不要求会实现, ...
- 【算法与数据结构】堆排序是什么鬼?
排序算法相必大家都见过很多种,例如快速排序.归并排序.冒泡排序等等.今天,我们就来简单讲讲堆排序. 在上一篇中,我们讲解了二叉堆,今天的堆排序算法主要就是依赖于二叉堆来完成的,不清楚二叉堆是什么鬼的, ...
- jdk7默认gc算法_JDK 7的算法和数据结构
jdk7默认gc算法 在定期检查JDK中是否存在一种或另一种标准算法时,我决定进行这种索引. 有趣的是,为什么其中包含一些著名的数据结构或算法,而其他却没有? 此调查的格式仅涉及JDK的算法和数据结构 ...
最新文章
- 基于图机器学习的微生物网络关系预测算法研究
- 10个最常见的JS错误
- C语言Main函数到底有几种,你真的懂吗?
- java的事务类型及定义
- 访问修饰符作用范围由大到小是_9个java基础小知识
- 转同事博客一则,随感
- 动态获取textarea后面的p标签_HTML简单标签连起实现的小玩意:
- python操作haproxy配置文件实例
- ecshop 全站内页 显示最新文章
- 没有人愿意把自己放在被动的位置,一切皆是有原因的
- leetcode最大矩形_柱状图中的最大矩形
- s:radio 赋值取值和添加事件
- Linux下如何查看高CPU占用率线程 LINUX CPU利用率计算
- 差速转向机器人数学模型
- 酷狗音乐的临时缓存文件转换为MP3文件,java源码
- 伯克利校长全员邮件:上课先用 Zoom,还不行就发电报!
- 解决安卓手机H5页面input获得焦点时页面挤压或者底部上移
- 10款精选的后台管理系统,快来一起颤抖吧!
- Qt技巧:获取QTextEdit文本内容
- 赣南雪景美得像妖孽 让人窒息