JAVA代码实现堆排序

堆排序

最近学习了堆排序算法。堆排序是一种选择排序,是不稳定的排序,其最坏、平均、最优时间复杂度都为O(nlogn)。堆排序逻辑上是利用完全二叉树对数进行移动,实际是对数组进行操作。

堆排序算法的基本思路:

  • 根据升序或降序将数组逻辑上转换成大顶堆或小顶堆;
  • 将根节点的数和最后的数交换位置,“沉”在数组的最后,然后继续调整成大顶堆/小顶堆;
  • 反复执行步骤2,直至结束。

看了上述步骤之后可能会有这两个问题:1.什么是大顶堆和小顶堆?2.为什么对数组操作,会跟二叉树有关系?

  • 首先来解释大顶堆和小顶堆的问题:
    大顶堆:对一颗二叉树而言,如果每个节点的值都大于或等于其左右子节点的值,则称为大顶堆;
    小顶堆:对一颗二叉树而言,如果每个节点的值都小于或等于其左右子节点的值,则称为小顶堆;
    注意:没有要求节点的左右子节点值的大小关系
  • 为什么对数组操作,会跟二叉树有关系?
    堆排序实际上是在对数组进行操作,但是其逻辑思想是利用完全二叉树来移动数据。首先将一个无序数组按顺序排列成一棵完全二叉树,例如数组arr:{8,6,7,4,3,2,5,1,9},排列成一棵完全二叉树:
    在这里插入图片描述
  • 然后从最后的非叶子节点开始,将这棵完全二叉树调整成大顶堆;

在这里插入图片描述

  • 再将根节点和最后的节点互换位置,将最大的数放到了数组的最后:
    在这里插入图片描述
  • 将9放到数组末端之后,将不再参与下一轮排序。重新将剩余的数调整为大顶堆。依次类推,直至结束。
    在这里插入图片描述
    其代码实现如下:
package com.tree.heapSort;

import java.util.Arrays;

/*
 * 堆排序
 * 将数组从小到大排序为例:
 * 说明:逻辑上是将数组按顺序排列成一个完全二叉树进行操作,实际上是对数组的操作
 * 1.构建大顶堆
 * 2.将最开始的节点和末尾节点互换位置,将最大元素“沉”到最后,也就是放在数组的最末尾,然后继续反复调整+交换
 */
public class HeapSortDemo2 {

	public static void main(String[] args) {
		// 生成一个无序的数组
		int[] arr = new int[10];
		int num = 0;
		int count = 0;
		boolean flag = false;
		while (count < arr.length) {
			num = (int) (Math.random() * 100);
			for (int i = 0; i < arr.length; i++) {
				if (arr[i] == num) {
					flag = true;
					break;
				}
			}
			if (!flag) {
				arr[count++] = num;
			} else {
				flag = false;
			}
		}
		System.out.println("原数组为:" + Arrays.toString(arr));

		// 堆排序
		heapSort(arr);

	}

	/**
	 * 堆排序
	 * 
	 * @param arr
	 */
	public static void heapSort(int[] arr) {
		// 1.将数组转换成大顶堆
		// 从最后的非叶子节点开始自下向上调整
		for (int i = arr.length / 2 - 1; i >= 0; i--) {
			adjustHeap(arr, i, arr.length);
		}
		System.out.println("数组转换成大顶堆后:" + Arrays.toString(arr));

		// 2.将顶端的元素与末尾元素置换位置,然后再继续调整成大顶堆
		int temp = 0;
		int count = 0;
		for (int j = arr.length - 1; j > 0; j--) {
			temp = arr[j];
			arr[j] = arr[0];
			arr[0] = temp;
			// 交换后较小的元素又被放在了根节点,需要重新调整成大顶堆
			adjustHeap(arr, 0, j);
			System.out.println("第" + (++count) + "轮堆排序后:" + Arrays.toString(arr));
		}
		System.out.println("堆排序最终结果:" + Arrays.toString(arr));
	}

	/**
	 * 将数组调整为大顶堆
	 * 
	 * @param arr    带调整的数组
	 * @param i      每次需要调整子树的父节点下标
	 * @param length 每次需要调整的元素个数,递减
	 */
	public static void adjustHeap(int[] arr, int i, int length) {
		// 将待调整的元素临时保存
		int temp = arr[i];
		// 开始调整
		// 从下标i开始,比较其左右子节点的值是否大于arr[i],如果大于,就和arr[i]互换位置,构建局部大顶堆
		for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
			// k表示下标i元素的左子节点,k+1表示其右子节点,首先判将k移动到较大值的位置
			if (k + 1 < length && arr[k] < arr[k + 1]) {// 说明右子节点大于左子节点
				k++;// 移动到右子节点
			}
			// 判断下标k的值是否大于待调整元素,如果大于,就arr[k]=arr[i]
			if (arr[k] > temp) {
				arr[i] = arr[k];
				// 赋值完毕后,要将i移动到k的位置,下一轮比较该位置和其左右子节点的大小
				i = k;
			} else {
				break;
			}
		}
		// 循环结束时,说明已经将最初下标为i的父节点的树调整为大顶堆,并且此时i的位置已经移动
		// 需要将temp填在arr[i]的位置
		arr[i] = temp;
	}

}

如有错误之处,还望指出,定会及时改正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章