堆排序及手写堆的基本操作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);
}
}
}
手写堆的基本功能代码:
模拟堆
维护一个集合,初始时集合为空,支持如下几种操作:
I x
,插入一个数 x;PM
,输出当前集合中的最小值;DM
,删除当前集合中的最小值(数据保证此时的最小值唯一);D k
,删除第 k 个插入的数;C k x
,修改第 k 个插入的数,将其变为 x;
现在要进行 N 次操作,对于所有第 22 个操作,输出当前集合的最小值。
输入格式
第一行包含整数 N。
接下来 N 行,每行包含一个操作指令,操作指令为 I x
,PM
,DM
,D 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代码的主要内容,如果未能解决你的问题,请参考以下文章