Java 堆排序

堆排序 是一种比较复杂的排序,看了算法书上的解释,网上又找了解释(网上算法解释一般是来坑人的,都不详细,且个人观点太强)

虽然不能保证本篇解释到位,力求易于理解,代码简洁精确。

堆排序是对简单选择排序的优化,是一种原地排序的排序过程,只需要一个辅助空间进行交换操作   ,所以其空间复杂度是O(1)

时间复杂度无论是最坏还是最好都是 :O(nlgn)

但是它也是一种不稳定的算法,不适用于较小的数组   ,对于较大的文件比较有效 


算法思想:  先将一个数组看成完全二叉树,

比如待排数组为:55   9  15  36  8  6  11  4  21  47

组成的完全二叉树为:


接着是建初堆过程

就是将此完全二叉树自底向上逐步转化为堆

堆定义树中任意结点满足 它的值大于等于它的左子树且大于等于右子树


// 普及下知识:若数组第一个元素为data[0],    则任意结点data[i] 的做孩子是 data[2i+1],右孩子是
  data[2i+2], 双亲结点是data[(i-1)/2]   ((i-1)/2取余运算)

如何自底向上呢,需要把树中最后一个非叶子节点 data[length/2 - 1](length为数组长度)作为根节点,

把这个结点和它的左子树右子树们看成一个完全二叉树进行重建堆操作

然后把这个小的二叉树转化为堆,一层层向上,最后整体满足堆

建初堆里面有一步是重建堆,so

解释下重建堆:已知一个二叉树

           以k为根节点,和它的左右结点较大者data[m]比较,
   如果大于,则过程结束;
   如果小于,则data[m]覆盖根结点,但是此时根节点还没找到位置,
   根结点继续和data[m]的左右结点较大的一个结点作比较,
   重复上面的过程


以上面的图的二叉树为例:用画图描述建初堆的过程:

第(1)步:找到最后一个非叶子结点即8,它的左子树是47大于8,把47放到8这,然后找原来47的左子树,没有,就只能把8放到原来47的位置



第(2)步:

根结点转移到了36,它左右子树最大值21小于它,二叉树无需变化结束此次重建堆


第(3)步:

根结点转移到了15,大于它的左右子树最大值,二叉树无需变化,结束重建堆


第(4)步,这次是9为根节点,小于47, 47上移,原来47的左子树为8,  9大于8,所以把9放在原先47的位置 


最后到了55,以它为根节点,大于 47,15,二叉树不变,整个建初堆过程结束,最后产生的二叉树是不是满足堆的定义了 ?

答案是肯定的


建初堆之后二叉树仍然不是完全有序,但是最大的数已经出来了,就是根节点,当然我这里给出的数组第一个数就是最大值,这是我的失误,

但是分析一下就知道我们通过自底向上重建堆,最顶上的数一定是最大的

把最大的数和二叉树最后一个数交换,然后把它踢出二叉树,不管它,剩下的仍看成一棵完全二叉树,对这棵二叉树进行重建堆操作,又可以把最大的数筛出来,

重复上述过程,最后就得到一个完全有序的二叉树,当然这里得到的二叉树 根节点向下是从小到大排列的


上代码时刻:

public class Test {
	private static int data[];

	
	public static void Rebuild(int k, int m) {
		int x = data[k]; // x 表示此时的根节点
		int i = k;
		int j = 2 * i + 1; // 左子树的位置  
		boolean finishend = false;
		while (j < m && !finishend) { // 每次循环的过程是根节点和它左右节点最大的那个进行比较
			if (j < (m - 1) && data[j] < data[j + 1]) { // 左子树小于右子树,就用右子树和x进行比较,总是要把最大的选到上面
				j++; // 树向后移动
			}
			if (x >= data[j]) {          // 堆顶元素大于左子树
				finishend = true;    // 本轮执行完跳出while循环
			} else {                     // 如果左或右子树大于根节点,那么我们现在还是不知道这个根节点x该放哪才能满足堆的定义
				data[i] = data[j];   // 如果x小于 就交换,每次交换的不是根节点 是上一次较大的子树的位置
				i = j;               // i表示这次比较的左(右)子树
				j = 2 * i + 1;       // j变为 j的左子树
			}
		}
		data[i] = x;  // 循环结束,i的位置即是最适合x 的位置 ,放进去,重建堆OK
	}

	/**
	 * 建初堆过程:可以将任意连续的序列看成完全二叉树,所以从最后一个非叶子结点开始进行重建堆操作 ,逐步向根节点靠近,最后得到的树整体满足堆定义
	 * 
	 * 建初堆的时间复杂度为O(n)
	 * @param length
	 *            数组总长度
	 */
	public static void initHeap(int length) {

		for (int i = (length / 2 - 1); i >= 0; i--) { // 最后一个非叶子结点位于 --(length/2
														// -1)-- 的位置
			Rebuild(i, length);
		}
	}

	/**
	 * 堆排序
	 */
	public static void HeapSort() {
		initHeap(10);

		for (int i = 9; i > 0; i--) {
			int b = data[0];   // *------
			data[0] = data[i]; // 根结点(总是当前完全二叉树的最大值)与当前待排序的完全二叉树最后一个元素交换,然后把最后一个元素剔除出二叉树,进入重建堆过程
			                   // 建初堆已经把二叉树变成完整的堆了 现在只需要一次重建堆就可以变成完整的堆了 
			data[i] = b;       // -----*
			Rebuild(0, i);
		}
	}

	public static void PrintData() {
		for (int i = 0; i < 10; i++) {
			System.out.print(" " + data[i]);
		}
		System.out.println("");
	}

	public static void main(String[] args) {
		data = new int[10];
		Scanner scanner = new Scanner(System.in);
		System.out.println("输入十个数字进行-堆排序-");
		for (int m = 0; m < 10; m++) {
			data[m] = scanner.nextInt();
		}
		HeapSort();
		PrintData();
		scanner.close();
	}

}


代码写的真心累,有不好的地方,多多指教







  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java 中的堆排序是一种比较快速的排序算法,它的时间复杂度为 O(nlogn),其中 n 是要排序的数组的长度。它的基本思想是:将数组中的元素构建成一个大根,然后依次将顶元素(也就是最大的元素)与末尾元素交换,再重新构建大根,以此类推,直到整个数组有序。 以下是 Java 中的堆排序代码: ``` public void heapSort(int[] arr) { // 构建大根 for (int i = arr.length / 2 - 1; i >= 0; i--) { adjustHeap(arr, i, arr.length); } // 堆排序 for (int j = arr.length - 1; j > 0; j--) { // 将顶元素与末尾元素交换 swap(arr, 0, j); // 重新调整为大根 adjustHeap(arr, 0, j); } } /** * 调整大根 */ public void adjustHeap(int[] arr, int i, int len) { // 将当前节点的值保存到临时变量中 int temp = arr[i]; // k 指向当前节点的左子节点 for (int k = i * 2 + 1; k < len; k = k * 2 + 1) { // 如果当前节点的右子节点存在,并且大于左子节点,则指向右子节点 if (k + 1 < len && arr[k] < arr[k + 1]) { k++; } // 如果子节点大于父节点,则交换 if (arr[k] > temp) { arr[i] = arr[k]; i = k; } ### 回答2: 堆排序(Heap Sort)是一种通过构建最大或最小来实现的排序算法,它的平均时间复杂度为O(nlogn)。 下面是Java语言实现堆排序的代码: ``` public class HeapSort { public static void heapSort(int arr[]) { int n = arr.length; // 从最后一个非叶子节点开始构建最大 for (int i = n / 2 - 1; i >= 0; i--) { heapify(arr, n, i); } // 依次将最大的元素交换到数组末尾,并重新构建最大 for (int i = n - 1; i >= 0; i--) { int temp = arr[0]; arr[0] = arr[i]; arr[i] = temp; heapify(arr, i, 0); } } // 调整以root为根节点的子树成为最大 public static void heapify(int arr[], int size, int root) { int largest = root; // 初始化最大元素为根节点 int left = 2 * root + 1; // 左子节点的索引 int right = 2 * root + 2; // 右子节点的索引 // 如果左子节点大于根节点,将最大元素索引更新为左子节点 if (left < size && arr[left] > arr[largest]) { largest = left; } // 如果右子节点大于根节点,将最大元素索引更新为右子节点 if (right < size && arr[right] > arr[largest]) { largest = right; } // 如果最大元素不是根节点,则交换根节点和最大元素,并继续调整子树 if (largest != root) { int temp = arr[root]; arr[root] = arr[largest]; arr[largest] = temp; heapify(arr, size, largest); } } public static void main(String args[]) { int arr[] = {56, 23, 12, 78, 45, 10, 45}; heapSort(arr); System.out.println("排序后的数组:"); for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } } ``` 以上代码定义了一个`HeapSort`类,其中包含了一个`heapSort`方法和一个`heapify`方法。`heapSort`方法用于执行堆排序算法,而`heapify`方法用于调整以某个节点为根节点的子树,使其成为最大。 在`heapSort`方法中,首先从最后一个非叶子节点开始构建最大。然后,依次将最大的元素与数组末尾交换,并重新构建最大。最后,输出排序后的数组。 在`main`方法中,我们定义了一个待排序的数组,并调用`heapSort`方法对其进行排序。最后,输出排序后的数组。 以上是Java中实现堆排序的代码。 ### 回答3: Java 堆排序是一种使用数据结构进行排序的算法。下面是一个简单的Java实现: ```java import java.util.Arrays; public class HeapSort { public static void heapSort(int[] array) { int n = array.length; // 构建最大 for (int i = n / 2 - 1; i >= 0; i--) heapify(array, n, i); // 从顶开始不断将最大元素移至数组末尾 for (int i = n - 1; i >= 0; i--) { // 交换顶元素与当前末尾元素 int temp = array[0]; array[0] = array[i]; array[i] = temp; // 对剩余元素重新构建最大 heapify(array, i, 0); } } // 将数组中的元素构建为最大 public static void heapify(int[] array, int n, int i) { int largest = i; // 初始化顶元素为最大值 int left = 2 * i + 1; // 左子节点的索引位置 int right = 2 * i + 2; // 右子节点的索引位置 // 如果左子节点大于根节点,将largest设置为左子节点 if (left < n && array[left] > array[largest]) largest = left; // 如果右子节点大于当前最大值,将largest设置为右子节点 if (right < n && array[right] > array[largest]) largest = right; // 如果largest不是根节点,将largest与根节点交换,并继续构建最大 if (largest != i) { int swap = array[i]; array[i] = array[largest]; array[largest] = swap; heapify(array, n, largest); } } public static void main(String[] args) { int[] array = {10, 7, 8, 9, 1, 5}; heapSort(array); System.out.println(Arrays.toString(array)); } } ``` 上述代码实现了堆排序。首先,它使用构建最大的函数 `heapify` 将输入数组构建为最大。然后,在每次循环中,将顶元素(即最大值)与当前数组末尾元素交换,然后对剩余元素重新构建最大。通过这样的迭代过程,最终得到一个有序的数组。 在主函数中,我创建了一个测试数组并调用堆排序函数 `heapSort`。最后,通过使用 `Arrays.toString()` 函数将排序结果打印出来。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值