算法(第四版)2.1初级排序算法
文章目录
一、算法2.1 选择排序
1.算法思想
- 找到数组元素中最小的元素
- 将它与数组中第一个元素互换(如果第一个元素最小,就和自己互换)
- 在生下的元素中找到最小的元素,与数组中第二个元素交换位置
- 以此类推,直至将整个数组排序
2.命名原因
不断地选择剩余元素之中的最小者
3.代码实现
public class Selection {
public static void sort(Comparable[] a) {
// 将a[]按升序排列
int N = a.length;
for (int i = 0; i < N; i++) {
int min = i;
for (int j = i + 1; j < N; j++)
if(less(a[j],a[min]))
min = j;
exch(a, i, min);
}
}
// v < w 返回true
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
// 交换a[i]和a[j]的位置
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
// 在单行打印数组
public static void show(Comparable[] a) {
for (int i = 0; i < a.length - 1; i++) {
System.out.print(a[i]+" ");
}
System.out.print(a[a.length - 1]);
System.out.println();
}
// 测试数组是否有序
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++)
if(less(a[i], a[i - 1]))
return false;
return true;
}
}
import java.util.Scanner;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(sc1.nextLine());
while(sc2.hasNext()) {
array.add(sc2.next());
}
Selection sel = new Selection();
String[] a = new String[array.size()];
for (int i = 0; i < a.length; i++) {
a[i] = array.get(i);
}
sel.sort(a);
assert sel.isSorted(a);
sel.show(a);
}
}
bed bug yes zoo all bad yet
all bad bed bug yes yet zoo
4.算法分析
对于长度为N的数组
- 需要大约
N
2
/
2
N^2/2
N2/2此比较
- 具体为$ (N-1)+(N-2)+···2+1=N(N-1)/2$
- 需要 N N N次交换
5.算法特点
- 运行时间和输入无关
- 无论数组一开始有序或者随机排列,运行时间不变
- 数据移动是最少的
- 交换次数和数组大小成线性关系
二、算法2.2 插入排序
1.算法思想
- 先将数组第一个元素不动
- 从第二个元素开始,将此元素与前一个元素比较
- 若此元素较小则交换两个元素位置,继续与前一个元素比较直至第一个元素
- 以此类推,直至将整个数组排序
2.命名原因
和理牌一样,新抽取的牌往里面插入适当位置
3.代码实现
public class Insertion {
public static void sort(Comparable[] a) {
// 将a[]按升序排列
int N = a.length;
for (int i = 0; i < N; i++) {
// 将a[i] 插入到a[i-1] a[i-2] a[i-3]···中
for(int j = i; j>0 && less(a[j], a[j-1]); j--)
exch(a, j, j-1);
}
}
// v < w 返回true
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
// 交换a[i]和a[j]的位置
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
// 在单行打印数组
public static void show(Comparable[] a) {
for (int i = 0; i < a.length - 1; i++) {
System.out.print(a[i]+" ");
}
System.out.print(a[a.length - 1]);
System.out.println();
}
// 测试数组是否有序
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++)
if(less(a[i], a[i - 1]))
return false;
return true;
}
}
import java.util.Scanner;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(sc1.nextLine());
while(sc2.hasNext()) {
array.add(sc2.next());
}
Insertion ins = new Insertion();
String[] a = new String[array.size()];
for (int i = 0; i < a.length; i++) {
a[i] = array.get(i);
}
ins.sort(a);
//assert sel.isSorted(a);
ins.show(a);
}
}
bed bug yes zoo all bad yet
all bad bed bug yes yet zoo
4.算法分析
对于长度为N的数组
- 平均情况:大约需要 N 2 / 4 N^2/4 N2/4次比较 N 2 / 4 N^2/4 N2/4次交换
- 最坏情况:需要
N
2
/
2
N^2/2
N2/2次比较
N
2
/
2
N^2/2
N2/2次交换
- 所有元素都需要移动位置(逆序数组)
- 1 + 2 + ⋅ ⋅ ⋅ + ( N − 2 ) + ( N − 1 ) 1+2+···+(N-2)+(N-1) 1+2+⋅⋅⋅+(N−2)+(N−1)
- 最好情况:需要
N
−
1
N-1
N−1次比较
0
0
0次交换
- 所有元素都不需要移动位置(有序数组)
- $ \overbrace{1+1+···+1}^{N-1} $
5.插入排序的适合情形
倒置:数组中两个顺序颠倒的元素
EXAMPLE中有:E-A,X-A,X-M,X-P,X-L,X-E,M-L,M-E,P-L,P-E,L-E共11对倒置
部分有序数组:数组中倒置的数量小于数组大小的某个倍数
几种典型的部分有序数组
- 数组中每个元素距离它的最终位置都不远
- 一个有序的大数组接一个小数组
- 数组中只有几个元素的位置不正确
插入排序需要的交换操作和数组中倒置的数量相同,需要比较的次数大于等于倒置的数量,小于等于倒置的数量加上数组的大小再减一
最快情况:比较次数=倒置数量= N ( N − 1 ) / 2 N(N-1)/2 N(N−1)/2
最好情况:比较次数=倒置数量+数组大小-1= N − 1 N-1 N−1
三、算法2.3 希尔排序
1.算法思想
- 使数组任意间隔为h的元素都是有序的
- (改进的插入排序)交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序
2.代码实现
public class Shell {
public static void sort(Comparable[] a) {
// 将a[]按升序排列
int N = a.length;
int h = 1;
while(h < N/3)
// h可以为1,4,13,40 ···
h = 3*h + 1;
while(h >= 1) {
for (int i = 0; i < N; i++) {
// 将a[i]插入a[i-h],a[i-2*h],a[i-3*h]···
for (int j = i; j>=h && less(a[j], a[j-h]); j -= h)
exch(a, j, j-h);
}
h = h/3;
}
}
// v < w 返回true
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
// 交换a[i]和a[j]的位置
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
// 在单行打印数组
public static void show(Comparable[] a) {
for (int i = 0; i < a.length - 1; i++) {
System.out.print(a[i]+" ");
}
System.out.print(a[a.length - 1]);
System.out.println();
}
// 测试数组是否有序
public static boolean isSorted(Comparable[] a) {
for (int i = 1; i < a.length; i++)
if(less(a[i], a[i - 1]))
return false;
return true;
}
}
import java.util.Scanner;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(sc1.nextLine());
while(sc2.hasNext()) {
array.add(sc2.next());
}
Shell sh = new Shell();
String[] a = new String[array.size()];
for (int i = 0; i < a.length; i++) {
a[i] = array.get(i);
}
sh.sort(a);
sh.show(a);
}
}
bed bug yes zoo all bad yet
all bad bed bug yes yet zoo
3.算法分析
- 希尔排序算法性能分析复杂(超过本书范围)(待查阅)
- 已知最坏情况下比较次数和 N 3 / 2 N^{3/2} N3/2成正比
- 使用1,4,13,40,···的希尔排序所需的比较次数不会超过N的若干倍数乘以递增序列的长度