算法与数据体系课笔记之-6.堆结构与堆排序

思维导图链接

算法与数据结构思维导图

参考左程云算法课程

6.堆结构与堆排序几个经典题目分析 总览

在这里插入图片描述

题1:堆结构

题目描述:
  • 用自定义数组实现堆结构

  • 题目扩展:堆中某个位置数边了,不知变大还是变小了
    如何恢复成原堆结构

    • 1.对改位置调用siftUp方法

      2.对改位置调用siftDown方法

代码实现
package class06;

import class03.Code04_ArraytoStackAndQueue.Array;

public class Code02_Heap{
	public static class MaxHeap <E extends Comparable <E>> { 
		private Array<E> data;
	
		public MaxHeap(int capacity) {
			data = new Array<>(capacity);
		}
		
		public MaxHeap() {
			data = new Array<>();
		}
		
	//	- 返回堆中的元素个数
		public int getSize() {
			return data.getSize();
		}
		
	//	- 返回一个布尔值,表示堆中是否为空
		public boolean isEmpty() {
			return data.isEmpty();
		}
		
	//	- 返回完全二叉树的数组表示中,一个索引所表示的父亲节点的索引
		private int parent(int index) {
			return (index - 1) / 2;
		}
		
	//	- 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
		private int leftChild(int index) {	
			return 2 * index + 1;
		}
		
	//	- 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
		private int rightChild(int index) {
			return 2 * index + 2;
		}
		
		// 向堆中添加元素,向上heapify过程,同时维护堆的性质
		public void add(E e) {
			data.addLast(e);
			// 因为调用的是辅助类动态数组,size已经在Array中维护了
			siftUp(data.getSize() - 1);
	
		}
	
		private void siftUp(int k) {
			// 往上heapify过程,主要k不能为根节点
			while(k > 0 && data.get(k).compareTo(data.get(parent(k))) > 0) {
				data.swap(k, parent(k));
				k = parent(k);
			}
			
		}
		
		// 查找堆中最大元素
		public E peek() {
			if(data.isEmpty()) {
				throw new IllegalArgumentException("can't findMax from an empty heap");		
			}
			return data.get(0);
		}
		
		// 向堆中删除最大元素
		public E poll() {
			E ret = peek();
			
			// 删除的逻辑是,先将最大元素用新变量保存,然后与最后一个元素交换,将最后一个元素删除,再堆化
			data.swap(0, data.getSize() - 1);
			data.removeLast();
			siftDown(data, 0, data.getSize()); // 向下堆化
			
			return ret;
		}
		// 向下heapfy,其中,heapSize为堆容器大小
		private void siftDown(Array<E> data, int k, int heapSize) {
			while(leftChild(k)< heapSize) {
				// 先比较左右子节点,取较大节点的索引
				int j = leftChild(k);
				if(j + 1 < heapSize && data.get(j + 1).compareTo(data.get(j)) > 0) j ++;
				// 处理父子节点,保证根节点最大
				if(data.get(j).compareTo(data.get(k)) > 0) {
					data.swap(j, k);
				}
				// 继续往下堆化,将改动的子节点作为根节点循环进行,另一个子节点完整不变
				k = j;   
			}
		}
	
	}
}

题2:堆排序

题目描述:
  • 用定义的堆实现排序
代码实现
package class06;

public class Code03_HeapSort {
	public static class HeapSort {
		public static <E extends Comparable<E>> void sort(E[] arr) {
			if (arr == null || arr.length <= 2)
				return;
	
			// 1.原地堆化,空间复杂度为O(1)
			// 1.1 逐个添加进行堆化,时间O(n)
//			for(int i = 0; i < arr.length; i ++) {
//				siftUp(arr, i);
//			}
			
			//1.2 一次性传入整个数组,将整个数组进行堆化
            //找非叶子节点,自下往上找;找到所有非叶子节点后,再自上往下交换进行堆化
	        //i为所有非叶子节点,故如果i为右节点,下一步要处理以左节点为根节点的树
            //即所有非叶子节点排列规律是从右往左,从下往上,层Z字行排列
			for(int i = parent(arr.length - 1); i >= 0; i --) {
				siftDown(arr, i, arr.length);
			}
						
			// 排序
			for(int i = arr.length - 1; i > 0; i --) {
				swap(arr, 0, i);	// 先交换,将堆顶元素放到指定位置i,[i,n)排好序
				siftDown(arr, 0, i);	// 对新的大小的堆结构进行堆化,[i,n)数不参与
			}
		}
	
		// 自下往上堆化
		private static <E extends Comparable<E>>void siftUp(E[] arr, int k) {
			while(k > 0 && arr[k].compareTo(arr[parent(k)]) > 0) {
				swap(arr, k, parent(k));
				k = parent(k);
			}		
		}
	
		private static int parent(int k) {
			return (k - 1) / 2;
		}

		/**
		 * 自上向下堆化实现
		 * 
		 * @param <E>
		 * @param arr ,最大堆结构
		 * @param k,  向下堆化的起始根节点
		 * @param heapSize,  最大堆容器的大小
		 */
		private static <E extends Comparable<E>> void siftDown(E[] arr, int k, int heapSize) {
			// n是新堆结构的大小,不能用data.length,data数组中一部分排好序的数不参与堆化
			while (2 * k + 1 < heapSize) {
				int j = 2 * k + 1; // 左子节点索引
				// 判断左右子节点哪个大
				if (j + 1 < heapSize && arr[j + 1].compareTo(arr[j]) > 0) {
					j++; // 如果右子节点大,j表示右节点
				}
				if (arr[k].compareTo(arr[j]) < 0) {
					swap(arr, k, j); // 以k为根节点的子二叉树,根节点最大
				}
				// 继续往下堆化,将改动的子节点作为根节点循环进行,另一个子节点完整不变
				k = j;
			}
		}
	
		private static <E> void swap(E[] data, int k, int j) {
			E t = data[k];
			data[k] = data[j];
			data[j] = t;
		}
	}
}

题3:2. 对几乎有序的数组进行排序

题目描述:
  • 已知一个几乎有序的数组。几乎有序是指,如果把数组排好顺序的话,
    每个元素移动的距离一定不超过k,并且k相对于数组长度来说是比较小的。

    请选择一个合适的排序策略,对这个数组进行排序。

代码实现
public static void sortedArrDistanceLessK(int[] arr, int k) {
		if (k == 0)
			return;

		// 1. 建立k+1个元素的小根堆
		PriorityQueue<Integer> heap = new PriorityQueue<Integer>();
		int index = 0; // 堆的边界位置[0,index),左闭右开
		for (; index <= Math.min(arr.length - 1, k); index++) {
			heap.add(arr[index]);
		}

		// 2. 弹出和弹入,依次从左往右排好序
		// 堆中在i位置的元素一定包括在堆中,且是堆顶元素
		int i = 0; // 数组中将要排序的位置
		for (; i < arr.length; i++) {
			if (index < arr.length) {
				arr[i] = heap.poll();   // 弹出最小放到i位置
				heap.add(arr[index++]); // 向右推进,继续向堆中弹入新元素
			} else {
				arr[i] = heap.poll(); // 弹出最后k个元素
			}
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值