堆排序及手写堆的基本操作java代码

Posted 知道什么是码怪吗?

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆排序及手写堆的基本操作java代码相关的知识,希望对你有一定的参考价值。

基础知识:

大根堆:父节点大于左右子节点

小根堆:父节点小于左右子节点

父节点下标x,左子节点下标2x,右子节点下标2x+1

堆排序代码(重点是Down()函数):

import java.util.Scanner;

public class Main {
	static int[] nums = new int[100010];
	static int n;
	// 小根堆构造:x的左节点下标2x,右节点下标2x+1
	public static void Down(int x) {
		int t = x;
		if (2 * x <= n && nums[2 * x] < nums[x]) {// 如果左节点存在且左节点小于父节点
			t = 2 * x;// 下标移动到左节点
		}
		if (2 * x + 1 <= n && nums[2 * x + 1] < nums[t]) {// 如果右节点存在且右节点小于父节点和左节点中较小的数
			t = 2 * x + 1;// 下标移动到右节点
		}
		if (t != x) {// t!=x说明父节点不是最小的,将子节点和父节点交换
			int temp = nums[t];
			nums[t] = nums[x];
			nums[x] = temp;
			Down(t);// 继续对这个节点进行移动
		}
	}

	public static void main(String args[]) {
		Scanner input = new Scanner(System.in);
		n = input.nextInt();//n表示需要排序的个数
		for (int i = 1; i <= n; i++) {
			nums[i] = input.nextInt();
		}
		for (int i = n; i > 0; i--)// 可以将i的初始值定为n/2
			Down(i);
		int count = n;
		for (int i = 1; i <= count; i++) {
			//下面这段代码用于查看父节点和左右子节点的值
//			System.out.printf("nums[%d]=%d  ", i, nums[i]);
//			if (2 * i <= n)
//				System.out.print("nums[2*i]= " + nums[2 * i] + " ");
//			if ((2 * i + 1) <= n)
//				System.out.print("nums[2*i+1]= " + nums[2 * i + 1] + " ");
//			System.out.println();
			System.out.print(nums[1] + " ");
			nums[1] = nums[n--];//每次输出完最小的那个数,然后更新根节点刷新最小的值
			Down(1);
		}
	}
}

手写堆的基本功能代码:

模拟堆

维护一个集合,初始时集合为空,支持如下几种操作:

  1. I x,插入一个数 x;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k 个插入的数;
  5. C k x,修改第 k 个插入的数,将其变为 x;

现在要进行 N 次操作,对于所有第 22 个操作,输出当前集合的最小值。

输入格式

第一行包含整数 N。

接下来 N 行,每行包含一个操作指令,操作指令为 I xPMDMD k 或 C k x 中的一种。

输出格式

对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。

每个结果占一行。

数据范围

1≤N≤10^5
−10^9≤x≤10^9
数据保证合法。

输入样例:

8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM

输出样例:

-10
6

代码(重点是Down()和Up()两个函数,难点是Index数组和ToIndex数组的含义): 

import java.util.*;

public class Main {
	static int[] nums = new int[100010];// 存放数
	static int[] Index = new int[100010];// 记录插入顺序,Index[i]表示第i个插入的数在nums中的下标
	static int[] ToIndex = new int[100010];// 存放“记录插入顺序”的数的下标,ToIndex[i]表示在nums中下标为i的数在Index中对应的下标
	static int count = 0;// 记录实际存放数的个数

	public static void Swap(int a, int b) {
		int temp = nums[a];// 交换值
		nums[a] = nums[b];
		nums[b] = temp;

		temp = Index[ToIndex[a]];// 交换数对应的插入顺序
		Index[ToIndex[a]] = Index[ToIndex[b]];
		Index[ToIndex[b]] = temp;

		temp = ToIndex[a];// 交换反向找到插入顺序的数
		ToIndex[a] = ToIndex[b];
		ToIndex[b] = temp;
	}

	public static void Down(int x) {
		int t = x;
		if (2 * x <= count && nums[2 * x] < nums[t])
			t = 2 * x;
		if (2 * x + 1 <= count && nums[2 * x + 1] < nums[t])
			t = 2 * x + 1;
		if (t != x) {
			Swap(t, x);
			Down(t);
		}
	}

	public static void Up(int x) {
		while (x / 2 != 0 && nums[x] < nums[x / 2]) {// 如果x存在父节点且父节点大于当前节点
			Swap(x, x / 2);
			x = x / 2;// 跑到父节点的位置继续执行
		}
	}

	public static void main(String args[]) {
		Scanner input = new Scanner(System.in);
		int time = input.nextInt();
		int m = 0;
		for (int i = 0; i < time; i++) {
			String str = input.next();
			if (str.equals("I")) {
				int x = input.nextInt();
				count++;
				m++;
				Index[m] = count;// 存放这个数对应的插入顺序
				ToIndex[count] = m;// 存放反向查找到这个数对应的插入顺序的下标
				nums[count] = x;
				Up(count);
			}
			if (str.equals("PM")) {
				System.out.println(nums[1]);
			}
			if (str.equals("DM")) {
				Swap(1, count);// 将第一个数和最后一个数交换
				count--;// 数的总数减一
				Down(1);
			}
			if (str.equals("D")) {
				int k = input.nextInt();
				k = Index[k];// 获取第k个插入的数在nums中的下标
				Swap(k, count);// 和末尾元素交换
				count--;
				Up(k);
				Down(k);

			}
			if (str.equals("C")) {
				int k = input.nextInt();
				int x = input.nextInt();
				k = Index[k];
				nums[k] = x;// 更改值
				Up(k);
				Down(k);
			}
		}
	}
}

以上是关于堆排序及手写堆的基本操作java代码的主要内容,如果未能解决你的问题,请参考以下文章

堆的实现及应用

数据结构:堆手写

c++ 手写堆 (包括建堆排序添加元素删除元素)

[ 数据结构 -- 手撕排序算法第七篇 ] 堆及其堆排序

数据结构 Java数据结构 ---- 堆(优先级队列)

数据结构(12)---二叉树之顺序结构