算法——排序之简单排序

来源:互联网 发布:批量注册淘宝小号论坛 编辑:程序博客网 时间:2024/06/14 04:17

排序是将一组对象按照某个逻辑顺序重新排列的过程。现在计算机的广泛使用使得数据无处不在,整理数据就变得非常重要了。而整理数据的第一步往往都是进行排序。


简单排序

冒泡排序

原理:

将所有临近的两个的对象按照某个逻辑一一进行比较,通常是由大到小或小到大,比较之后交换两个对象的位置。

反复操作,最终即可排序成功。


如上图所示,我们发现,进行一次循环,冒泡排序只能确定一个对象的位置。第一次循环只能够确定一个对象的位置。所以我们需要多少次循环呢?我们就可以的得出,我们总共需要n-1次循环。因为n-1次循环能确定n-1个对象的位置,这时候自然最后一个对象的位置也确定了。

代码如下:

public static void sort(Comparable[] a) {for (int i = 0; i < a.length - 1; i++) {for (int j = 0; j < a.length - 1; j++) {if (less(a[j + 1], a[j])) {swap(a, j, j + 1);}}}}
实际也就是依靠两层循环,外层控制循环次数,内层逐个进行比较。


冒泡排序的优点就是简单,空间复杂度低。缺点就是慢!无用功多。

时间复杂度O(n^2)


选择排序

原理:

首先找到当前数组中最小的元素,将这个元素和第一个元素进行交换位置。其次,在剩下的数组中找到最小的元素,将它和数组中第二个元素进行位置的交换。依次类推,就可以达到排序的效果。简单来说就是每次都选最小的那个,放在前面。

代码:

public static void sort(Comparable[] a) {for (int i = 0; i < a.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < a.length; j++) {if (less(a[j], a[minIndex])) {minIndex = j;}}swap(a, minIndex, i);}}

选择排序将数组分成两个部分,已经排序好的和未排序两部分。每次都从未排序部分中找到最小的元素,增加到排好序的末尾。

同样的,外层循环控制次数,内层循环找到最小的元素。外层循环,因为只要找到n-1个元素的位置,第n个元素的位置就直接确定了。所以外层循环需要n-1次。

我个人认为选择排序是最简单理解的排序之一。

时间复杂度O(n^2)


插入排序

插入排序就和平常打扑克牌一样。整理扑克牌的一般使用的就是插入排序。整理方法是一张一张来,将每一张牌插入其他已经有序的牌中的适当位置。当然,在数组操作中,如果需要给某个元素插入,腾出空间,则需要将其他元素向后移动一位。


代码“:

public static void sort(Comparable[] a) {for (int i = 1; i < a.length; i++) {for (int j = i - 1; j >= 0 && less(a[j+1], a[j]); j--) {swap(a, j, j + 1);}}}
这个代码简洁,但是却不容易让人看懂。不个人不推荐这么做。而是应该将思路表达的清晰。

改变:

public static void sort(Comparable[] a) {for (int i = 1; i < a.length; i++) {int position = findPosition(a, i); // 找到应该插入的位置Comparable tempI = a[i];for (int j = i - 1; j >= position; j--) { // 为插入的位置腾出空间,向后挪动a[j + 1] = a[j];}a[position] = tempI; // 插入值}}public static int findPosition(Comparable[] a, int i) {for (int j = i - 1; j >= 0; j--) {if (less(a[j], a[i])) return j + 1;}return 0;}

和选择排序不同,插入排序所需的时间和取决于输入元素的初始顺序。对一个已经接近有序的数组进行排序,会比对顺序随机的数组进行排序快得多。

平均时间复杂度:O(n^2)。最好情况时间复杂度O(n)。

插入排序对于部分有序的数组来说非常高效,这是它最大的优点。


希尔排序

希尔排序是对插入排序的一个优化。对于大规模的乱序数组,插入排序会比较缓慢,因为他需要慢慢的向前找到位置,所以元素只能一点一点的从数组中移动到另一端。

希尔排序本质就是分组的插入排序,但是它并不需要一个一个比较,而是能将元素一下子移到一个比较远的地方。

原理:

先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。


上图中,方框指向的是同一组,也就是将同一组数据排序。当分组越来越少,一组中元素越来越多的时候,排序就基本完成了。而最终,当所有元素都在同一组中的时候,就是简单插入排序。因为这个时候数组已经接近有序了,所以排序起来非常快。


代码:

public static void sort(Comparable[] a) {int gap = 1;while (gap < a.length /3) gap = 3*gap + 1;while (gap >= 1) {for (int i = 0; i < gap; i++) {for (int j = i + gap; j < a.length; j += gap) {int position = findPosition(a, i, j, gap);Comparable tempJ = a[j];for (int k = j - gap; k >= position; k -= gap) {a[k + gap] = a[k];}a[position] = tempJ;}}gap /= 3;}}public static int findPosition(Comparable[] a, int begin, int current, int gap) {for (int i = current - gap; i >= begin; i -= gap) {if (less(a[i], a[current])) return i + gap;}return begin;}
和插入排序非常类似,仅仅是分组的插入排序。


比较:

代码:

public static void main(String[] args) {  final int NUM = 10000;  Integer[] a1 = new Integer[NUM];  Integer[] a2 = new Integer[NUM];  Integer[] a3 = new Integer[NUM];  Integer[] a4 = new Integer[NUM];  for (int i = 0; i < NUM; i++) {    a1[i] = (int)(Math.random() * NUM);    a2[i] = a1[i];    a3[i] = a1[i];    a4[i] = a1[i];  }    long startTime;  long endTime;  /*   * 交换   * */  startTime = System.currentTimeMillis();   //获取开始时间  switchSort.sort(a2);  assert isSorted(a2);  endTime = System.currentTimeMillis();  System.out.println("交换排序cost: " + (endTime - startTime) + " ms");    /*   * 插入   * */  startTime = System.currentTimeMillis();   //获取开始时间  InsertSort.sort(a3);  assert isSorted(a3);  endTime = System.currentTimeMillis();  System.out.println("插入排序cost: " + (endTime - startTime) + " ms");    /*   * 冒泡   * */  startTime = System.currentTimeMillis();   //获取开始时间  bubbleSort.sort(a1);  assert isSorted(a1);  endTime = System.currentTimeMillis();  System.out.println("冒泡排序cost: " + (endTime - startTime) + " ms");    /*   * shell   * */  startTime = System.currentTimeMillis();   //获取开始时间  ShellSort.sort(a4);  assert isSorted(a4);  endTime = System.currentTimeMillis();  System.out.println("希尔排序cost: " + (endTime - startTime) + " ms");}
运行多次,我们会发现:

当数据量不大的时候,插入排序比交换排序快。而数据量大起来,交换排序就比插入排序更加快了。

当然,如果数组是接近有序的,插入排序的性能会非常强大。

冒泡排序比较缓慢。

而希尔排序的性能非常不错。即使数据量非常大,希尔排序的性能也是不错的。希尔排序的平均时间复杂度是O(n^1.3)左右。相对简单插入排序来说确实是极大的提升。并且希尔排序的代码量并不大。


0 0