堆的插入和删除
堆的定义
堆就是一棵可以自我平衡的完全二叉树
优先队列的底层数据结构就是堆,实现和堆基本一样
由于堆存储在下标从0开始计数的数组中,因此,在堆中给定下标为i的结点时:
如果 i = 0,结点 i 是根结点,无父结点;否则结点 i 的父结点为结点 [(i - 2) / 2]
如果 2i + 1 > n - 1,则结点 i 无左子女;否则结点 i 的左子女为结点 2i + 1
如果 2i + 2 > n - 1,则结点 i 无右结点;否则结点 i 的右子女为结点 2i + 2
堆特性
1、每个父节点都大于等于其所有后代结点。
2、堆的根节点含有堆中最大(或者最小)的对象。
3、堆中以任意节点为根节点的子树仍然是堆。
堆的分类
最大堆(大根堆,大顶堆):根节点对象是最大对象的堆
最小堆(小根堆,小顶堆):根节点对象是最小对象的堆
堆的插入操作(以最小堆为例,将一个数组变成堆)
堆的插入操作是自底向上进行的,每次从堆的最后一个结点开始插入(将插入的值放入完全二叉树的最后一个结点),为了维持堆的性质,还要从插入结点开始依次往前递归,去维持堆的三个特性
①我们现在有一个长度为n的数组a,里边的元素都没有顺序,把这个数组变成最小堆
②然后我们新建一个完全二叉树b,按数组下标0到n-1的顺序依次放入完全二叉树,也就是说现在b[0]=a[0],b[1]=a[1]......。
③取插入结点的父节点,比较父节点的值和插入结点的值,将较小的值交换到父节点位置。
④再以父节点为当前结点,重复上一步操作,知道遇到父节点比插入结点值小,就可以结束递归0
//建堆时间复杂度O(n)
int Heap[MAX_SIZE]; int Cur = 0; void Insert(int val) //插入 { Heap[++Cur] = val; //新元素加入堆 int Temp_Cur = Cur; //加入元素后维持堆的性质,一直递归到符合堆性质的结点 while (Temp_Cur > 1) { //找父节点 int Root = Temp_Cur / 2; //父节点比子节点大-->小根堆 if (Heap[Root] > val) swap(Heap[Root], Heap[Temp_Cur]); //交换 //找到符合性质的堆就退出 else break; //更新当前结点位置 Temp_Cur = Root; } }
堆的删除操作
堆的删除操作是只能删除堆顶的元素,删除堆顶元素之后,把堆的最后一个元素放到堆顶,然后不断向下维护堆的特性1、2、3
//删除堆顶,接着维护堆的性质的时间复杂度为O(lgn)
int Pop() //删除 { if (Cur == 0) //堆空 return -99999999; //保存堆顶 int Temp_Top = Heap[1]; //将末尾结点提到树根 Heap[1] = Heap[Cur]; int Root = 1; //从堆顶往下维护堆的特性,一直处理到叶子结点-->小根堆 while (2 * Root < Cur) { //左儿子 int L_Child = Root * 2; //右儿子 int R_Child = Root * 2 + 1; //只有左节点或者左节点比右节点的值小 if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) { //让左结点和根节点比较,把较小值交换到父节点位置上 if (Heap[Root] > Heap[L_Child]) { swap(Heap[Root], Heap[L_Child]); //更新位置 Root = L_Child; } else break; } //左结点值比右结点值大,比较父节点和右节点 else { if (Heap[Root] > Heap[R_Child]) { swap(Heap[Root], Heap[R_Child]); Root = R_Child; } else break; } } Cur--; //返回删除的堆顶元素 return Temp_Top; }
堆的排序操作
//堆排序
//排序的空间复杂度为O(1)
int temp[MAX_SIZE]; int k = 0; void quick_sort(int n) { for (int i = 1; i <= n; i++) { temp[k++] = Pop(); } }
完整代码
#include<iostream> #include<string> #define MAX_SIZE 100005 using namespace std; int Heap[MAX_SIZE]; int Cur = 0; void Insert(int val) //插入 { Heap[++Cur] = val; //新元素加入堆 int Temp_Cur = Cur; //加入元素后维持堆的性质,一直递归到符合堆性质的结点 while (Temp_Cur > 1) { //找父节点 int Root = Temp_Cur / 2; //父节点比子节点大-->小根堆 if (Heap[Root] > val) swap(Heap[Root], Heap[Temp_Cur]); //交换 //找到符合性质的堆就退出 else break; //更新当前结点位置 Temp_Cur = Root; } } int Pop() //删除 { if (Cur == 0) //堆空 return -99999999; //保存堆顶 int Temp_Top = Heap[1]; //将末尾结点提到树根 Heap[1] = Heap[Cur]; int Root = 1; //从堆顶往下维护堆的特性,一直处理到叶子结点-->小根堆 while (2 * Root < Cur) { //左儿子 int L_Child = Root * 2; //右儿子 int R_Child = Root * 2 + 1; //只有左节点或者左节点比右节点的值小 if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) { //让左结点和根节点比较,把较小值交换到父节点位置上 if (Heap[Root] > Heap[L_Child]) { swap(Heap[Root], Heap[L_Child]); //更新位置 Root = L_Child; } else break; } //左结点值比右结点值大,比较父节点和右节点 else { if (Heap[Root] > Heap[R_Child]) { swap(Heap[Root], Heap[R_Child]); Root = R_Child; } else break; } } Cur--; //返回删除的堆顶元素 return Temp_Top; } //堆排序 int temp[MAX_SIZE]; int k = 0; void quick_sort(int n) { for (int i = 1; i <= n; i++) { temp[k++] = Pop(); } } int main() { int n, x; cin >> n; for (int i = 0; i < n; i++) { cin >> x; Insert(x); } quick_sort(n); for (int i = 0; i <k; i++) { cout << temp[i] << ' '; } cout << endl; system("pause"); return 0; }
等风起的那一天,我已准备好一切