二叉堆的建立,删除,插入

二叉堆的性质:1.堆中任意节点的值总是不大于(不小于)其子节点的值。2.二叉堆是一颗二叉完全树,一般可以用数组直接表示。如果数组二叉堆下表从0开始, n大于0且为偶数时,该节点是其父节点的右节点。n大于0且为奇数时候,该节点是其父节点的左节点。n的父节点是(n-1)/2。n的两个子节点(如果有的话)2n+1(左),2n+2(右)。

二叉堆的建立

package demo10;
import java.util.ArrayList;


public class test {
	static int[] A = {5 , 2, 4, 1, 3, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14};
	static ArrayList<Integer> B = new ArrayList<Integer>();
	public static void main(String[] args) {
		        //建堆
		for (int i = 0; i < A.length; i++)
			B.add(A[i]);
		for (int i = B.size() / 2 - 1; i >= 0; i--)
			maintain(i);
		for (int i = 0; i < B.size(); i++) // 查看是否建堆成功
		    System.out.println(B.get(i));
	}
	
	 static void maintain(int indx){
	        //堆维护
		 while (indx < B.size() / 2){
	            //只需对根节点维护
	        int temp = optimal(indx * 2 + 1, indx, indx * 2 + 2);
	        if (temp == indx)
	            break;
	        else swap(indx, temp);
	            indx = temp; //对被交换的子节点再进行维护
	     }
	 }
	        
	private static void swap(int i, int j) {
		int temp = B.get(i);
		int temp1 = B.get(j);
		B.set(i, temp1);
		B.set(j, temp);	
	}
			
	static int optimal(int l,int i,int r){
			        //依据性质找出最为优先的
		if (r > B.size() - 1 && B.get(l) > B.get(i))
			i = l;
		if (r <= B.size() - 1) {
			if (B.get(l) > B.get(i))
				i = l;
			if (B.get(r) > B.get(i))
			    i = r;
		}
		return i;
	}
}

二叉堆的删除

private static void Delete() {
		// TODO Auto-generated method stub
		B.set(0, B.get(B.size() - 1)); 
		B.remove(B.size() - 1); // 把第一个位置的数字改成最后一个位置的数字 然后删除最后一个位置
		maintain(0);
	}

二叉堆的插入

 private static void Insert(int i) {
		// TODO Auto-generated method stub
		B.add(i);
		for (int j = B.size() / 2 - 1; j >= 0; j--) // 添加一条数据然后重新建堆就行
			maintain(j);
	}

<think>我们正在分析二叉堆的各种操作时间复杂度。根据引用[1]和[2],自底向上建堆的时间复杂度为O(n)。同时,引用[4]提到了二叉堆的其他操作。首先,二叉堆通常用于实现优先队列。其基本操作包括:1.插入(insert):将一个新元素插入堆中。2.删除最小(或最大,取决于堆的类型)元素(extractMin或extractMax)。3.建立堆(makeHeap):给定一组元素,建立一个二叉堆。4.减小关键字(decreaseKey):将堆中某个元素的值减小(在最小堆中),并调整堆。5.合并(union):合并两个堆(二叉堆通常不支持高效合并,因此这里主要分析前几种操作)。根据引用[1]和[2]的分析:-自底向上建堆(也称为heapify)的时间复杂度为O(n)。这是建立堆的最优方法。例如,给定n个元素,自底向上建堆的过程是从最后一个非叶子节点开始,依次向上调整。调整次数与每个节点的高度有关。引用[2]给出了具体分析:假设堆的高度为h(根节点高度为0),则高度为i的节点有2^i个。每个节点需要调整的次数(即向下交换直到满足堆性质)最多为(h-i)次。因此,总调整次数为:T(n)=sum_{i=0}^{h}[2^i*(h-i)]通过级数求和,可以证明T(n)=O(n)。对于其他操作:1.插入(insert)操作:将新元素放在堆的末尾,然后向上调整(称为swim或percolateup)。因为堆的高度为logn,所以最坏时间复杂度为O(logn)。2.删除最小元素(extractMin):将堆顶元素(最小)取出,然后将堆的最后一个元素放到堆顶,再向下调整(称为sink或percolatedown)。同样,调整次数最多为树的高度,即O(logn)。3.减小关键字(decreaseKey):先找到该元素(二叉堆通常没有索引,所以需要遍历?但实际上,如果我们用数组存储二叉堆,并且知道每个元素的位置,比如在Dijkstra算法中,我们可以用额外数组记录每个节点的位置,那么我们可以直接访问到该元素)然后将其值减小,在最小堆中,这可能导致需要向上调整,因此时间复杂度为O(logn)。但是,注意:在普通的二叉堆实现中,如果没有辅助结构,我们很难在O(1)时间内找到某个特定元素的位置(除非我们记录每个元素的位置,例如使用一个额外的索引数组)。因此,在一般实现中,我们可能不支持快速定位任意元素,从而decreaseKey操作通常需要先查找元素,这需要O(n)时间。然而,在优先队列的应用中(如Dijkstra算法),我们通常会维护一个额外的数组来记录每个节点在堆中的位置,这样我们可以直接访问到该元素,从而使得decreaseKey操作的时间复杂度为O(logn)。引用[4]提到,二叉堆的各个操作时间复杂度如下:操作|二叉堆(无辅助结构)|二叉堆(有辅助结构)--------------|-----------------------|---------------------insert|O(logn)|O(logn)extractMin|O(logn)|O(logn)delete|O(n)|O(logn)(需要先定位,然后通过decreaseKey操作将其变成最小,再extractMin?但实际上,我们可以将待删除元素与最后一个元素交换,然后调整堆,但定位需要O(n))decreaseKey|O(n)(定位)|O(logn)(如果已经知道位置,只需要向上调整)makeHeap|O(n)|O(n)findMin|O(1)|O(1)注意:在普通的二叉堆中,我们通常不实现删除任意节点(delete)和减小任意节点(decreaseKey)的操作,除非我们维护索引。但如果我们维护了索引(例如在堆的每个元素上记录其在数组中的位置,并在每次交换元素时更新索引),那么decreaseKey和delete操作都可以在O(logn)时间内完成。总结二叉堆的时间复杂度:建堆:O(n)插入:O(logn)获取最小值:O(1)删除最小值:O(logn)减小某个元素的键值(已知位置):O(logn)删除任意元素(已知位置):可以先将其键值减小到比最小值还小(在最小堆中,则设成负无穷),然后extractMin,这样需要O(logn)(不包括定位时间)。如果使用索引,则整个操作O(logn)。否则,定位需要O(n),总时间O(n)。此外,引用[5]提到二叉堆是完全二叉树,这保证了树的高度为logn,从而插入删除操作都可以在O(logn)时间内完成。因此,回答用户的问题:二叉堆的时间复杂度分析:-建立堆:O(n)(通过自底向上构建)-插入操作(insert):O(logn)-获取最小(或最大)值:O(1)-删除最小(或最大)值:O(logn)-减小一个元素的值(假设我们维护了元素在堆中的位置):O(logn)-删除一个元素(同样需要知道位置):O(logn)(如果我们维护了索引,否则定位需要O(n))值得注意的是,在实现优先队列时,二叉堆是一个常用的选择,因为它构建效率高且操作复杂度较低。下面,我们还可以用公式推导来简要说明建堆的时间复杂度O(n):设堆的高度为h,总节点数n=2^{h+1}-1(满二叉树的情况)。但实际上,我们使用完全二叉树。从倒数第二层开始调整(高度1到高度h)。设高度i的节点有2^i个(i从0到h-1,高度0为根节点),调整次数为(h-i)次。总调整次数T=sum_{i=0}^{h-1}2^i*(h-i)令j=h-i,则T=sum_{j=1}^{h}2^{h-j}*j=2^h*sum_{j=1}^{h}j/(2^j)因为求和项sum_{j=1}^{\infty}j/(2^j)收敛于2(等比级数求导),所以T=2^h*O(1)=O(2^h)=O(n)(因为n≈2^h,所以2^h=O(n))。因此,自底向上建堆的时间复杂度为O(n)。现在,我们可以根据上述内容给出回答。</think> ### 二叉堆时间复杂度分析 二叉堆二叉堆)是一种基于完全二叉树实现的优先队列数据结构。以下是对其关键操作的时间复杂度分析,结合引用材料进行说明: #### 1. **建堆操作**(`makeheap()`) - **时间复杂度**:$O(n)$ 当给定 $n$ 个键值对时,若采用**自底向上(Bottom-up)建堆法**,算法会从最后一个非叶子节点开始,向前依次对每个节点执行下沉(`sink`)操作。虽然单次下沉操作的最坏复杂度为 $O(\log n)$,但通过数学推导可证明整体复杂度为 $O(n)$[^1]。 **推导示例**: 设堆高度 $h$,节点数为 $n=2^{h+1}-1$。每个高度 $i$ 的节点下沉最多 $(h-i)$ 次,总操作次数为: $$T(n) = \sum_{i=0}^{h} 2^i \cdot (h-i) = O(n)$$ 这是因为级数求和收敛于线性复杂度(引用[1][2])。 #### 2. **插入操作**(`insert(H, x)`) - **时间复杂度**:$O(\log n)$ 新元素插入堆末尾,并通过**上浮(`swim`)操作**调整位置(从叶节点向根节点比较交换)。由于堆高度为 $\log_2 n$,最坏情况下需遍历整条路径(引用[2][4])。 #### 3. **删除最小元素**(`extractmin(H)`) - **时间复杂度**:$O(\log n)$ 移除根节点后,将末尾元素移至根位置,执行**下沉操作**(与子节点比较并交换)。下沉深度不超过树高 $O(\log n)$(引用[4][5])。 #### 4. **减小元素值**(`decreasekey(H, x, k)`) - **时间复杂度**:$O(\log n)$ 若已知元素位置(需维护索引表),减小键值后需执行**上浮操作**调整位置。最坏情况需从叶节点上浮至根节点(引用[4])。 #### 5. **合并堆**(`union(H1, H2)`) - **时间复杂度**:$O(n)$ 二叉堆通常不支持高效合并,需将两个堆元素合并后重建堆,重建复杂度为 $O(n)$(引用[4])。 #### 总结表格 | 操作 | 时间复杂度 | 依赖条件 | |-------------------|------------|-----------------------| | 自底向上建堆 | $O(n)$ | $n$ 个初始元素 | | 插入元素 | $O(\log n)$| 堆大小 $n$ | | 删除最小元素 | $O(\log n)$| 堆大小 $n$ | | 减小元素值 | $O(\log n)$| 已知元素位置 | | 合并两个堆 | $O(n)$ | 堆大小之和 $n_1+n_2$ | **关键性质**:二叉堆的 $O(\log n)$ 操作复杂度源于完全二叉树的高度特性(高度为 $\log_2 n$),所有调整操作均沿树路径进行(引用[2][3][5])。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值