排序算法之选择排序

选择排序的基本思想是:每一趟在后面n-i+1个待排序的元素中选取关键字最小的元素,作为有序子序列的第i

个元素,直到第n-1趟做完,待排序元素,只剩下一个,就不用再选择了。以简单选择排序和堆排序为例。

 简单选择排序:

	public static int[] selectSort(int[] array)
	{
		int[] newArray = Arrays.copyOf(array, array.length); // 深拷贝(避免修改原来的数组元素顺序)
		int min = 0; // 记录最元素所在的位置
		for (int i = 0; i < array.length; i++)
		{
			min = i;
			for (int j=i+1; j<newArray.length; j++)
			{
				if (newArray[min] > newArray[j])
				{
					min = j; // 记录每一轮最小元素所在的位置
				}
			}
			
			if (min != i)     // 如果当前位置不是最小元素的话,就交换
			{
				int tmp = newArray[i];
				newArray[i] = newArray[min];
				newArray[min] = tmp;
			}
		}
		return newArray;
	}

 简单选择排序是一个不稳定的排序方法。

堆排序:

堆排序是一种树形选择排序方法,它的特点是:在排序的过程中,将L[1..n]看成是一棵完全二叉树的顺序

存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区域中选择关键字最大(或最小)

的元素。

堆分为大根堆和小根堆。在大根堆中,最大的元素放在根结点中,且对其任一非根结点,它的值小于或等于 

其双亲结点的值。

堆排序的关键是构建初始堆,对初始序列建堆,就是一个反复筛选的过程。n个结点的完全二叉树,最后一个

结点是第n/2个结点的孩子。对第n/2个结点为根的子树筛选(对大根堆:若根结点的关键字小于左右子女中关键

字较大者,则交换),使该子树成为堆。之后向前依次对各结点(n/2-1~1)为根的子树进行筛选,看该结点值是否

大于其左右子结点的值,若不是,将左右子结点中较大者与之交换,交换后可能会破坏下一级的堆,于是继续采用

上述方法构造下一级的堆,直到以该结点为根的子树构成堆为止。反复利用上述调整堆的方法建堆,直到根结点。 

	public void heapSort(int[] array)
	{
		if (array==null || array.length<=1)
		{
			return;
		}
		// 创建大堆
		buidMaxHeap(array);
		for (int i=array.length-1; i>=1; i--)
		{
			// 一次大堆构造后,最大元素就会变为下标为0的元素
			exchangeElements(array, 0, i); // 交换首尾两个元素
			adjustDown(array, i, 0); // 从0开始是因为进行第一次大堆构造后,除了二叉树中除了第0个元素外,其余的满足大顶堆要求
		}
	}

	private void buidMaxHeap(int[] a)
	{
		 int half = (a.length - 1) / 2; // 只需遍历一半,就可找到所有元素
		 for (int i=half; i>=0; i--) // 从后向前的构造容易
		 {
			 adjustDown(a, a.length, i); // 数据, 长度, 从第i的元素开始
		 }
	}

	/**
	 * 将元素a[i]向下进行调整
	 * @param a 存放数据的数组
	 * @param length  数据的长度
	 * @param i   起始构造的位置
	 */
	private void adjustDown(int[] a, int length, int i)
	{
		int left = i*2+1;
		int right = i*2+2;
		int largest = i;
		// 找到该二叉树中的最大值
		if (left<length && a[left]>a[i])
		{
			largest = left;
		}
		
		if (right<length && a[right]>a[largest])
		{
			largest = right;
		}
		
		if (i != largest)
		{
			// 进行数据交换
			exchangeElements(a, i, largest);
			// 因为largest和它的父节点位置发生了改变,需要对以largest为根的二叉树进行调整
			adjustDown(a, length, largest); 
		}
	}
	
	private void exchangeElements(int[] a, int i, int largest)
	{
		int tmp = a[i];
		a[i] = a[largest];
		a[largest] = tmp;
	}

空间效率:O(1)。

时间效率:建堆时间为O(n),之后又n-1次向下调整操作,每次调整的时间复杂度是O(h),在最好,最坏和平均情况

下堆排序的时间复杂度为O(nlog2n)。

稳定性:堆排序是一种不稳定的排序方法。

图示:可参考博文   https://blog.csdn.net/sxhelijian/article/details/50295637

阅读更多

没有更多推荐了,返回首页