Algorithm——堆排序算法(八)

Algorithm——堆排序算法


在分析堆排序之前,我们先来回顾一些基本概念:在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。而完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树(即叶子结点都是从左到右依次排布)。

堆是一个数组,它可以被看成一个近似的完全二叉树。树的每一个节点对应数组中的一个元素。除了最底层外,该树是完全满的,而且是从左向右填充。表示堆的数组A包括两个属性:

  • A.length:给出数组元素的个数
  • A.heap-size:表示有多少个堆元素存储在该数组中。也就是说,虽然A[1...A.length]可能都存有数据,但只有A[1...A.heap-size]中存放的是堆的有效数据,这里0=<A.heap-size=<A.length;在实际算法实现中,使用heap-size属性可以剔除掉已经交换得到的最大值元素。

同时堆也分为最大堆和最小堆。在最大堆中,最大堆性质是指除了根以外的所以节点i都要满足:A[PARENT(i)]>=A[i]。也就是说,某个节点的值至多与其父节点一样大。因此,堆中的最大元素存放根节点中;且,在任意子树中,该子树所包含的所有节点的值都不大于该子树根节点的值。最小堆的组织方式正好相反:最小堆性质是指除了根以外的所有节点i都有A[PARENT(i)]=<A[i]。

通常,堆排序算法中使用最大堆;最小堆则用于构造优先队列。

用Java实现的堆排序算法如下所示:

	/**
	 * 堆排序
	 * 
	 * @param A
	 */
	public void heapSort(int[] A) {
		int heap_size = A.length - 1;
		buildMaxHeap(A, heap_size);
		for (int i = A.length - 1; i > 0; i--) {
			exchange(A, 0, i);
			heap_size--;
			maxHeapify(A, heap_size, 0);
		}
	}

	/**
	 * 
	 * 建立初始最大堆
	 * 
	 * 由完全二叉树的性质可以推出:如果要将数组A[1...n]转换成最大堆,则子数组元素A[n/2 + 1...n]都是树的叶子节点,且叶子节点可以看做是只有一个元素的堆
	 * 
	 * @param A
	 */
	public void buildMaxHeap(int[] A, int heap_size) {
		for (int i = A.length / 2 - 1; i >= 0; i--) {
			maxHeapify(A, heap_size, i);
		}
	}

	/**
	 * 堆排序中使用最大堆性质
	 * 
	 * 维护最大堆性质:以iMax节点为根节点,维护此子二叉树中的最大堆性质
	 * 
	 * @param A
	 *            排序的数组
	 * @param idex
	 *            操作的子树的根节点对应的下标值
	 */
	public void maxHeapify(int[] A, int heap_size, int iMax) {

		int left = getLeftIndex(iMax);// 该根节点的左节点元素下标
		int right = getRightIndex(iMax);// 该根节点的右节点元素下标

		int largest = iMax;

		if (heap_size >= left && A[left] > A[iMax])// 判断根节点下标的元素与左子节点下标的元素哪个大;largest存放较大的元素的下标
			largest = left;
		else
			largest = iMax;

		if (heap_size >= right && A[right] > A[largest])// 判断largest下标的代表元素与右节点下标的元素哪个大;largest存放较大的元素的下标
			largest = right;

		if (iMax != largest) {// 如果largest与iMax不等,说明根节点的元素并不是最大的;随即进行元素调换,保证根节点的元素值是根节点/左右子节点中最大的那个
			exchange(A, iMax, largest);
			maxHeapify(A, heap_size, largest);// 在递归调用maxHeapify()函,使以largest下标元素为根节点的子树也保证堆最大性质
		}
	}

	public void exchange(int[] A, int idex, int idex2) {
		int temp = A[idex];
		A[idex] = A[idex2];
		A[idex2] = temp;

	}

	/**
	 * 
	 * 获取下标i对应元素的父元素下标
	 * 
	 * @param i
	 * @return
	 */
	public int getParentIndex(int iMax) {
		return (iMax == 0) ? 0 : (iMax % 2 == 0 ? (iMax - 1 / 2) : (iMax / 2));
	}

	/**
	 * 
	 * 获取下标i代表的父元素的左子元素的下标
	 * 
	 * @param i
	 * @return
	 */
	public int getLeftIndex(int iMax) {
		return 2 * iMax + 1;
	}

	/**
	 * 获取下标i代表的父元素的右子元素的下标
	 * 
	 * @param i
	 * @return
	 */
	public int getRightIndex(int iMax) {
		return 2 * (iMax + 1);
	}
堆排序算法完整的测试代码如下:
public class SortAlgor {

	public static void main(String[] args) {
		int[] array = { 2,5,10,8,7 };
		SortAlgor algorithm = new SortAlgor();
		algorithm.heapSort(array);
		algorithm.arrayPrint(array);
	}

	/**
	 * 堆排序
	 * 
	 * @param A
	 */
	public void heapSort(int[] A) {
		int heap_size = A.length - 1;
		buildMaxHeap(A, heap_size);
		for (int i = A.length - 1; i > 0; i--) {
			exchange(A, 0, i);
			heap_size--;
			maxHeapify(A, heap_size, 0);
		}
	}

	/**
	 * 
	 * 建立初始最大堆
	 * 
	 * 由完全二叉树的性质可以推出:如果要将数组A[1...n]转换成最大堆,则子数组元素A[n/2 + 1...n]都是树的叶子节点,且叶子节点可以看做是只有一个元素的堆
	 * 
	 * @param A
	 */
	public void buildMaxHeap(int[] A, int heap_size) {
		for (int i = A.length / 2 - 1; i >= 0; i--) {
			maxHeapify(A, heap_size, i);
		}
	}

	/**
	 * 堆排序中使用最大堆性质
	 * 
	 * 维护最大堆性质:以iMax节点为根节点,维护此子二叉树中的最大堆性质
	 * 
	 * @param A
	 *            排序的数组
	 * @param idex
	 *            操作的子树的根节点对应的下标值
	 */
	public void maxHeapify(int[] A, int heap_size, int iMax) {

		int left = getLeftIndex(iMax);// 该根节点的左节点元素下标
		int right = getRightIndex(iMax);// 该根节点的右节点元素下标

		int largest = iMax;

		if (heap_size >= left && A[left] > A[iMax])// 判断根节点下标的元素与左子节点下标的元素哪个大;largest存放较大的元素的下标
			largest = left;
		else
			largest = iMax;

		if (heap_size >= right && A[right] > A[largest])// 判断largest下标的代表元素与右节点下标的元素哪个大;largest存放较大的元素的下标
			largest = right;

		if (iMax != largest) {// 如果largest与iMax不等,说明根节点的元素并不是最大的;随即进行元素调换,保证根节点的元素值是根节点/左右子节点中最大的那个
			exchange(A, iMax, largest);
			maxHeapify(A, heap_size, largest);// 在递归调用maxHeapify()函,使以largest下标元素为根节点的子树也保证堆最大性质
		}
	}

	public void exchange(int[] A, int idex, int idex2) {
		int temp = A[idex];
		A[idex] = A[idex2];
		A[idex2] = temp;

	}

	/**
	 * 
	 * 获取下标i对应元素的父元素下标
	 * 
	 * @param i
	 * @return
	 */
	public int getParentIndex(int iMax) {
		return iMax;
	}

	/**
	 * 
	 * 获取下标i代表的父元素的左子元素的下标
	 * 
	 * @param i
	 * @return
	 */
	public int getLeftIndex(int iMax) {
		return 2 * iMax + 1;
	}

	/**
	 * 获取下标i代表的父元素的右子元素的下标
	 * 
	 * @param i
	 * @return
	 */
	public int getRightIndex(int iMax) {
		return 2 * (iMax + 1);
	}

	public void arrayPrint(int[] array) {
		System.out.print("[");
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + ", ");
		}
		System.out.println("]");
	}
}
本来是会上一点图示,来展示堆排序算法的运行过程的;但网站貌似有问题,图片插入了无法显示。后面补上吧~~


PS:

下面是堆排序的一个运行过程的图示:




猜你喜欢

转载自blog.csdn.net/csdn_of_coder/article/details/80100664