内容概述

1,堆结构就是用数组实现的完全二叉树结构
2,完全二叉树中如果每棵子树的最大值都在顶部就是大根堆
3,完全二叉树中如果每棵子树的最小值都在顶部就是小根堆
4,堆结构的heaplnsert与heapify操作
5,堆结构的增大和减少
6,优先级队列结构,就是堆结构

1. 堆的定义

首先需要明确的是,堆是完全二叉树,完全二叉树不一定是堆(因为堆一定是排好序的【大根或者小跟】,完全二叉树就不一定了,不一定是堆)

这里有一篇文章,对堆描述的非常好【点击此处】

我们需要知道的是,堆是有大根堆和小根堆的,只有这两种特殊的完全二叉树才能称为堆

大根堆是指,每个根节点一定比其子节点大;小根堆是指,每个根节点一定比其子节点小
比方:

这个就是大根堆

根据这个特性,我们可以很快的拿到最大值或者最小值(均在根节点处)

但是,大根堆或者小根堆的数组,不一定是已经有序的,想变成有序的数组,还需要继续堆排序

2. 堆的数学属性

i是数组的某元素下标,现在需要知道该元素在堆中的父叶或者左子叶、右子叶的位置

父叶:parent(i) = (i - 1)/2
左子叶:left(i) = 2i + 1
右子叶:right(i) = 2i + 2

这是用的最多的堆的数学属性

3. 大根堆的插入

如果需要在一个大根堆里插入一个数,让其继续变成大根堆,应该怎么实现呢?
这就是HeapInsert过程

HeapInsert过程
当往一个大根堆里放入数的时候,先通过公式父叶:parent(i) = (i - 1)/2找到该元素对应的父节点,然后将该元素与父叶比较,如果该元素大,则与父元素交换(后面如果又比该父叶的父叶大,则继续交换);如果父元素大,则该元素不掉换位置

程序:

public static void HeapInsert(int[] arr, int index){//判断插入的元素与其父叶的大小,大则交换while (arr[index] > arr[(index - 1) / 2]){Swap(arr, index, (index - 1) / 2);//交换元素的自定义方法index = (index - 1) / 2;//下标换为其父叶坐标,继续判定}}

4. 大根堆的取元素

因为堆的根节点要么最大,要么最小,经常需要取出其元素的值,然后重新排序成大根堆的结构,这个过程叫做Heapify

Heapify过程:
在某个堆中,取出某根节点(父叶),方法是将该元素与堆数组中最后一个元素互换后,比较交换后元素与根节点的左、右节点的最大值比较,叶大,则与叶交换;叶小,则不变

程序:

//取大根堆的某一元素后,把剩下的元素依然组成大根堆
//方法中的参数含义:arr-数组;index-已经交换后的元素下标;size-数组的长度public static void Heapity(int[] arr, int index, int size){//左子叶的下标int left = index * 2 + 1;//如果该父叶的左子叶存在,那么继续,不存在就跳出while (left < size){//右子叶存在,且找出左、右子叶的最大值的下标int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;//将该下标对应的元素与父子叶对应的元素比较大小,当该下标对应的元素大于父子叶对应的元素时,交换元素argest = arr[largest] > arr[index] ? largest : index;//当该下标对应的元素小于或者等于父子叶对应的元素时,跳出循环if (largest == index){break;}//交换父子叶元素与最大值子叶下标元素的值Swap(arr, largest, index);//继续判断,此时的index下标变成刚刚的最大的子叶元素下标index = largest;//重新给左子叶赋值left = index * 2 + 1;}}

5. 大根堆的形成

1.先让整个数组都变成大根堆结构,建立堆的过程:
从上到下的方法,时间复杂度为0(N*l ogN)
从下到上的方法,时间复杂度为O(N)

2.把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(N*logN)

3.堆的大小减小成0之后,排序完成

方法1:
将数组每一个元素进行HeapInsert过程,直至所有元素使用完

代码;

//大根堆的形成public static void HeapForBig(int[] arr){if (arr == null || arr.Length < 2){return;}for (int i = 1; i < arr.Length; i++){HeapInsert(arr, i);}}

方法2
这种方法会快一些,用Heapify操作

 public static void HeapForBig(int[] arr){if (arr == null || arr.Length < 2){return;}//原理://每次从最底层比较,依次往上,但是最底层下面没有数,所以节约了此处的时间复杂度,往上一层,则调用heapify过程,从子叶找最大值与其比较,再往上一层也是如此for (int i = arr.Length-1; i>=0; i--){Heapity(arr,i,arr.length)}}

6. 堆排序(使用大根堆,从小到大排序)

把堆的最大值和堆末尾的值交换,然后减少堆的大小之后,再去调整堆,一直周而复始,时间复杂度为O(NlogN),也就是说,堆排序时间复杂度为O(NlogN)

首先将数组变成大根堆的形式,然后再把最上面的根节点与最后面的叶节点互换,数组元素移除最后一个并进行Heapify操作;然后再将上面的根节点与最后面的叶节点互换,数组元素移除最后一个并进行Heapify操作…直至数组中只剩一个元素

代码:

public static void HeapSort(int[] arr){//首先进行大根堆组合if (arr == null || arr.Length < 2){return;}HeapForBig(arr);//数组初始长度int size = arr.Length;//将第0位的数组元素与最后一位进行交换,并size长度自减Swap(arr, 0, --size);//如果数组还有剩下的数,则再进行此操作while (size > 0){Heapity(arr, 0, size);Swap(arr, 0, --size);}}

7. 小根堆的插入

原理是和大根堆的插入是一样的
代码:

 //插入数、按小根堆组合public static void HeapInsertForLittle(List<int> arr, int index){while (arr[index] < arr[(index - 1) / 2]){Swap2(arr, index, (index - 1) / 2);index = (index - 1) / 2;}}

8. 小根堆的取元素

需要实现的目的是,每取走一个数(拿一个数与其交换),剩下的数仍然是小根堆

代码:

//取小根堆的某一元素后,把剩下的元素依然组成小根堆public static void HeapifyForLittle(List<int> arr, int index, int size){int left = index * 2 + 1;while (left <= size){var maxindex = (left + 1) <= size && (arr[left] < arr[left + 1]) ? left : left + 1;var t = arr[maxindex] < arr[index] ? maxindex : index;if (t == index){break;}//交换数组元素Swap2(arr, index, maxindex);left = index * 2 + 1;}}

9. 小根堆的形成

 //组成小根堆public static void HeapForLittle(List<int> arr){if (arr == null || arr.Count < 2){return;}for (int i = 1; i < arr.Count; i++){HeapInsertForLittle(arr, i);}}

10. 运用堆的算法题

题目:
已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序,从小到大排序

思路:
可以看出的是,假设数组第0位现在需要放最小值,因为几乎有序,所以其值一定在[0,k]之间,在这里面找数组最小值即可,也就是我们取的小根堆最上面的值即可;然后现在数组第1位需要放全数组的次小值,于是将数组[1,k+1]进行小根堆排序,小根堆最上面的根节点就是整个数组的次小值…

程序:

class Program{static void Main(string[] args){List<int> t1 = new List<int>() { 5, 1, 3, 2, 6, 4, 9 };Heap.SortArrayDistanceLessK(t1, 4);Console.ReadLine();}}class Heap{//已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序,从小到大排序。//可以看出的是,假设数组第0位现在需要放最小值,因为几乎有序,所以其值一定在[0,k]之间,在这里面找即可,那么我们取的小根堆最上面的值即可//假定给定的K=4,作为测试public static void SortArrayDistanceLessK(List<int> arr, int k){List<int> replace = new List<int>();List<int> have = new List<int>();//int[] replace = {ar}for (int i = 0; i < arr.Count; i++){if (replace.Count==0){for (int j = 0; j <= k; j++){replace.Add(arr[j]);}}if (k+i+1<arr.Count){HeapForLittle(replace);have.Add(replace[0]);replace.Remove(replace[0]);replace.Add(arr[k + i+1]);}else if(replace!=null){HeapForLittle(replace);have.Add(replace[0]);replace.Remove(replace[0]);}else{return;}}for (int z = 0; z < arr.Count; z++){Console.WriteLine(have[z]);}}//组成小根堆public static void HeapForLittle(List<int> arr){if (arr == null || arr.Count < 2){return;}for (int i = 1; i < arr.Count; i++){HeapInsertForLittle(arr, i);}}//插入数、按小根堆组合public static void HeapInsertForLittle(List<int> arr, int index){while (arr[index] < arr[(index - 1) / 2]){Swap2(arr, index, (index - 1) / 2);index = (index - 1) / 2;}}//交换元素public static void Swap2(List<int> arr, int i, int j){var t = arr[i];arr[i] = arr[j];arr[j] = t;}}

算法笔记-堆相关、堆的定义、大小根堆、算法程序实现、堆的算法题、C#写法相关推荐

  1. 算法笔记——数学相关

    算法笔记--数学相关 高精度 乘法逆元 排列组合 二项式定理 质数的判定和应用 约数 拓展欧几里得 大步小步算法(BSGS) 拓展大步小步算法 快速乘和快速幂 矩阵相关 欧拉函数 欧拉定理及费马小定理 ...

  2. C++ 优先队列priority_queue,如何定义大小根堆

    1. 代码如下(示例): priority_queue < int >默认构建的是一个大根堆,所以每次从头取数据得到的是一个从大到小的队列排序. priority_queue< in ...

  3. 算法笔记-链相关、链的基础、单链双链环链、链的各种功能实现、链的算法题、面试题以及算法优化方法(多)、C#

    1. 链定义及其基础 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素.这组存储单元既可以是连续的,也可以是不连续的. 链表定义: 链表是一种线性表数据结构: 从底层存储 ...

  4. linux堆上的内存可执行吗,pwn的艺术浅谈(二):linux堆相关

    这是linux pwn系列的第二篇文章,前面一篇文章我们已经介绍了栈的基本结构和栈溢出的利用方式,堆漏洞的成因和利用方法与栈比起来更加复杂,为此,我们这篇文章以shellphish的how2heap为 ...

  5. 算法笔记之:大整数的四则运算

    带来<算法笔记>第五章: 大整数的四则运算 #include<cstdio> #include<cstring>struct bign{int d[1000];in ...

  6. codeup墓地目录(算法笔记习题刷题笔记)

    在线codeup contest 地址:http://codeup.cn/contest.php Contest100000575 - <算法笔记>3.1小节--入门模拟->简单模拟 ...

  7. Eva 初学算法笔记 —— 1.直接插入排序

    Eva 初学算法笔记  ---  1.直接插入排序 引言: 上一年专业开设了<数据结构与算法>这门课程,但当时没有认真学.过了之后发现自己对于那些基本的经典算法都忘得差不多了,大学剩下的日 ...

  8. 《算法笔记》2.3小节——C/C++快速入门-选择结构

    <算法笔记>2.3小节--C/C++快速入门->选择结构 Contest100000567 - <算法笔记>2.3小节--C/C++快速入门->选择结构 Conte ...

  9. 【STL学习】堆相关算法详解与C++编程实现(Heap)

    堆简介 堆并不是STL的组件,但是经常充当着底层实现结构.比如优先级队列(Priority Queue)等等. 堆是一种完全二叉树,因此我们可以用数组来存储所有节点.在这里的实现中,采用了一个技巧:将 ...

最新文章

  1. 毕业季:理想很丰满,现实也可以很丰满!
  2. C++中的const关键字(zz)
  3. 闭包,函数式编程学习小记
  4. ArrayList深入解析,看这篇就够了
  5. 两个多元正态分布的KL散度、巴氏距离和W距离
  6. 小程序获取列表的下标
  7. asp无组件上传图片 动态保存文件名 upload.inc逐句翻译
  8. eclipse 快捷键收藏
  9. 黑马程序员 手写xaml界面系统维护
  10. Ubuntu 9.04 解决没有声音的问题 (Realtek声卡)
  11. 华为培训中华为数通HCIE考试流程-ielab
  12. 微信小程序tabbar消失_微信小程序tabBar 返回tabBar不刷新页面
  13. IOS 苹果手机 使用重力加速度,js web devicemotion,deviceorientation事件
  14. php网络图片拼接,图片处理 - PHP图片拼接如何高效的实现
  15. 关于LTE网络质量的参数
  16. 基于R的飞机航线数据可视化(行政区划)
  17. 【历史上的今天】4 月 3 日:亚马逊卖出第一本书;世界上第一通手机电话;IBM 计算机先驱出生
  18. 用Excel自带图表插入带分类变量的箱形图(单坐标轴并列箱)
  19. adb 模拟按键,调试按键,android测试可以用
  20. 信创云元年,易捷行云EasyStack发布新一代全栈信创云

热门文章

  1. centos pureftpd mysql_使用PureFTPd和MySQL的虚拟主机(包括配额和带宽管理)在CentOS 6.2上...
  2. Promise基础用法
  3. 微信小程序 --- 拨打电话
  4. instanceOf,isInstance,Class,isAssignableFrom区别比较
  5. sqlserver锁机制详解(sqlserver查看锁)
  6. iOS -- UIApplication
  7. Android程序的反编译对抗研究
  8. iOS纯代码工程手动快速适配
  9. WindowsPhone基础琐碎总结-----数据绑定(一)
  10. Qt动画框架The Animation Framework