一.选择排序
1.基本思想
设所排序序列的记录个数为n。i取1,2,…,n-1,从所有n-i+1个记录(Ri,Ri+1,…,Rn)中找出排序码最小的记录,与第i个记录交换。执行n-1趟 后就完成了记录序列的排序。(选择待排数列最小的数排在待排数列首位)
2.思路
(1)从待排序序列中,找到关键字最小的元素;
(2)如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
(3)从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
3.代码
/**
* 方法名:ChooseSort
* 详述:选择排序
* @author: NowUSeeMe
*/
public static void ChooseSort(int numbers[]){
for(int i=0; i<numbers.length;i++){
int position = i; //选择最小元素的位置 一开始默认首位
//查找最小元素位置
for (int j = i; j < numbers.length; j++) {
if (numbers[position] > numbers[j]) {
position =j;
}
}
//交换位置
if(position != i){
int temp = numbers[position];
numbers[position] = numbers[i];
numbers[i] = temp;
}
}
}
测试:
@Test
public void testChooseSort(){
int[] numbers = {9,1,2,5,7,4,8,6,3,5};
AllSorts.ChooseSort(numbers);
AllSorts.printArr(numbers);
}
结果:
1 2 3 4 5 5 6 7 8 9
4.优缺点
(1)优点
移动数据的次数已知(n-1 次)
(2)缺点
比较次数多。不稳定。算法复杂度为n^2
二.堆排序
1.基本思想
堆是一棵顺序存储的完全二叉树。
其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小根堆。
其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大根堆。
堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的定义下:具有n个元素的序列 (h1,h2,…,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,…,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
2.思路
(1)初始化堆:根据初始数组去构造初始堆(构建一个完全二叉树,保证所有的父结点都比它的孩子结点数值大)。
(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆。
(3)当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。
3.代码
public class HeapSort {
public static void sort(Integer[] data) {
// 构建最大堆
buildMaxHeap(data);
// 循环,每次把根节点和最后一个节点调换位置
for (int i = data.length; i > 1; i--) {
Integer temp = data[0];
data[0] = data[i - 1];
data[i - 1] = temp;
//堆的长度减少1,排除置换到最后位置的根节点
maxHeapify(data, 1, i - 1);
}
}
// 根据输入数组构建一个最大堆
private static void buildMaxHeap(Integer[] data) {
for (int i = data.length / 2; i > 0; i--) {
maxHeapify(data, i, data.length);
}
}
//堆调整,使其生成最大堆
private static void maxHeapify(Integer[] data, int parentNodeIndex, int heapSize) {
// 左子节点索引
int leftChildNodeIndex = parentNodeIndex * 2;
// 右子节点索引
int rightChildNodeIndex = parentNodeIndex * 2 + 1;
// 最大节点索引
int largestNodeIndex = parentNodeIndex;
// 如果左子节点大于父节点,则将左子节点作为最大节点
if (leftChildNodeIndex <= heapSize && data[leftChildNodeIndex - 1]>data[parentNodeIndex - 1]) {
largestNodeIndex = leftChildNodeIndex;
}
// 如果右子节点比最大节点还大,那么最大节点应该是右子节点
if (rightChildNodeIndex <= heapSize && data[rightChildNodeIndex - 1]>data[largestNodeIndex - 1]) {
largestNodeIndex = rightChildNodeIndex;
}
// 最后,如果最大节点和父节点不一致,则交换他们的值
if (largestNodeIndex != parentNodeIndex) {
Integer tmp = data[parentNodeIndex - 1];
data[parentNodeIndex - 1] = data[largestNodeIndex - 1];
data[largestNodeIndex - 1] = tmp;
// 交换完父节点和子节点的值,对换了值的子节点检查是否符合最大堆的特性
maxHeapify(data, largestNodeIndex, heapSize);
}
}
}
测试:
@Test
public void testHeapSort(){
Integer[] numbers = {16,7,3,20,17,8};
HeapSort.sort(numbers);
for(int i=0;i<numbers.length;i++){
System.out.print(numbers[i]+" ");
}
}
结果:
3 7 8 16 17 20
4.复杂度及稳定性
(1)时间复杂度
堆的存储表示是顺序的。因为堆所对应的二叉树为完全二叉树,而完全二叉树通常采用顺序存储方式。
当想得到一个序列中第k个最小的元素之前的部分排序序列,最好采用堆排序。
因为堆排序的时间复杂度是O(n+klog2n),若k≤n/log2n,则可得到的时间复杂度为O(n)。
(2)算法稳定性
堆排序是一种不稳定的排序方法。
因为在堆的调整过程中,关键字进行比较和交换所走的是该结点到叶子结点的一条路径,因此对于相同的关键字就可能出现排在后面的关键字被交换到前面来的情况。
三.参考
http://www.cnblogs.com/0201zcr/p/4764705.html
http://www.cnblogs.com/jingmoxukong/p/4303289.html