java实现堆的操作(建堆,插入,删除)

本文提供了一个使用Java实现的小顶堆代码示例,包括插入、删除、调整等关键操作,并展示了如何通过这些基本操作实现堆的构建过程。
import java.util.Arrays;

//小顶堆的代码实现

public class Heap {
	// 向下调整,顶端的大值往下调,主要用于删除和建堆,i表示要调整的节点索引,n表示堆的最有一个元素索引
	// 删除时候,i是0,建堆时候i从最后一个节点的父节点依次往前调整
	public static void fixDown(int[] data, int i, int n) {
		int num = data[i];
		int son = i * 2 + 1;
		while (son <= n) {
			if (son + 1 <= n && data[son + 1] < data[son])
				son++;
			if (num < data[son])
				break;
			data[i] = data[son];
			i = son;
			son = i * 2 + 1;
		}
		data[i] = num;
	}

	// 向上调整,小值往上走,用于增加,往上调整不需要制定最上面的索引,肯定是0
	public static void fixUp(int[] data, int n) {
		int num = data[n];
		int father = (n - 1) / 2;
		// data[father] > num是进入循环的基本条件,father减到0就不会减少了
		// 当n等于0时,father=0;进入死循环,所以当n==0时,需要跳出循环
		while (data[father] > num && n != 0) {
			data[n] = data[father];
			n = father;
			father = (n - 1) / 2;
		}
		data[n] = num;
	}

	// 删除,n表示堆的最后一个元素的索引
	public static void delete(int[] data, int n) {
		data[0] = data[n];
		data[n] = -1;
		fixDown(data, 0, n - 1);
	}

	// 增加,i表示要增加的数字,n表示增加位置的索引,是堆的最后一个元素
	public static void insert(int[] data, int num, int n) {
		data[n] = num;
		fixUp(data, n);
	}

	// 建堆,n表示要建堆的最后一个元素的索引
	public static void creat(int[] data, int n) {
		for (int i = (n - 1) / 2; i >= 0; i--)
			fixDown(data, i, n);
	}

	public static void main(String[] args) {
		int[] data = { 15, 13, 1, 5, 20, 12, 8, 9, 11 };
		// 测试建堆
		creat(data, data.length - 1);
		System.out.println(Arrays.toString(data));
		// 测试删除
		delete(data, data.length - 1);
		delete(data, data.length - 2);
		System.out.println(Arrays.toString(data));
		// 测试插入
		insert(data, 3, data.length - 2);
		System.out.println(Arrays.toString(data));

	}

}

<think> 首先,用户的问题是:“我想查找Java中的实现方法 请问Java实现方式”。这表明用户想知道如何在Java实现数据结构。 在我的系统级指令中,我需要遵循规则: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户提供了几个引用: - 引用[1]:讨论了的基本操作如isEmpty()和peekHeap(),并提到核心思想。 - 引用[2]:通过PriorityQueue实现,包括小顶和大顶的示例。 - 引用[3]:展示了一个排序的实现,包括adjust方法来维护属性。 这些引用是站内引用,我需要参考它们来构回答。用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,这是第一个用户查询,所以没有“上一次的问题”。用户说“用户上一次的问题”,但在这个上下文,它是新的。我会假设我需要基于这些引用来回答。 用户的具体需求是“Java实现方式”。通常有两种实现方式: 1. 使用PriorityQueue类(这是Java内置的)。 2. 手动实现,使用数组。 从引用中: - 引用[2]直接提到了PriorityQueue实现。 - 引用[3]展示了手动数组实现排序。 - 引用[1]展示了部分手动实现的方法。 所以,我的回答应该涵盖这两种方式。 回答结构: - 结构清晰,逐步解决问题。 - 使用中文。 - 在末尾生成相关问题。 关键点: 1. **介绍的概念**:是一种特殊的树状数据结构,通常是二叉树,满足属性(最大或最小)。 - 行内数学表达式:如果需要提到属性,例如对于最大,父节点大于等于子节点:$\text{parent} \geq \text{children}$。 2. **实现方式**: - **使用PriorityQueue**:这是最简单的方式。引用[2]提供了代码示例。 - 小顶:`PriorityQueue<Integer> pq = new PriorityQueue<>();` - 大顶:`PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> o2 - o1);` - **手动实现使用数组**:引用[1]和[3]展示了部分。 - 数组表示:根在索引0,左子节点在2*i+1,右子节点在2*i+2。 - 需要实现操作插入删除化(heapify)。 - 引用[3]的排序代码可以作为基础。 3. **核心方法**: - `isEmpty()`:检查是否为空。 - `peek()`:查看顶元素。 - `add()`或`offer()`:插入元素。 - `poll()`:删除顶元素。 - 手动实现时,需要`heapifyUp()`和`heapifyDown()`方法。 4. **代码示例**: - 基于引用,我可以提供简化的代码片段。 - 确保代码是Java的。 5. **引用标识**:在回答中引用段落时,末尾添加引用标识。例如,提到PriorityQueue实现时加[^2],手动实现时加[^3]或[^1]。 组织回答: - **引言**:解释的概念和Java中的实现方式。 - **方式一:使用PriorityQueue**:描述如何使用Java内置类。 - 代码示例。 - **方式二:手动实现使用数组**:描述手动实现。 - 代码示例基础操作。 - **比较和选择**:简要讨论何时使用哪一种。 - **结尾**:总结。 生成相关问题: - 在结尾添加§§相关问题§§,基于用户可能感兴趣的点,如的应用、性能、常见问题等。 确保LaTeX正确: - 可能不需要复杂数学,但如果有,使用$...$。例如,属性:$\text{parent} \geq \text{child}$。 - 排序的时间复杂度:$O(n \log n)$。 最终回答大纲: 1. 介绍。 2. PriorityQueue实现。 3. 手动数组实现。 4. 相关问题。 </think> ### Java实现方式 是一种特殊的完全二叉树数据结构,满足属性:对于最大,$\text{parent} \geq \text{children}$;对于最小,$\text{parent} \leq \text{children}$。在Java中,实现主要有两种方式:使用内置的`PriorityQueue`类或手动基于数组实现。下面我将逐步解释这两种方法,确保结构清晰可靠。 #### 1. **使用内置的`PriorityQueue`类** `PriorityQueue`是Java集合框架的一部分,它默认实现最小,但可通过比较器轻松切换到最大。这种方式简单高效,适合日常开发。 - **最小实现**:默认情况下,`PriorityQueue`按自然排序(升序)存储元素。 ```java // 最小示例 PriorityQueue<Integer> minHeap = new PriorityQueue<>(); minHeap.offer(5); // 插入元素 minHeap.offer(3); System.out.println(minHeap.peek()); // 查看顶元素 (输出:3) minHeap.poll(); // 删除顶元素 (删除3) ``` 核心操作: - `offer()`:插入元素,时间复杂度 $O(\log n)$。 - `peek()`:查看顶元素,时间复杂度 $O(1)$。 - `poll()`:删除顶元素,时间复杂度 $O(\log n)$。 - `isEmpty()`:检查是否为空(如`usedSize == 0`)[^1]。 - **最大实现**:通过自定义比较器实现最大(降序)。 ```java // 最大示例:使用Lambda表达式比较器 PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1); // 或用匿名类 PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; // 降序排序 } }); maxHeap.offer(10); maxHeap.offer(20); System.out.println(maxHeap.peek()); // 输出:20 ``` 这种方式无需手动维护结构,依赖Java内部实现,适合快速开发[^2]。 #### 2. **手动基于数组实现** 手动实现使用数组模拟结构(索引规则:根在0,左子节点在 $2i+1$,右子节点在 $2i+2$)。需实现化(heapify)操作来维护属性。 - **核心步骤**: 1. **初始化数组**:存储元素。 2. **插入元素**:添加到数组末尾,然后上浮(heapify up)调整。 3. **删除顶**:交换顶与末尾元素,删除末尾元素,然后下沉(heapify down)调整。 4. **化方法**:确保属性始终成立(例如,`adjust`方法处理下沉操作)。 - **完整代码示例**: 基于排序实现,扩展为通用结构: ```java public class ArrayHeap { private int[] heap; // 存储的数组 private int size; // 当前大小 public ArrayHeap(int capacity) { heap = new int[capacity]; size = 0; } // 插入元素,并上浮调整 public void offer(int value) { if (size == heap.length) throw new IllegalStateException("已满"); heap[size] = value; heapifyUp(size); // 从新插入位置开始上浮 size++; } // 上浮操作:比较当前节点与父节点(索引为 (i-1)/2) private void heapifyUp(int index) { while (index > 0) { int parentIndex = (index - 1) / 2; if (heap[index] > heap[parentIndex]) { // 最大:子节点大于父节点则交换 swap(index, parentIndex); index = parentIndex; } else { break; } } } // 删除顶元素(返回并移除) public int poll() { if (size == 0) throw new NullElementException("为空"); int root = heap[0]; heap[0] = heap[size - 1]; // 末尾元素移到顶 size--; heapifyDown(0); // 从根开始下沉调整 return root; } // 下沉操作:类似adjust方法[^3] private void heapifyDown(int parent) { int child = 2 * parent + 1; // 左子节点索引 while (child < size) { int rChild = child + 1; // 右子节点索引 // 选择较大子节点(最大) if (rChild < size && heap[rChild] > heap[child]) { child = rChild; } if (heap[parent] < heap[child]) { swap(parent, child); parent = child; child = 2 * child + 1; } else { break; } } } // 查看顶元素 public int peek() { if (size == 0) throw new NullElementException("为空"); return heap[0]; } // 检查是否为空 public boolean isEmpty() { return size == 0; // 直接比较大小 } private void swap(int i, int j) { int temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; } } ``` 手动实现时: - 时间复杂度:插入删除操作均为 $O(\log n)$。 - 优点:完全可控,适合学习原理或优化特定场景[^1][^3]。 #### 3. **实现方式比较与选择** - **`PriorityQueue`**:推荐用于生产环境,内置优化(如动态扩容),代码简洁。 - **手动数组实现**:更适合理解底层原理,或需要定制行为(如固定大小)。 - 核心注意事项: - 操作需维护属性,避免错误(如手动实现中忘记调用heapify)。 - 常用于优先队列、排序等;时间复杂度分析: $O(n)$,操作 $O(\log n)$[^3]。 选择实现方式时,优先考虑`PriorityQueue`,除非有特殊性能或教育需求。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值