初级算法排序(选择排序,插入排序,希尔排序)
选择排序
一.思想
选择排序就是找到数组中最小的那个元素,然后将该元素和数组的第一个元素交换。然后在剩下的数组(除去第一个元素)找到最小的元素,将他与第二个元素交换,然后继续将剩下的数组(除去第一个,第二个元素,即除去已经排序好的元素)继续执行上述操作直到所有元素排序完毕。
二.代码
下面用了排序算法模板
public class Selection {
/**
* 选择排序(升序)
* @param a
*/
public static void sort(Comparable[] a){
int min;
for (int i = 0; i < a.length-1; i++) {
//将a[i]与a[i+1~a.length]中最小的元素交换
min=i;
for (int j = i+1; j < a.length ; j++) {
if (less(a[j],a[min])){
min = j;
}
}
exch(a, i , min);
}
}
/**
* 比较大小,如果v小于w就返回true
* @param v
* @param w
* @return
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
/**
* 交换数组a的i位置和j位置的元素
* @param a
* @param i
* @param j
*/
private static void exch(Comparable[] a,int i,int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
三.特点
- 运行时间和输入无关,一个已经有序的数组或者是元素全部相等的数组和一个元素随机排列的数组所用的排序时间是一样长的。
- 数据移动是最少,选择排序用了N次交换——交换次数和数组的大小是线性关系。
- 对于长度为N的数组,选择排序需要大约N的2次方除于2次比较和N次交换。
插入排序
一.思想
插入排序和人们整理桥牌的方法一样,一张一张的排序,将每一张牌插入到其他已经有序的牌中的适当位置。与选择排序一样,待插入元素的左边的数组都是有序的,但是他们的最终位置不是确定的,他们的最终位置是通过插入元素一步步确定的,为了找到待插入元素在左边有序的数组的位置,我们需要将比待插入小(或大)的元素向右移动。
二.代码
下面用了排序算法模板
public class Insertion {
/**
* 插入排序
* @param a
*/
public static void sort(Comparable[] a){
for (int i = 1; i < a.length; i++) {
//将a[i]插入到前面有序的数组中(这里是通过交换的方式,后面有讲如何改进)
for (int j = i; j > 0 && less(a[j], a[j-1]) ; j--) {
exch(a, j, j-1);
}
}
}
/**
* 比较大小,如果v小于w就返回true
* @param v
* @param w
* @return
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
/**
* 交换数组a的i位置和j位置的元素
* @param a
* @param i
* @param j
*/
private static void exch(Comparable[] a,int i,int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
三.特点
- 和选择排序不同,插入排序所需要的时间取决于输入中元素的初始顺序。对一个有序(或接近有序)的数组进行排序会比对随机顺序的数组或是逆序数组进行排序要快得多。
- 对于随机排列的长度为N且元素不重复的数组,平均情况下插入排序需要大概N的2次方除于4次比较以及N的2次方除于4次交换。最坏情况下需要N的2次方除于2次比较和交换,最好情况下需要N-1次比较和0次交换(N为数组元素的个数)
- 我们经常会遇到要将部分有序的数据排序的情况,插入对这样的部分有序的数组非常有效。下面是几种非常典型的部分有序的数组:
数组中每个元素距离他的最终位置都不远
一个有序的大数组接一个小数组
数组中只有几个元素的位置不正确
倒置指的是数组中的两个顺序颠倒的元素。当倒置的元素很少时,插入排序很可能比其他的任何算法都要快
四.改进
1.不总是交换
要大幅度提高插入排序的速度,我们只需要在sort方法中,将待插入元素给一个元素保存,然后再内循环中将较大(较小)的元素都向右移动而不总是交换,代码如下:
public class NoExchInsertion {
/**
不需要交换的插入排序
**/
public static void sort(Comparable[] a){
Comparable insertElement;
int insertIndex;
for (int i = 1; i < a.length; i++) {
//把待插入元素保存起来
insertElement = a[i];
insertIndex = i;
for (int j = i; j >0 && less(,a[j-1]) ; j--) {
//将元素后移
a[j] = a[j-1];
insertIndex = j-1;
}
a[insertIndex] = insertElement;
}
}
/**
* 比较大小,如果v小于w就返回true
* @param v
* @param w
* @return
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
}
2.插入排序的哨兵
在sort方法的内层for循环中,每次都要判断j>0来作为判断有没有越界的,我们可以先找出最小的元素把他放在第一位,让他作为哨兵,因为任何元素都不可能小过他,这样就避免了每次都要判断j>0,代码如下:
public class GuardInsertion {
/**
* 插入排序
* @param a
*/
public static void sort(Comparable[] a){
int minIndex = 0;
for (int i = 1; i < a.length ; i++) {
if (less(a[i],a[minIndex])){
minIndex = i;
}
}
//把最小的元素移动到最左边,当作哨兵,可以防止下面的内循环中的j>0的判断
exch(a, 0, minIndex);
Comparable insertElement;
int insertIndex;
for (int i = 1; i < a.length; i++) {
insertElement = a[i];
insertIndex = i;
for (int j = i; less(a[j], a[j-1]) ; j--) {
a[j]=a[j-1];
insertIndex = j-1 ;
}
}
}
/**
* 比较大小,如果v小于w就返回true
* @param v
* @param w
* @return
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
}
希尔排序
一.前言
希尔排序是一种基于插入排序的快速的排序算法。对于大规模乱序数组插入排序很慢,因为他会交换相邻的元素,因此元素只能一步一步的从数组的一端移动到另一段。例如:假设数组中最小的元素在数组的尽头,要将他移动到正确的位置就需要移动N(数组的元素个数)-1次。希尔为了加快速度改进了插入排序,希尔通过交换不相邻的元素对数组进行局部排序,并最终用插入排序将局部有序的数组排序。
二.思想
希尔排序的思想就是使数组中任意间隔为h(增量)的元素都是有序的。这样的数组我们被称为h有序数组,就是说,一个h有序数组就是h个互相独立的有序数组编织在一起组成的一个数组,如图:
我们将这个h个数组独立地排序,就可以形成h个有序的数组,在进行排序时我们可以将元素移动到很远的地方,不需要像普通的插入排序一样,一步一步的移动。然后我们通过递减h,慢慢地加深数组的有序程度,最后当h等于1的时候,就进行直接插入排序,就可以将整个数组排序。
关于h怎么去选择这个问题,算法的性能不仅取决于h,还取决于h之间的数学性质。有很多论文研究了各种不同的h,但都无法证明用那个h,怎么递减h时最好的。在实际应用中,使用下面的代码选择的h取值规则和递减规则基本就足够了,我们先看看希尔排序的代码。
三.代码
public class Shell {
public static void sort(Comparable[] a){
int N = a.length;
int h = 1;//h代表的是增量
while (h < N/3){
h=3*h+1;
}
while (h>=1){
for (int i = h; i < N; i++) {
for (int j = i; j >= h && less(a[j] , a[j-h]); j-=h) {
exch(a, j, j-h);
}
}
h/=3;
}
}
/**
* 比较大小,如果v小于w就返回true
* @param v
* @param w
* @return
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
/**
* 交换数组a的i位置和j位置的元素
* @param a
* @param i
* @param j
*/
private static void exch(Comparable[] a,int i,int j){
Comparable temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}