再写堆(堆的性质,向下调整,建堆,堆的插入删除初始化,堆排序,TopK问题)

堆的概念

如果有一个关键码的集合K={k0,k1,k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储再一个一维数组中,并满足:Ki<=K2i+1且Ki<=K2i+1(Ki >= K2i+1 且 Ki >= K2i+2),i=0,1,2,3…。则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆

堆的性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

堆的向下调整算法

顺序存储的完全二叉树

已知[parent]
[left] = 2*[parent]+1;
[right] = 2*[parent]+2;已知[child] 无论左右
[parent] = ([child]-1)/2

基本步骤

建立小堆

  1. 要调整root所在结点,前提:root的左右结点子树已经满足堆的性质
  2. 如果root所在结点已经是叶子结点,调整结束
  3. 找到左右孩子中最小的一个min
  4. if (array[root] <= array[min])。调整结束
  5. else: swap(&array[root],&array[min]) ; [min] = [root] ; 再回到1重复这几个步骤

代码实现

void AdjustDown(int array[], int size, int root)
{//判断 root 是否是叶子结点//因为 堆是完全二叉树,所以没有左孩子一定没有右孩子//又因为堆是顺序存储的//所以,找到左孩子的下标,如果左孩子的下标越界了,则没有左孩子while (1){int left = 2 * root + 1;if (left >= size){//越界了,就是叶子结点return;}//走完上面一定有左孩子,判读是否有右孩子//找到左右孩子最小的一个int right = 2 * root +2;int min = left;//最开始就认为最小的值为左孩子if (right < size && array[right] < array[left]){//没有越界,就有右孩子,如果右孩子的值小于左孩子的值min = right;}//比较array[min]  array[root]if (array[root] <= array[min]){return;}//调整,交换值int t = array[root];array[root] = array[min];array[min] = t;//需要继续向下调整,以min作为结点root = min;}
}

测试

void PrintArray(int array[], int size)
{for (int i =0; i < size; ++i){printf("%  d", array[i]);}printf("\n");
}void TestAdjustDown()
{int array[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };int size = sizeof array / sizeof(int);PrintArray(array, size);AdjustDown(array, size, 0);PrintArray(array, size);
}

时间复杂度(logn)

在这里插入图片描述

建堆

循环变量i从最后一个非叶子节点开始,到0结束,在这之间不断的做向下调整

最后一个非叶子节点 = 最后一个结点的双亲结点( size-1) = ((size-1)-1)/2

在这里插入图片描述

代码实现

//建堆
void CreateHeap(int array[], int size)
{for (int i = (size - 2) / 2; i >= 0; i--){//不断的向下调整AdjustDown(array, size, i);}
}

测试

时间复杂度O(n)

void TestCreateHeap(){int array[] = { 15, 37, 2, 45, 63, 9, 18, 7, 16, 13 };int size = sizeof(array) / sizeof(int);CreateHeap(array, size);PrintArray(array, size);
}

在这里插入图片描述

堆的初始化

//初始化
void HeapInit(Heap *heap, int array[], int size){memcpy(heap->array, array, size*sizeof(int));heap->size = size;CreateHeap(heap->array, size);
}

堆的删除

  1. 只能删除堆顶元素,删其他位置没有意义
  2. 拿数组中最后一个元素,覆盖掉数组第一个元素,也就是堆顶元素,然后向下调整

代码实现

//删除
void HeapPop(Heap *heap)
{heap->array[0] = heap->array[heap->size - 1];AdjustDown(heap->array, heap->size - 1,0);heap->size--;
}

堆的插入

  1. 往数组中最后一个位置插入,然后向上调整
//向上调整
/*
array 数组
size 数组的长度
child 要向上调整的结点下标
*///小堆的情况
void AdjustUp(int array[], int size, int child)
{while (1){//已经到堆顶位置if (child == 0){return;}int parent = (child - 1) / 2;//父结点的值比孩子的值小的就不用调整if (array[parent] <= array[child]){return;}//交换int t = array[parent];array[parent] = array[child];array[child] = t;child = parent;}
}
/插入操作
void HeapPush(Heap *heap, int val){heap->array[heap->size++] = val;AdjustUp(heap->array, heap->size, heap->size - 1);
}

堆排序

堆排序不能找最小的放到最前,要不然堆的结构会被破坏

堆排序的注意事项

  1. 排升序,建大堆
  2. 排降序,建小堆
  3. 原因是,重新调整回堆的成本更小(向下调整(O(logn)) < 建堆(O(n)))
i的意义是被选出的最大的数的个数
for(i < size-1){//一次循环,找出一个最大的数放到最后swap(&array[0],&array[size-1-i])AdjustDown(array,size-1-i);
}

代码实现

//大堆的情况void AdjustDown222(int array[], int size, int root)
{//判断 root 是否是叶子结点//因为 堆是完全二叉树,所以没有左孩子一定没有右孩子//又因为堆是顺序存储的//所以,找到左孩子的下标,如果左孩子的下标越界了,则没有左孩子while (1){int left = 2 * root + 1;if (left >= size){//越界了,就是叶子结点return;}//走完上面一定有左孩子,判读是否有右孩子//找到左右孩子最小的一个int right = 2 * root + 2;int min = left;//最开始就认为最小的值为左孩子if (right < size && array[right] > array[left]){//没有越界,就有右孩子,如果右孩子的值小于左孩子的值min = right;}//比较array[min]  array[root]if (array[root] >= array[min]){return;}//调整,交换值int t = array[root];array[root] = array[min];array[min] = t;//需要继续向下调整,以min作为结点root = min;}
}
void AdjustUp222(int array[], int size, int child)
{while (1){//已经到堆顶位置if (child == 0){return;}int parent = (child - 1) / 2;//父结点的值比孩子的值小的就不用调整if (array[parent] >= array[child]){return;}//交换int t = array[parent];array[parent] = array[child];array[child] = t;child = parent;}
}//堆排序
//升序,建大堆void HeapSort(int array[], int size)
{CreateHeap(array, size);//i 表示被找出的最大的数的个数for (int i = 0; i < size - 1; i++){//每次循环,会找出最大的一个数放到最后int t = array[0];array[0] = array[size - i - 1];array[size - i - 1] = t;//进行向下调整,数据规模是size-1-i;AdjustDown222(array, size - 1 - i, 0);}
}

测试

void TestHeapSort()
{int array[] = { 39, 129, 12, 38, 27, 9, 33, 2, 14 };int size = sizeof(array) / sizeof(int);HeapSort(array, size);PrintArray(array, size);
}

在这里插入图片描述

测试堆排序与冒泡排序的速度

#define SIZE 50000void TestSortSpeed(){srand(20190104);int array[SIZE] ;for (int i = 0; i < SIZE; i++){array[i] = rand() % 10 * 10000;}int s = time();HeapSort(array, SIZE);int e = time();printf("%d\n", e - s);}

堆排序速度:
在这里插入图片描述
冒泡排序速度:
在这里插入图片描述

TopK问题

在海量数据中(n>>100*10000),找最大的k=10个数

/*
类似伪代码,实际中,size是海量的,内存中放不下,需要借助文件操作
*/
void TopK(int array[], int size, int k){int *heap = (int *)malloc(sizeof(int)*k);for (int i = 0; i < k; i++){heap[i] = array[i];}//针对heap建小堆CreateHeap(heap, k);for (int j = k; j < size; j++){if (array[j]>heap[0]){heap[0] = array[j];AdjustDown(heap, k, 0);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383158.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二叉树题目----6 二叉树的最近公共祖先 AND 二叉树搜索树转换成排序双向链表

二叉树的最近公共祖先 思路 在左、右子树中分别查找是否包含p或q&#xff1a;如果以下两种情况&#xff08;左子树包含p&#xff0c;右子树包含q/左子树包含q&#xff0c;右子树包含p&#xff09;&#xff0c;那么此时的根节点就是最近公共祖先如果左子树包含p和q&#xff0c;…

二叉树题目 ----7 前序中序遍历构造二叉树

前序中序遍历构造二叉树 思路 在前序中找根结点根据根结点 中序&#xff0c;分成左右两棵子树根据子树长度&#xff0c;把前序分成左右两颗子树递归处理子树 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* s…

关联式容器(map,set,multimap,multiset)

关联式概念 STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这 些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。关联式容器也是用来存储数据的&#xff0c;与序列式…

详解 二叉搜索树-----AVL树

二叉搜索树 根结点比左子树中所有结点都大根结点比右子树所有结点都小最小的元素在最左侧最大的元素在最右侧中序遍历有序 具有以上的特征的二叉树就是二叉搜索树也叫二叉排序数 二叉搜索树的操作 查找 要保存查找值的双亲&#xff0c;以便于后续执行插入操作 Node* Find(…

json格式与cJSON函数库

json的格式 键/值对 key:value&#xff0c;用半角冒号分割文档对象 JSON对象写在花括号中&#xff0c;可以包含多个键/值对。数组 JSON 数组在方括号中书写&#xff1a; 数组成员可以是对象&#xff0c;值&#xff0c;也可以是数组(只要有意义)。 { "stars":[ { &qu…

HTTP清晰的学习笔记

HTTP协议—应用层 请求消息(Request)—浏览器给服务器发 包含四部分 请求行&#xff1a;说明请求类型&#xff0c;要访问的资源&#xff0c;以及使用的http版本请求头&#xff1a;说明服务器要使用的附加信息,由键值对构成的空行&#xff1a;空行是必须要有的&#xff0c;即…

如何在 Centos7 x86_64下将vim一键配置为一款强大的C++,IDE

vim功能很强大&#xff0c;但是对于我这样的小白很不友好 然后就有一位大佬&#xff0c;为了辅助我这样的菜鸡&#xff0c;然后供我们一键搞定&#xff0c;在vim畅快编写代码的功能。 首先强烈不推荐在root用户下使用&#xff0c;确保电脑连着网。在普通用户下执行此命令 cur…

详解string容器(应用+模拟实现,string练习题)

为什么要有string容器 string&#xff1a;其实就是一个字符串,c对字符串进行了封装的&#xff0c;封装到一个类里面&#xff0c;这样用户就不用担心开辟空间的问题&#xff0c;只需要往string类里放字符串就可以了&#xff0c;string其实还可以自增长 很多人就会有一个疑问&am…

浅拷贝+引用计数--写时拷贝---模拟实现string容器

引用计数 深拷贝 多个对象共享同一份资源时&#xff0c;最后能够保证该资源只被释放一次 应该由哪个对象释放资源&#xff1f; 由最后一个使用该资源的对象去释放 怎么知道一个对象是最后一个使用该资源的对象&#xff1f; 给一个计数&#xff0c;记录使用该资源对象的个数 实…

详解vector容器(应用+模拟实现,vector相关练习题)

vector容器 动态的顺序表&#xff0c;数组。 vector操作 vector操作及其概念 构造 vector<int>v1;vector<int>v2(10, 5);vector<int>v3(v2);int array[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };vector<int>v4(array, array sizeof(array) / sizeof(a…

详解list容器(应用+模拟实现)

list容器 带头结点的双向循环链表 list操作 list容器的概念及其操作 构造和销毁 list<int>L1;list<int>L2(10, 5);vector<int>v{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };list<int>L3(v.begin(), v.end());list<int>L4(L3);元素访问 cout << L3.…

vector和list容器有哪些区别

这个问题的本质还是在问顺序表和链表的区别 底层结构不同 vector容器list容器一段连续的空间带头结点的双向循环链表 元素访问方式 vector容器list容器支持随机访问—O(1)不支持随机访问—O(N)需要扩容不需要扩容任意位置插入元素----O(N)–搬移元素O(1) 迭代器不同 vector…

复习栈和队列,详解最小栈,栈的弹出压入序列,逆波兰表达式求值

栈和队列的概念 栈:吃进去吐出来 对列&#xff1a;吃进去拉出来 数据结构中的栈和内存中的区别 数据结构中的栈具有后进先出的特性&#xff0c;而内存中的栈是一个内存空间&#xff0c;只不过这个内存空间具与数据结构的栈具有相同的特性。 栈和队列操作 栈和队列基本操作…

详解优先级队列priority_queue(应用+模拟实现)

优先级队列的概念 优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元素(优先队列中位于顶部的元 素)。优先队列被实现为容…

私人博客定制

项目背景 可行性方面 需求分析&#xff1a; 详细设计&#xff1a; 数据库设计 博客管理API的设计 标签相关API 服务器端的实现 对数据库操作进行封装 对服务器操作进行封装 客户端实现 具体操作 使用markdown 具体实现 测试 项目效果展示 维护 完整代码 项目…

初识c++中的函数模板

函数模板 函数模板概念 函数模板:编译器生成代码的一个规则。函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实参类型产生函数的特定类型版本。 函数模板格式 //要让这个函数与类型无关 //Add函数模板 template…

深入理解c++中的函数模板

非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量来使…

c++中的IO流

c语言中的IO操作 标准类型的输入输出: 输入------>数据来源是通过键盘进行输入输出------>程序中的数据输出到控制台 c语言中: scanf:输入 printf:输出 两个函数的相同点 1 —格式串 2 —不定参数 两个函数的缺陷 1 —用户要提供数据的格式—用户要记忆大量的格式串—…

201301 JAVA2~3级---走格子

请编写一个函数&#xff08;允许增加子函数&#xff09;&#xff0c;计算n x m的棋盘格子&#xff08;n为横向的格子数&#xff0c;m为竖向的格子数&#xff09;沿着各自边缘线从左上角走到右下角&#xff0c;总共有多少种走法&#xff0c;要求不能走回头路&#xff0c;即&…

复习Linux基本操作----常见指令

Linux基本操作 ls命令 ls(list):相当于windows上的文件资源管理器 语法&#xff1a; ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及其他信息。 常用选项&#xff1a; -a 列出目…