合并方法:
- 先将\(H_2\)合并到\(H_1\)的右子树中,记录每一步被当作根的节点
 
- 从最后一个被当作根的节点开始,向上回溯
- 如果是左偏堆,则根据NPL判断是否需要交换左右子树
 
- 如果是斜堆,则始终交换左右子树
 
 
左偏堆 Leftist Heap
一、基础概念
Full Binary Tree:每个节点都有0/2个儿子
Complete Binary Tree:除了最后一层,每一层都满了
堆Heap:
- 任意一条从根到叶节点的路径,是有序的
 
- 结构性质、顺序性质
 
- 在O(n)的时间内,可以将两个堆合并
 
二、性质
空路径长度null path
length:Npl(X):
- 从节点x开始到叶节点的最短的路径长度
 
- Npl(x) = min{Npl(c) c为x的子节点} + 1
 
左偏堆Leftist Heap:
- 满足顺序性质,但是不满足结构性质(左子树size>右子树size)
 
- 对于左偏堆中的每个节点x,左子节点的空路径长度>=右子节点
 
节点上的数字,表示当前节点的Npl值

定理:如果左偏堆的右路径有r个节点,那么其至少有\(2^r\) - 1个节点 \[
\begin{aligned}
证明:&1.当r=1时,显然成立 \\
     &2.当r>1时,假设k=r-1时,假设成立\\
     &设当前节点为a,左子节点为b,右子节点为c\\
     &对于此时的根节点,因为右路径为r,因此其NPL(c)\geq r-1\\
     &由于左偏堆的性质,此时左子树满足NPL(b)\geq r-1\\
     &由假设得,左右子树的节点个数均\geq 2^{r-1}-1 \\
     &因此该子树的节点个数\geq 2^r-1
\end{aligned}
\]
三、结构体
struct LHeap{     int element;     LHeap* Left;     LHeap* Right;     int NPL; };
  | 
 
四、合并
LHeap* Merge(LHeap* H1, LHeap*H2){     if(H1 == NULL) return H2;     if(H2 == NULL) return H1;     if(H1->element < H2->element) return Merge1(H1,H2);     else return Merge1(H2,H1); } LHeap* Merge1(LHeap* H1, LHeap* H2 ){ 
  	if (H1->Left == NULL){ 		H1->Left = H2;     }else { 		H1->Right = Merge(H1->Right,H2); 		if (H1->Left->Npl < H1->Right->Npl) 			SwapChildren(H1); 		H1->Npl = H1->Right->Npl + 1; 	} 	return H1; }
  | 
 

斜堆 Skew Heap
一、合并
1.1 原理
总是交换左右子树,除非右面路径上的最大节点的子树没有被交换
任意M次连续操作,时间最多为O(M log
N),N=N1+N2


1.2 代码
struct Node {     int data;     Node* lch, * rch;     Node() {};     Node(int x) {         data = x;         lch = rch = NULL;     } }; Node* root; void SwapChildren(Node* H) {     Node* temp = H->lch;     H->lch = H->rch;     H->rch = temp; } Node* Merge(Node* H1, Node* H2) {     if (H1 == NULL)return H2;     if (H2 == NULL)return H1;     if (H1->data > H2->data) {         Node* temp = H1;         H1 = H2;         H2 = temp;     }     if (H1->lch == NULL)         H1->lch = H2;     else {         H1->rch = Merge(H1->rch, H2);         SwapChildren(H1);     } }
  | 
 
二、时间复杂度证明
\[
\begin{aligned}
定义:&如果节点p的右子树的后代至少为p的后代数的一半,则p为重节点\\
     &注意,p节点的后代数包括p本身\\\\     
引理1:&在H_1、H_2右路径中的重节点,在H_3中变为了轻节点\\
     &假设a为重节点,a要和b合并\\
     &(1)a>b,则a的右子树与b合并,并且作为a的左子树,\\
     &合并后左子树比右子树大,a变为轻节点\\
     &(2)a<b,则a与b的右子树合并,设b的右节点为c\\
     &如果a>c,则变为(1)\\
     &如果a<c,则变为(2),继续向下处理\\
\\
引理2:&一棵树,如果其右路径有k个轻节点,则至少有2^k-1个节点\\
     &数学归纳法:当前树的节点数\geq 2(2^k-1)+1 \geq 2^{k+1}-1\\
\\
证明:&设合并前为H_1,H_2,合并后为H_3\\
     &\Phi(H_i)=H_i树中,重节点的个数\\
     &设一共进行了m次递归,则\\
     &\sum_{i=1}^{m} \hat c_i = \sum_{i=1}^{m} c_i
+\Phi(H_3)-\Phi(H_1)-\Phi(H_2) \\
     &设H_i的右路径中,轻节点有l_i个,重节点有h_i个,则\\
     &\sum_{i=1}^{m} c_i \leq l_1 + h_1 + l_2 + h_2 \\
     & 令\Delta \Phi=\Phi(H_3)-\Phi(H_1)-\Phi(H_2),由引理1得 \\
     &\Delta \Phi \leq l_1 + l_2 - (h_1 + h_2) \\
     &\therefore \sum_{i=1}^{m} \hat c_i \leq l_1 + h_1 + l_2 + h_2
+ \Delta \Phi \\
     &\leq l_1 + h_1 + l_2 + h_2 +  l_1 + l_2 - (h_1 + h_2)\\
     &=2(l_1 + l_2),由引理2\\
     &=O(log\ n_1)+O(log\ n_2)
\end{aligned}
\]
例题

右路径上重点、轻点的个数为O(log
N)
斜堆的代码实现
#pragma warning(disable:4996) #include <cstdio> #include <queue> #include <algorithm> #include <ctime> using namespace std; class SkewHeap { 	struct Node { 		int data; 		int size; 		Node* lch, *rch; 		Node() { 			data = 0; 			size = 0; 			lch = rch = NULL; 		} 		Node(int x) { 			data = x; 			size = 1; 			lch = rch = NULL; 		} 		void Update() { 			size = 1; 			if (lch != NULL) size += lch->size; 			if (rch != NULL) size += rch->size; 		} 	}; private: 	static const int maxSize = 100000 + 10; 	Node* root; 	void SwapChildren(Node* H) { 		Node* temp = H->lch; 		H->lch = H->rch; 		H->rch = temp; 	} 	int GetData(Node* u) { 		if (u == NULL)return -1; 		return u->data; 	} 	Node* Merge(Node* H1, Node* H2) { 		if (H1 == NULL)return H2; 		if (H2 == NULL)return H1; 		if (H1->data > H2->data) { 			Node* temp = H1; 			H1 = H2; 			H2 = temp; 		} 		if (H1->lch == NULL) 			H1->lch = H2; 		else { 			H1->rch = Merge(H1->rch, H2); 			SwapChildren(H1); 		} 	} public: 	SkewHeap() { 		root = new Node(); 	} 	void reset() { 		root = new Node(); 	} 	int size() { 		return root->size; 	} 	void Insert(int x) { 		if (root->size == 0) { 			root->data = x; 			root->size = 1; 			return; 		} 		Node* now = new Node(x); 		root = Merge(root, now); 	} 	int top() { 		return root->data; 	} 	void pop() { 		root = Merge(root->lch, root->rch); 	} };
   |