堆排序算法及实现

堆排序分为小根堆(最小堆)和大根堆(最大堆)两种,前一种是升序排列,后一种是降序排列。堆排序与快速排序及归并排序一样,时间复杂度都是O(nlogn),属于比较常用的排序方法。

堆排序是利用了二叉树的特性,二叉堆类似于完全二叉树,小(大)根堆的每一个非叶子节点的值都小于(大于)或等于其左右孩子的值。

堆的存储一般是用数组的形式,i结点的父结点下标是(i-1)/2,左孩子结点下标是2*i+1,右孩子结点下标是2*i+2。数组下标从零开始。

堆排序的过程是一个建堆、删除根和不断调整堆的过程,当然堆的操作还包括插入元素。

整个过程实现如下:

public class HeapSort {
	/*
	 * 方法1: 以需要调整的结点的左孩子为基准,每次判断交换后的非叶子结点
	 */
	private void adjustHeapNoRec(int[] a, int i, int n) {
		int tmp;
		tmp = a[i];
		int j = 2*i + 1;
		while(j < n) {
			if(j+1 < n && a[j+1] < a[j]) {
				j ++;
			}
			
			if(a[j] >= tmp) break;
			
			a[i] = a[j];
			i = j;
			j = 2*i + 1;
		}
		a[i] = tmp;
	}
	/*
	 * 方法1的递归的处理方法
	 */
	private void adjustHeapRec(int[] a, int i, int end) {
		if(i >= end) return;
		int tmp = a[i];
		int j = 2*i + 1;
		if(j >= end) return;
		if(j + 1 < end && a[j+1] < a[j])
			j ++;
		if(a[j] > a[i]) return;
		a[i] = a[j];
		i = j;
		a[i] = tmp;
		adjustHeapRec(a, i, end);
	}
	/*
	 * 方法2: 以需要调整的结点为基准,判断其左右孩子,调整到最后一个非叶子节点即可。
	 */
	private void ajustHeapNonRec(int[] a, int i, int n) {
		int min = i;
		while(i < (n-1)/2) {
			int left = 2*i+1;
			int right = 2*i+2;
			
			if(left < n && a[left] < a[min])
				min = left;
			if(right < n && a[right] < a[min])
				min = right;
			
			if(min != i) {
				int tmp = a[i];
				a[i] = a[min];
				a[min] = tmp;
				i = min;
			}
		}
	}
	/*
	 * 方法2的递归处理方法
	 */
	private void adjustHeapRecur(int[] a, int i, int n) {
		if(i >= (n-1)/2) return;
		int min = i;
		int left = 2*i+1;
		int right = 2*i+2;
		if(left < n && a[left] < a[min])
			min = left;
		if(right < n && a[right] < a[min]) 
			min = right;
		if(min != i) {
			int tmp = a[i];
			a[i] = a[min];
			a[min] = tmp;
			adjustHeapRecur(a, min, n);
		}
	}
	/*
	 * 建堆的过程就是从最后一个非叶子结点开始,不断调整堆的过程。
	 */
	private void buildHeap(int[] a, int start, int end, int n) {
		int mid = (start + end - 1) >> 1;
		for(int i = mid; i >= 0; i --) {
//			adjustHeapRec(a, i, n);
//			ajustHeapNonRec(a,i,n);
			adjustHeapRecur(a,i,n);
		}
	}
	/*
	 * 删除堆:每次删除时,将根与当前堆的最后一个元素交换。
	 */
	private void deleteHeap(int[] a, int n) {
		int j = n - 1;
		while(j > 0) {
			int tmp = a[0];
			a[0] = a[j];
			a[j] = tmp;
			adjustHeapRec(a, 0, j);
			j --;
		}
	}
	
	public static void main(String[] args) {
		int[] a = {3,4,1,5,2};
		HeapSort t = new HeapSort();
		int n = a.length;
		t.buildHeap(a, 0, n-1, n);
		t.deleteHeap(a, n);
		//输出排序后的数组
		boolean flag = true;
		for(int i = n-1; i >= 0; i --) {
			if(flag) flag = false;
			else
				System.out.print(" ");
			System.out.print(a[i]);
		}
		System.out.println();
	}

}
还有就是向堆中插入一个元素,只需要判断该元素与其父结点的关系,向上调整。

	/*
	 * 插入元素:插入结点i,其父节点的坐标为(i-1)/2
	 */
	private void insertElem(int[] a, int i) {
		int min = i;
		while(i > 0) {
			int father = (i-1)/2;
			if(father >= 0 && a[father] < a[i])
				min = father;
			if(min == i) {
				int tmp = a[i];
				a[father] = a[i];
				a[i] = tmp;
				i = father;
			}
		}
	}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值