二叉堆介绍 Introduction to Heap (C implementation)


Preface

This article belongs to the Data Structure series, which give some brief introduction to Tree,Heap,and Hash. Binary Heap is an important basic structure and has great efficiency in Sorting problems (Heap Sort). If there is any error in the article, please feel free to point it out and I will be really aprreciated with it.
We will start from the properties of Binary Heap, which tells you what can be regarded as a binary heap. Then, we introduce the basic operations of it, like insertion, deletion, and build. Enjoy!


一、What is binary Heap?

Binary heap is one of the most common used heaps. It can also be used as a priority Queue (A Queue with order so that any element with higher priority will be pop first, and the elements with the same order will be pop following FIFO principle)
It’s a binary tree with 2 key properties:

Structure Property:

It must be a complete tree, which means all levels except possibly the last are full( Full means each node is either a leaf or has 2 children). And all nodes in the last level have to be as left as possible.
Binary Heap
This property makes it extremely easy to implement the binary heap with array.

Order Property:

There are 2 kinds of Binary Heap: Min Heap and Max Heap. Let’s take Max Heap as an example. In Max Heap, the element in root must be the maximum element in the whole heap, and element in the parent nodes must be bigger or equal to that of the children nodes. Note: There is no sorting information in the heap, no comparison between nodes in the same level.
Example of a max heap

二、Operations of Binary Heap (Max Heap)

1.Basic Struct

How can we represent a binary heap?

Since heap is a complete binary tree, we usually represent it as an array:
Root Node: Array[0]
For the i-th Node:
Its parent node: Array[(i-1)/2]
Left child: Array [2 * i+1]
Right child: Array [2 * i+2]

Last Element: Array[HeapSize-1]
There won’be any gap in the array since the tree is complete.

Array representation

typedef int Element 
struct HeapStruct{
    int Size;//Current number of Elements
    int Capacity;//Maximum element
    Element* Array;
    }
typedef struct HeapStruct* Heap;
//2 common boolean function
int isEmpty(Heap H){
return H->size==0;
}

int isFull(Heap H){
return H->size==H->capacity;
}

2.Insert

There are 2 Steps of Insertion.
Step1:
We insert the new input to the end, update the size information.
在这里插入图片描述
Step2:
Since the insertion breaks the Order Principle of heap, we need to keep comparing the new input and the element (Let’s say, A) in its parent node. We switch the postion of the new input and A until: new input is smaller or equal to A or new input is at the root.
在这里插入图片描述
Here’s the code in details:

//int has been type redefined as Element
//This helps a lot when you want to change the input type
void insert(Heap H,Element input){
if(isFull(H)){
   printf("Heap is Full!\n");
   return;
}
//Step1:
H->size++;
int childIndex=H->size-1, parentIndex=(childIndex-1)/2; 
//Step2:
while(parentIndex>=0&& input>H->Array[parentIndex]){
  H->Array[childIndex]=H->Array[parentIndex];
  childIndex=parentIndex;
  parentIndex=(childIndex-1)/2;
}
H->Array[childIndex]=input;
return;

Speed: O(logn)

3.Pop_Max

To Pop the max value, there are also 2 steps.
Step1:
We first create a variable to store the max value, which is at the root node. Then, we delete the root value, and use the last element to fill the blank. Remember to update the heap size.
EG
Step2:
After Step1, the order property of the heap might be broken. Then, we first find the bigger value of the new root’s children, and switch the new root’s position with the bigger child. Keep this process until the new root value is bigger than both of its children or it reaches the leaf level.
EG
Remember to return the variable you used to store the old root value. Below is the code in detail:

Element Pop_Max(Heap H){
//Step1:
Element Temp=H->Array[0];//Use the Temp to store the return value
Element lastElement=H->Array[H->size-1];//Get the value of the last
                                       //  element. 
H->size--;//Update the size
int parentIndex=0,childIndex=parentIndex*2+1;//Suppose the last element has been put at the root
                   
//Step2:
while(childIndex<=H->size-1){//Not reaches the leaf level
     if(childIndex<H->size-1){//If the node has 2 children
      if(H->Array[childIndex]]<H->Array[childIndex+1])
          childIndex++;//Find the bigger child
     }
     if(lastElement>=H->Array[childIndex])
         break;//Bigger than both, end.
     H->Array[parentIndex]=H->Array[childIndex];
     parentIndex=childIndex;
     childIndex=parentIndex*2+1;
}
H->Array[parentIndex]=lastElemen;
return Temp;

Speed: O(logn)

4.get_Max

Root is the maximum value due to the order principle

Element get_Max(Heap H){
 if(isEmpty(H)){
 printf("Heap is Empty!\n");
 exit(1);
 }
 return H->Array[0];
 }

Speed: O(1)

5.Build_Heap

Simple Approach

It’s very easy to build heap, you just need to allocate some space for the heap structure and insert all the elements into the heap:

Heap Build_Heap(int MaxSize){
Heap H=malloc(sizeof(struct HeapStruct));
H->size=0;
H->capacity=MaxSize;
H->Array=malloc(sizeof(Element)*(MaxSize+1));
}
//And just use the function insert to do the insertion of all elements

Better Approach:

The time complexity of the previous approach is O(n*logn). Can we do better? The answer is yes, we just need to insert all the elements in the heap without considering the Order Property, which means, do not care which one is bigger and just follow the input flow. Then, we start adjusting the order property of the last non-leaf node in reverse level order.
EG
Codes are below:

Heap buildHeap(int size, Element* list) {
	Heap H = malloc(sizeof(struct HeapStruct));
	H->size = size;
	H->capacity = size + 1;
	H->Array = malloc(sizeof(Element) * (size + 1));
	for (int i = 0;i < size;i++)
		H->Array[i] = list[i];
	int parent = (size-1 - 1) / 2;
	int children, Temp;
	while (parent >= 0) {//Reverse Order
		Temp = H->Array[parent];
		children = parent * 2 + 1;
		int localchildren = children, localparent = parent;
		if (children == size - 1) {
			if (Temp < H->Array[children])
			{
				H->Array[parent] = H->Array[children];
				H->Array[children] = Temp;
			}
		}
		else {
			do {
				if (H->Array[localchildren] < H->Array[localchildren + 1])
					localchildren++;
				if (Temp < H->Array[localchildren]) {
					H->Array[localparent] = H->Array[localchildren];
					H->Array[localchildren] = Temp;
				}
				localparent = localchildren;
				localchildren = localparent * 2 + 1;
			} while (localchildren<=size-1&&Temp < H->Array[localchildren]);

		}
		parent--;
	}

6. Delete Heap

void Delete_Heap(Heap H){
  free(H->Array);
  free(H);
  }

Conlusion:

This article is aiming at helping you to better understand the inside thoughts of binary heap. So some codes here might not be that clean, to help you better understand. If there is more time, I will introduce the 2 applications of heap: Heap Sort and K-th Smallest Element later. Anyway, if there is any question, feel free to comment.
(By the way, if you have better idea of the second Algorithm of Build_Heap, please let me know. I think it could be more clean with a little adjustment)

### 回答1: lower_bound是C++ STL中的一个模版函数,用于在有序的序列中查第一个大于等于给定值的元素的位置。lower_bound函数有两个参数,第一个参数表示要查的序列的起始位置,第二个参数表示要查的序列的结束位置,第三个参数表示要查的值。 下面是一个使用lower_bound函数的简单例子: ```c++ #include <iostream> #include <algorithm> #include <vector> using namespace std; int main() { vector<int> v{1, 2, 3, 3, 5, 6, 7, 8, 9}; int x = 3; auto it = lower_bound(v.begin(), v.end(), x); if (it != v.end() && *it == x) { cout << "Element found at position: " << it - v.begin() << endl; } else { cout << "Element not found!" << endl; } return 0; } ``` 这段代码创建了一个vector v,里面存储了1到9的整数,然后定义了一个整数x,用于查第一个大于等于x的元素的位置。接着使用lower_bound函数在v中查第一个大于等于x的元素,并将结果赋值给一个迭代器it。最后,根据迭代器it是否等于v.end(),以及迭代器指向的元素是否等于x,输出对应的结果。 需要注意的是,如果序列中有多个等于x的元素,lower_bound函数可能返回它们中的任意一个。如果要查最后一个等于x的元素,可以使用upper_bound函数,并将结果减一。如果要同时查第一个和最后一个等于x的元素,可以使用equal_range函数,它返回一个pair对象,pair.first表示第一个等于x的元素的位置,pair.second表示最后一个等于x的元素的位置的后一个位置。 ### 回答2: lower_bound(下界)是C++标准库中的一个算法函数,用于在已排序的容器中查第一个大于等于给定值的元素的位置。 lower_bound函数的原型如下: template <class ForwardIterator, class T> ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& value); 其中,first和last是表示范围的迭代器,value是要查的值。 lower_bound函数返回的是一个迭代器,指向第一个大于等于value的元素。如果容器中不存在此元素,则返回指向容器中第一个大于value的元素的迭代器。 lower_bound函数使用二分查的方法,在已排序的容器中快速定位元素的位置,因此它的时间复杂度为O(log n),其中n为容器的大小。 下面是一个使用lower_bound函数的例子: ```cpp #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> v = {1, 3, 5, 7, 9}; int target = 4; auto it = std::lower_bound(v.begin(), v.end(), target); if (it != v.end()) { std::cout << "到了大于等于" << target << "的元素,位置在索引" << std::distance(v.begin(), it) << std::endl; } else { std::cout << "未到大于等于" << target << "的元素" << std::endl; } return 0; } ``` 在上面的例子中,容器v中包含有序的整数。lower_bound函数被用来查大于等于target的元素。最后,通过distance函数计算出到元素的位置,并输出结果。 总的来说,lower_bound函数是一个在已排序的容器中查第一个大于等于给定值的元素的有用工具。它方便快速地定位元素,提高了程序的效率。 ### 回答3: lower_bound是C++中的标准库函数,主要用于在有序容器(如数组、向量、列表等)中查指定值或插入位置。其返回值是一个迭代器,指向容器中不小于指定值的第一个元素(如果指定值存在,则指向该元素;如果不存在,则指向大于指定值的第一个元素)。以下是lower_bound函数的使用示例: ``` #include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 7, 8, 9}; int target = 7; // 查指定值或插入位置 auto it = std::lower_bound(nums.begin(), nums.end(), target); // 输出迭代器指向的元素 std::cout << "lower_bound of " << target << " is " << *it << std::endl; return 0; } ``` 在上述代码中,我们定义了一个有序向量nums,并指定了要查的目标值target为7。通过调用lower_bound函数,我们可以得到一个迭代器it,它指向容器中不小于target的第一个元素,即7。最后,我们输出该元素的值,即"lower_bound of 7 is 7"。 需要注意的是,lower_bound函数需要在有序容器上使用,否则结果将是未定义的。此外,它还可以接受一个自定义的比较函数作为第三个参数,以实现更灵活的查方式。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值