【算法导论学习】堆排序+优先队列

长得像树的数组

啥玩意是堆啊

堆,其实就是一个数组,按一定的规律排列其中的元素。他虽然是数组,但是可以看成一个完全树。

  1. 以二叉堆(这也是用的最多的堆)为例,按图形展示他就长这样:
    在这里插入图片描述
    在一个完整的大堆中,所有的有子节点的节点,都是一个小堆的堆顶元素。

  2. 按照数组的形式表示的话就长这样:
    在这里插入图片描述
    其中连线表示前面的节点是后面的节点的父节点。

堆又有什么性质呢
  1. 堆分为两种,一种是最大堆,一种是最小堆,后面都以最大堆为例。
  2. 在最大堆中,所有的父节点都大于他的子节点,但是子节点没有顺序要求,毕竟不是平衡二叉树。
  3. 满足完全二叉树的定义,也就是除了最后一层,其他层的节点必须是满的。
  4. 如果是用数组储存的话,那么假设某个节点的下标是i,那么他的两个子节点就是 2i 和 2i + 1。同理,他的父节点的下标就是 i / 2。
代码部分
核心:维护堆的性质

为了维护堆的基本性质,那么就需要写个函数出来,满足父节点一定大于子节点。
那么在不满足的条件下,也就是父节点小于了子节点,这很好办,交换就完事了。

但是父节点的父节点也小于这个牛逼的子节点呢,万一祖宗结点还是小于这个字结点呢。
所以我们不能好事只做一般,光把这个子节点和他的父节点换了,还要一直往上找,一直比较然后找到他的老祖宗,并且依次交换。
这里可以用递归也可以直接用个while循环。
在这里插入图片描述

  1. 算法导论上的伪代码:
    在这里插入图片描述
  2. 我自己按照伪代码写的程序:
//堆的构造,输入数组、构造点和数组长度 
void heapify(int A[], int i , int n)
{
	int l = i*2;
	int r = i*2+1;
	int largest = i;
	if(l <= n-1 && A[l] > A[largest])
		largest = l;
	if(r <= n-1 && A[r] > A[largest])
		largest = r;
	if(i != largest)
	{
		swap(A, i, largest);
		heapify(A, largest, n);
	}
} 
  1. 时间复杂度:
    按道理来说是时间复杂度是O(lg n)
    在这里插入图片描述
    通过之前讲的主定理解得T(n) = O(logn)

堆的应用

见的多的应该是堆排序,用的多的应该是优先队列。

堆排序

快又快不过快速排序,只是作为知识点了解下就好了

构建堆

很简单,把所有带有子节点的结点都heapify一边就完事了

  1. 算法导论上的伪代码在这里插入图片描述
  2. 我自己写的代码
//构建堆 
void build_heap(int A[], int n)
{
	for(int i = n/2; i>=0; --i)
		heapify(A, i, n);
}
  1. 时间复杂度
    树高 lg n, 高度为h的堆最多包括n/2^(k+1)个结点
    在这里插入图片描述
    所有时间复杂度就是O(n)
堆排序

我们还是拿最大堆举例,因为现在已经知道了堆顶的元素是最大元素,那么直接将这个元素提出来,再对剩下的元素继续heapify,然后依次取出堆顶元素就好了。
在这里插入图片描述在这里插入图片描述

  1. 算法导论上的伪代码:
    在这里插入图片描述
  2. 我自己写的:
//堆排序 
void heapsort(int A[], int n)
{
	build_heap(A, n);
	for(int i = n-1; i > 0; i--)
	{
		swap(A, 0, i);
        heapify(A, 0, i);
	}
} 
  1. 时间复杂度分析:
    在这里插入图片描述
    所以堆排序的时间效率就是O(n lg n)
优先队列

在这里插入图片描述
这个部分都挺简单,直接贴个伪代码和C语言版本的就算了。

MAXIMUM
  1. 伪代码:
    在这里插入图片描述

  2. C语言代码:

//仅返回队列中最大的元素
int max_mum(int A[])
{
	return A[0];
} 
EXTRACT-MAX
  1. 伪代码:
    在这里插入图片描述
  2. C语言代码:
//返回队列中最大的元素,并且在队列中删除该元素
int extract_max(int A[], int n)
{
	if(n < 1)
	{
		printf("数组空,没最大");
		return -1; 
	}
	int max = A[1];
	n--;
	heapify(A, n, 0);
	return max;
}
INCREASE-KEY
  1. 伪代码:
    在这里插入图片描述

  2. C语言代码:

//增加i的值到key(只能增加,减小的话可以改下条件)
void increase_key(int A[], int i, int key)
{
	if(key < A[i])
	{
		printf("更改的值小于原来的值");
		return ;
	} 
	A[i] = key;
	while(i > 0 && A[i/2] < A[i])
	{
		swap(A, i, i/2);
		i = i/2;
	} 
}
INSERT
  1. 伪代码:
    在这里插入图片描述
  2. C语言代码:
    因为我的数组长度不是一个全局变量,所以需要返回重置n的值
//插入一个元素
int insert(int A[], int key, int n)
{
	n++;
	A[n] = -10086;
	increase_key(A, n, key);
	//返回改变后的队列长度 
	return n;
} 

全部的完整代码

#include<stdio.h>

//自己写的数组交换函数 
void swap(int A[], int i, int j)
{
	int temp = A[i];
	A[i] = A[j];
	A[j] = temp;
}

//堆的构造,输入构造点和数组长度 
void heapify(int A[], int i , int n)
{
	//左右子节点 
	int l = i*2;
	int r = i*2+1;
	
	//寻找三个结点中最大的结点 
	int largest = i;
	if(l <= n-1 && A[l] > A[largest])
		largest = l;
	if(r <= n-1 && A[r] > A[largest])
		largest = r;

	//将最大的元素置为堆顶,并递归执行 
	if(i != largest)
	{
		swap(A, i, largest);
		heapify(A, largest, n);
	}
} 

//构建堆 
void build_heap(int A[], int n)
{
	for(int i = n/2; i>=0; --i)
		heapify(A, i, n);
}

//堆排序 
void heapsort(int A[], int n)
{
	//先初始化堆 
	build_heap(A, n);
	
	
	//通过循环一次将数组第一位排出 
	for(int i = n-1; i > 0; i--)
	{
		swap(A, 0, i);
        heapify(A, 0, i);
	}
} 

/*******下面是优先队列部分*******/
/*以最大优先队列为基础*/

//仅返回队列中最大的元素
int max_mum(int A[])
{
	return A[0];
} 

//返回队列中最大的元素,并且在队列中删除该元素
int extract_max(int A[], int n)
{
	if(n < 1)
	{
		printf("数组空,没最大");
		return -1; 
	}
	
	int max = A[1];
	n--;
	
	heapify(A, n, 0);
	return max;
}

//增加i的值到key(只能增加,减小的话可以改下条件)
void increase_key(int A[], int i, int key)
{
	if(key < A[i])
	{
		printf("更改的值小于原来的值");
		return ;
	} 
	A[i] = key;
	while(i > 0 && A[i/2] < A[i])
	{
		swap(A, i, i/2);
		i = i/2;
	} 
} 
	 
//插入一个元素
int insert(int A[], int key, int n)
{
	n++;
	A[n] = -10086;
	increase_key(A, n, key);
	
	//返回改变后的队列长度 
	return n;
} 

/**************over**************/

int main()
{
	int tree[] = {2, 5, 3, 1, 10, 4};
    int n = 6;
    heapsort(tree, n);
	for(int i; i<n; i++)
	{
	    printf("%d ",tree[i]);
	}
    
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值