选择排序的基本思想是:每一趟(例如第i趟)在后面n-i+1(i=1,2,…,n-1)个待排序元素中选取关键字最小的元素,作为有序子序列的第i个元素,直到第n-1趟做完,待排序元素只剩下1个,就不用再选了。
简单选择排序
简答选择排序是一朴素的选择排序算法。算法的思想:假设排序表为L[1…n],第i趟排序即从L[i…n]中选择关键字最小的元素与L(i)交换,每趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可以使得整个排序表有序。
示例代码
public static void selectSort(int[] arr){ int length = arr.length; for(int i=0;i<length-1;i++){ int min = i; for(int j=i+1;j<length;j++){ if(arr[min]> arr[j]) min = j; } if(min != i){ int tmp = arr[i]; arr[i] = arr[min]; arr[min] = tmp; } } }
堆排序
堆排序是一种树形选择排序方法,它的特点是:在排序过程中,将L[1…n]看成一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子节点之间的内在关系,在当前无序区 中选择关键字最大(或最小)的元素。当且仅当满足下列条件时,n个关键字序列L[1…n]称为堆。
- L(i)⩽L(2i)且L(i)⩽L(2i+1)被称为小根堆或L(i)⩾L(2i)且L(i)⩾L(2i+1)被称为大根堆 (1⩽i⩽⌊n/2⌋)
图中所示的是大根堆
堆的初始化
堆排序的关键是构造初始堆,对初始序列建堆是一个反复筛选的过程。n个节点的完全二叉树,最后一个节点是第⌊n/2⌋个节点的孩子。对第⌊n/2⌋个节点为根的子树进行筛选(对于大根堆:若根节点的关键字小于左右子女中关键字较大者,则交换),使该子树成为堆。之后向前依次对各节点(⌊n/2⌋-1~1)为根的子树进行筛选,看该节点值是否大于其左右子节点的值,若不是,将左右子节点中较大值与之交换,交换后会破坏下一级的堆,于是对交换后的那个子节点继续采用上述方法构造下一级的堆,直到以该节点为根的子树构成堆为止。 注意:整体是从下往上调整,局部是从上往下调整,比如上图中先调整32,在调整78,再调整45,但是在调整45时可能破坏了原来的堆,所以要沿着45往下调整
示例代码(大根堆)
public static void maxHeapInit(int[] arr){ //数组中的下标是从0开始的,而树中节点的编号是从1开始的 int len = arr.length; for(int i=len/2;i>0;i--) adjustDown(arr, i-1, len); }</span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> adjustDown(<span style="color: #0000ff">int</span>[] arr, <span style="color: #0000ff">int</span> k, <span style="color: #0000ff">int</span><span style="color: #000000"> len) { </span><span style="color: #0000ff">int</span> tmp = arr[k]; <span style="color: #008000">//</span><span style="color: #008000">保存子树中根节点值</span> <span style="color: #0000ff">for</span>(<span style="color: #0000ff">int</span> i=2*k+1; i<=len; i=2*k+1){ <span style="color: #008000">//</span><span style="color: #008000">从当前节点沿较大子节点向下调整</span> <span style="color: #0000ff">if</span>(i<len && arr[i]<arr[i+1<span style="color: #000000">]) i</span>++; <span style="color: #008000">//</span><span style="color: #008000">取的k较大的子节点下标</span> <span style="color: #0000ff">if</span>(tmp >= arr[i]) <span style="color: #008000">//</span><span style="color: #008000">如果根节点比子节点大则不需要继续调整</span> <span style="color: #0000ff">break</span><span style="color: #000000">; </span><span style="color: #0000ff">else</span><span style="color: #000000">{ arr[k] </span>= arr[i];<span style="color: #008000">//</span><span style="color: #008000">交换根节点和子节点关键字</span> k = i; <span style="color: #008000">//</span><span style="color: #008000">修改k值,以便向下继续筛选</span>
}
}
arr[k] = tmp;
}
大根堆排序
大根堆只能保证根节点的值大于等于子节点的值,但是不能保证在同一层上的元素从左向右是按从小到大排序了。所以在数组上做大根堆初始化还后还需要进行调整才能产生一个有序序列。我们知道大根堆中根节点的值最大,对应的是数组中的第一个位置的元素(默认是下标为0的元素),所以我们每次将数组中的第一个元素输出直到数组中没有元素,那么我们就能得到一个有序序列。每次将堆顶元素(数组中的第一个元素)输出后,需要将堆底的元素(数组中的最后一个元素)送入堆顶从而将堆进行破坏,这样再次对堆进行调整,我们就能保证堆顶元素为最大值。
示例代码
public static void heapSort(int[] arr){ maxHeapInit(arr); for(int i=arr.length-1;i>0;i--){ System.out.println(arr[0]); //输出堆顶元素 int tmp = arr[0]; //将堆底元素送到堆顶 //为了保证元素不丢失,这里进行交换 arr[0] = arr[i]; arr[i] = tmp; //根节点元素发生改变,需要从根节点开始调整 //因为有一个元素被已被取出,所以数组的有效长度需要减一 adjustDown(arr, 0, i-1); } System.out.println(arr[0]); }