排序---初级排序算法(选择排序、插入排序和希尔排序)

写在前面的话:

一枚自学Java和算法的工科妹子。

  • 算法学习书目:算法(第四版) Robert Sedgewick
  • 算法视频教程:Coursera  Algorithms Part1&2

本文是根据《算法(第四版)》的个人总结,如有错误,请批评指正。

一、排序算法介绍

1.排序算法的目的:将所有元素的主键(如学生类,主键可以是姓名、学号、年龄等)按照某种方式排列(通常按照大小或字母排序)。

2.排序算法类模板:sort()排序方法;less()元素比较;exch()元素交换位置;show()打印字符数组内容;isSorted()判断元素是否有序。

 1 public class Example {
 2 
 3     private Example () { }
 4 
 5     public static void sort(Comparable[] a) {
 6       // 选择排序、插入排序、希尔排序、归并排序、快速排序,实现Comparable接口
 7     }
 8     public static void sort(Object[] a, Comparator comparator) {
 9       //  数据类型中定义内部类实现Comparator
10     }
11 
12     // 判断元素大小
13     private static boolean less(Comparable v, Comparable w) {
14         return v.compareTo(w) < 0;
15     }
16     private static boolean less(Comparator comparator, Object v, Object w) {
17         return comparator.compare(v, w) < 0;
18     }
19 
20     // 交换元素位置
21     private static void exch(Object[] a, int i, int j) {
22         Object swap = a[i];
23         a[i] = a[j];
24         a[j] = swap;
25     }
26 
27     // 判断元素是否已排序
28     private static boolean isSorted(Comparable[] a, int lo, int hi) {
29         for (int i = lo + 1; i <= hi; i++)
30             if (less(a[i], a[i-1])) return false;
31         return true;
32     }
33 
34     private static boolean isSorted(Object[] a, Comparator comparator, int lo, int hi) {
35         for (int i = lo + 1; i <= hi; i++)
36             if (less(comparator, a[i], a[i-1])) return false;
37         return true;
38     }
39 
40     // 打印字符数组内容
41     private static void show(Comparable[] a) {
42         for (int i = 0; i < a.length; i++) {
43             StdOut.println(a[i]);
44         }
45     }
46 }

3.排序算法需要注意的问题

  • 验证:在测试代码中添加一条语句 assert isSorted(a) 来确认排序后数组元素都是有序的。
  • 运行时间:在研究排序算法时,我们需要计算比较和交换的数量。对于不交换元素的算法,我们会计算访问数组的次数。
  • 额外的内存使用:(1)原地排序算法:除了函数调用所需的栈和固定数目的实例变量之外无需额外内存;(2)其他排序算法:需要额外内存空间来存储另一份数组副本。
  • 数据类型:上述排序算法模板使用与实现了Comparable接口或者在类中定义了内部类实现Comparator接口的数据类型。Java中Integer、Double、String、File、URL等数据类型都实现了Comparable接口。当然也可以自己设计数据类型实现Comparable接口,实现compareTo()方法来自定义目标类型对象的自然次序。可参考Comparable和Comparator区别(实现和使用)

二、初级排序算法

1. 选择排序介绍

1.1 选择排序算法流程

Step1:从待排序序列中,找到关键字最小的元素;

Step2:如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;

Step3:从余下的 N - 1 个元素中,找出关键字最小的元素,重复Step1和Step2,直到排序结束。

图1 选择排序的轨迹

1.2 选择排序算法代码实现

 1 import java.util.Comparator;
 2 
 3 public class Selection {
 4 
 5     private Selection() { }
 6 
 7     public static void sort(Comparable[] a) {
 8         int n = a.length;
 9         for (int i = 0; i < n; i++) {
10             int min = i;
11             for (int j = i+1; j < n; j++) {
12                 if (less(a[j], a[min])) min = j;
13             }
14             exch(a, i, min);
15             assert isSorted(a, 0, i);
16         }
17         assert isSorted(a);
18     }
19 
20     public static void sort(Object[] a, Comparator comparator) {
21         int n = a.length;
22         for (int i = 0; i < n; i++) {
23             int min = i;
24             for (int j = i+1; j < n; j++) {
25                 if (less(comparator, a[j], a[min])) min = j;
26             }
27             exch(a, i, min);
28             assert isSorted(a, comparator, 0, i);
29         }
30         assert isSorted(a, comparator);
31     }
32 
33     // is v < w ?
34     private static boolean less(Comparable v, Comparable w) {
35         return v.compareTo(w) < 0;
36     }
37 
38     // is v < w ?
39     private static boolean less(Comparator comparator, Object v, Object w) {
40         return comparator.compare(v, w) < 0;
41     }
42          
43     // exchange a[i] and a[j]
44     private static void exch(Object[] a, int i, int j) {
45         Object swap = a[i];
46         a[i] = a[j];
47         a[j] = swap;
48     }
49 
50     // is the array a[] sorted?
51     private static boolean isSorted(Comparable[] a) {
52         return isSorted(a, 0, a.length - 1);
53     }
54         
55     // is the array sorted from a[lo] to a[hi]
56     private static boolean isSorted(Comparable[] a, int lo, int hi) {
57         for (int i = lo + 1; i <= hi; i++)
58             if (less(a[i], a[i-1])) return false;
59         return true;
60     }
61 
62     // is the array a[] sorted?
63     private static boolean isSorted(Object[] a, Comparator comparator) {
64         return isSorted(a, comparator, 0, a.length - 1);
65     }
66 
67     // is the array sorted from a[lo] to a[hi]
68     private static boolean isSorted(Object[] a, Comparator comparator, int lo, int hi) {
69         for (int i = lo + 1; i <= hi; i++)
70             if (less(comparator, a[i], a[i-1])) return false;
71         return true;
72     }
73 
74     // print array to standard output
75     private static void show(Comparable[] a) {
76         for (int i = 0; i < a.length; i++) {
77             StdOut.println(a[i]);
78         }
79     }
80 }

 1.3 选择排序算法性能分析

  • 对于长度为N的数组,选择排序需要大约(N-1)+(N-2)+...+2+1=N(N-1)/2~N2/2次比较和N次交换;
  • 运行时间和输入无关;
  • 数据移动是最少的;
  • 算法不稳定。

2. 插入排序介绍

2.1 插入排序算法流程

Step1:从第一个元素开始,该元素可以认为已经被排序;
Step2:取出下一个元素,在已经排序的元素序列中从后向前扫描;
Step3:如果该元素(已排序)大于新元素,将该元素移到下一位置;
Step4:重复Step3,直到找到已排序的元素小于或者等于新元素的位置;
Step5:将新元素插入到该位置后;
Step6:重复步骤Step2~Step5.

图2 插入排序的轨迹

 2.2 插入排序算法代码实现

  1 import java.util.Comparator;
  2 
  3 public class Insertion {
  4 
  5     // This class should not be instantiated.
  6     private Insertion() { }
  7 
  8     public static void sort(Comparable[] a) {
  9         int n = a.length;
 10         for (int i = 0; i < n; i++) {
 11             for (int j = i; j > 0 && less(a[j], a[j-1]); j--) {
 12                 exch(a, j, j-1);
 13             }
 14             assert isSorted(a, 0, i);
 15         }
 16         assert isSorted(a);
 17     }
 18 
 19     public static void sort(Comparable[] a, int lo, int hi) {
 20         for (int i = lo; i <= hi; i++) {
 21             for (int j = i; j > lo && less(a[j], a[j-1]); j--) {
 22                 exch(a, j, j-1);
 23             }
 24         }
 25         assert isSorted(a, lo, hi);
 26     }
 27 
 28    
 29     public static void sort(Object[] a, Comparator comparator) {
 30         int n = a.length;
 31         for (int i = 0; i < n; i++) {
 32             for (int j = i; j > 0 && less(a[j], a[j-1], comparator); j--) {
 33                 exch(a, j, j-1);
 34             }
 35             assert isSorted(a, 0, i, comparator);
 36         }
 37         assert isSorted(a, comparator);
 38     }
 39 
 40     public static void sort(Object[] a, int lo, int hi, Comparator comparator) {
 41         for (int i = lo; i <= hi; i++) {
 42             for (int j = i; j > lo && less(a[j], a[j-1], comparator); j--) {
 43                 exch(a, j, j-1);
 44             }
 45         }
 46         assert isSorted(a, lo, hi, comparator);
 47     }
 48 
 49 
 50     public static int[] indexSort(Comparable[] a) {
 51         int n = a.length;
 52         int[] index = new int[n];
 53         for (int i = 0; i < n; i++)
 54             index[i] = i;
 55 
 56         for (int i = 0; i < n; i++)
 57             for (int j = i; j > 0 && less(a[index[j]], a[index[j-1]]); j--)
 58                 exch(index, j, j-1);
 59 
 60         return index;
 61     }
 62 
 63     // is v < w ?
 64     private static boolean less(Comparable v, Comparable w) {
 65         return v.compareTo(w) < 0;
 66     }
 67 
 68     // is v < w ?
 69     private static boolean less(Object v, Object w, Comparator comparator) {
 70         return comparator.compare(v, w) < 0;
 71     }
 72         
 73     // exchange a[i] and a[j]
 74     private static void exch(Object[] a, int i, int j) {
 75         Object swap = a[i];
 76         a[i] = a[j];
 77         a[j] = swap;
 78     }
 79 
 80     // exchange a[i] and a[j]  (for indirect sort)
 81     private static void exch(int[] a, int i, int j) {
 82         int swap = a[i];
 83         a[i] = a[j];
 84         a[j] = swap;
 85     }
 86 
 87     private static boolean isSorted(Comparable[] a) {
 88         return isSorted(a, 0, a.length - 1);
 89     }
 90 
 91     // is the array sorted from a[lo] to a[hi]
 92     private static boolean isSorted(Comparable[] a, int lo, int hi) {
 93         for (int i = lo+1; i <= hi; i++)
 94             if (less(a[i], a[i-1])) return false;
 95         return true;
 96     }
 97 
 98     private static boolean isSorted(Object[] a, Comparator comparator) {
 99         return isSorted(a, 0, a.length - 1, comparator);
100     }
101 
102     // is the array sorted from a[lo] to a[hi]
103     private static boolean isSorted(Object[] a, int lo, int hi, Comparator comparator) {
104         for (int i = lo + 1; i <= hi; i++)
105             if (less(a[i], a[i-1], comparator)) return false;
106         return true;
107     }
108 
109    // print array to standard output
110     private static void show(Comparable[] a) {
111         for (int i = 0; i < a.length; i++) {
112             StdOut.println(a[i]);
113         }
114     }
115 }

2.3 插入排序算法性能分析

  • 对于长度为N且主键不重复的数组,平均情况下需要~N2/4次比较和~N2/4次交换,最坏情况下(逆序数组)需要~N2/2次比较和~N2/2次交换,最好情况下(数组已经有序)需要~N-1次比较和0次交换;
  • 比较的总次数总是在交换的总次数上加一个额外项,该项为N减去被插入的元素正好是已知排序中的最小元素的次数;
  • 插入排序算法适用于部分有序的数组:1)数组中每个元素的距离离它的终点位置都不远;2)一个有序的大数组接一个小数组;3)数组中只有几个元素的位置不确定;
  • 算法稳定。

3. 希尔排序介绍

希尔(Shell)排序又称为缩小增量排序,它是一种插入排序。它是直接插入排序算法的一种威力加强版。

3.1 希尔排序算法流程

把记录按步长 gap 分组,对每组记录采用直接插入排序方法进行排序。
随着步长逐渐减小,所分成的组包含的记录越来越多,当步长的值减小到 1 时,整个数据合成为一组,构成一组有序记录,则完成排序。

图3 希尔排序的轨迹

 3.2 希尔排序算法代码实现

 1 public class Shell {
 2 
 3     // This class should not be instantiated.
 4     private Shell() { }
 5 
 6     public static void sort(Comparable[] a) {
 7         int n = a.length;
 8 
 9         // 3x+1 increment sequence:  1, 4, 13, 40, 121, 364, 1093, ... 
10         int h = 1;
11         while (h < n/3) h = 3*h + 1; 
12 
13         while (h >= 1) {
14             // h-sort the array
15             for (int i = h; i < n; i++) {
16                 for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
17                     exch(a, j, j-h);
18                 }
19             }
20             assert isHsorted(a, h); 
21             h /= 3;
22         }
23         assert isSorted(a);
24     }
25     
26     // is v < w ?
27     private static boolean less(Comparable v, Comparable w) {
28         return v.compareTo(w) < 0;
29     }
30         
31     // exchange a[i] and a[j]
32     private static void exch(Object[] a, int i, int j) {
33         Object swap = a[i];
34         a[i] = a[j];
35         a[j] = swap;
36     }
37 
38     private static boolean isSorted(Comparable[] a) {
39         for (int i = 1; i < a.length; i++)
40             if (less(a[i], a[i-1])) return false;
41         return true;
42     }
43 
44     // is the array h-sorted?
45     private static boolean isHsorted(Comparable[] a, int h) {
46         for (int i = h; i < a.length; i++)
47             if (less(a[i], a[i-h])) return false;
48         return true;
49     }
50 
51     // print array to standard output
52     private static void show(Comparable[] a) {
53         for (int i = 0; i < a.length; i++) {
54             StdOut.println(a[i]);
55         }
56     }
57 }

3.3 希尔排序算法性能分析

  • 对于长度为N且主键不重复的数组,平均情况下需要~NlgN次比较,最坏情况需要~N1.5次比较;
  • 目前最好的排序序列是1,5,19,41,109...,由Sedgewick提出;
  • 希尔排序算法属于插入排序,但是不稳定。

三、一种洗牌方法的实现(直接上代码)

public class StdRandom
{// 这是一个随机数静态方法库,包含各种随机数生成方法和随机数操作方法
...
   public static void shuffle(int[] a) {
        // 参数可以改成其他数据类型double
        if (a == null) throw new IllegalArgumentException("argument array is null");
        int n = a.length;
        for (int i = 0; i < n; i++) {
            int r = i + StdRandom.uniform(n-i);       
            //StdRandom.uniform(n-i) 方法随机产生1到n-i之间的整数
            int temp = a[i];
            a[i] = a[r];
            a[r] = temp;
        }
    }
}

作者: 邹珍珍(Pearl_zhen)

出处: http://www.cnblogs.com/zouzz/

声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可邮件(zouzhenzhen@seu.edu.cn)咨询.

posted @ 2016-11-23 21:08  Pearl_zhen  阅读(277)  评论(0编辑  收藏  举报