堆-二叉堆

      我们这里说的堆并不是内存中的堆,我们所说的是数据结构-堆,堆 也叫优先队列是一种特殊的完全二叉树,看下图这棵完全二叉树有什么特点? 他的每个父节点的值都不小于其子节点的值。这称为一个最大堆,最小堆就是父节点的值均不大于字节点的值的完全二叉树。

堆-二叉堆_第1张图片

二叉堆有两点性质:

      ①结构性:堆具有完全树的结构

      ②有序性:堆的父节点值一定不小大于(小于)其子节点的值

那堆有什么作用呢?举一个栗子

      假如有9个数:100 19 36 17 3 25 1 2 7 让你找出其中的最大值该怎么找?很简单 遍历一遍即可,时间复杂度位 O(N) 把问题复杂化一点 将这9个数中的最大值删除,再增加一个数20 再找出其中的最大值 怎么做?和原来相似:遍历一遍将最大值替换为20 然后再遍历一遍 时间复杂度为 为 O (2N) 所以也是 O(N), 但是 再复杂一点 进行 9 次这样的查找删除查找操作将怎样做?很容易也能想到 一个 for 中 嵌套 两个 for 时间复杂度变为了 O(N^2),那有没有更好的方法了呢?

      答案是肯定的,那就是用最大堆来实现,首先我们将 这 9 个数建成一个最大堆 (如上图), 最大值 100 直接打印,然后把 100 替换为 20 ,此时我们要维护一个最大堆 最大堆的特性是 父节点的值要不小于子节点的值 我们看 36 比 20 要大 就将 20 和 36 交换,然后 再看 25 要比 20 大 我们再将两者值交换 ,再看 20 已经 处于 叶子节点位置 此时 可以 停止了 打印最大值 36 .我们只进行了 2 次 交换就完成 了一次  ,这样的时间复杂度是 O(log N) 完成 总的N次操作 时间复杂度是 O(N log N)。

      如果你们感觉 两个算法差不多的话 我们来比较一下在大数据的情形下两者的差距 假如 有 1亿 个数 来进行 上面的 操作 我们

      假设电脑每秒钟运行10亿次 ,那么 第一种算法 需要运行 1 亿 * 1 亿 次 也就是 1000 亿次  也就是 电脑 需要运行 1000 万秒 也就是 115 天 ! 再 看第二种算法 : 需要运行 1 亿 * log 1 亿 = 27亿 (不到)  也就是 电脑运行 2.7 秒!通过比较 我们就可以感受到了 算法的强大!

好了 继续 说我们 二叉堆的存储 :二叉堆一般是 用 数组 来实现的 我们一般从 下标为一的 数组开始储存这样方便操作 比如 将以上最大堆存储在一个数组中

0 1 2 3 4 5 6 7 8 9
  100 19 36 17  3 25   1   2   7

 看这个数组有什么性质:下标为 i 的父节点的左子节点的下标是:2 * i,右子节点的下标是 2 * i + 1;

 好了介绍完 储存该讲讲怎么建立了 我把创建堆分成两个函数,一个是初始化函数,一个是插入函数  看代码:

typedef struct heap* priority;
struct heap{
	int *array; //用来储存内容 
	int size;  //用来记录目前储存内容的数量 
	int capacity; //记录最大能储存的数量 
};

priority Create(int capacity){
	priority H = (priority)malloc(sizeof(struct heap));
	H->array = (int*)malloc(sizeof(int)*(capacity + 1)); //因为要从 下标一开始存储 所以要capacity + 1 
	H->size = 0;
	H->capacity = capacity;
	return H;
}

void Insert(priority H,int data){
	if (H->size > H->capacity)
	{
		printf("FULL!"); //判断数组是否存满了;
		return ;
	}
	int flag = ++H->size; //从数组的最后一个元素向前判断,每次与父节点判断 
	while (flag != 1 && data> H->array[flag / 2]) //当父节点的值比其子节点的值小时,将子节点的值换作父节点的值 
	{
		H->array[flag] = H->array[flag / 2];
		flag = flag / 2;
	}
	H->array[flag] = data; //找到 data 合适的位置后将data赋给该位置 
}

再将删除操作,这里的删除操作是将最大值删除然后将数组最后一个元素放到第一个位置 再维护一个最大堆

void Delete(priority H){
	if (H->size==1) //当只剩一个元素时无法进行删除
	{
		printf("Only one Element!\n");
		return ;
	}
	int value = H->array[H->size--]; //将最后一个值用另一个变量保存起来然后删除最后一个变量
	int child;
	int i = 1;
	for ( i=1;2*i<=H->size;i=child)  从第一个元素开始比较看其子节点的值是不是小于父节点若不是 则将父节点的值换位子节点的最大值(我们假设第一个位置先是value)
	{
		child = 2*i;
		if (child != H->size)
		{
			child = H->array[2*i] > H->array[2*i+1] ? 2*i :2*i+1;
		}
		if (H->array[child] > value)
		H->array[i] = H->array[child];
		else break; //当子节点的值不大于父节的值时结束
	}
	H->array[i] = value; //将该位置的值换为value
}

 

你可能感兴趣的:(二叉堆,数据结构,C)