zoukankan      html  css  js  c++  java
  • 数据结构和算法分析 排序

    交换次数和比较次数是衡量一部分排序算法的复杂度的

    排序例子 O(N2)

    这是一个人人都能实现的例子。通过循环判断,将最小的元素放在最前面。

    1. public static void sort(int[] arr) {
    2.       // 每次对比下标i到arr.length-1的值 然后将最小值放在i下标的位置
    3.       for (int i = 0; i < arr.length; i++) {
    4.          // 记录最小值的下标
    5.          int minIndex = i;
    6.          for (int j = i; j < arr.length; j++) {
    7.             if (arr[j] < arr[minIndex]) {
    8.                minIndex = j;
    9.             }
    10.          }
    11.          // 交换最小值与i
    12.          if(i != minIndex)
    13.          {
    14.             System.out.println("交换");
    15.             int temp = arr[i];
    16.             arr[i] = arr[minIndex];
    17.             arr[minIndex] = temp;
    18.          }
    19.       }
    20.    }

    这个排序比较的次数是N*(N-1)/2次,交换的次数取决于原始数组和排序后的数组的重合程度(下标相同且数值相同),这基本是随机的,实际应用中不会有巧合的重合。时间复杂度为O(N2)。这种排序基本书上都不会讲,没有意义。

    冒泡排序O(N2)

    1. public static void bubblingSort(int[] arr)
    2.    {
    3.       for(int i = 0; i < arr.length; i++)
    4.       {
    5.          for(int j = 0; j < arr.length - i - 1; j++)
    6.          {
    7.             if(arr[j] > arr[j+1])
    8.             {
    9.                //交换
    10.                int temp = arr[j];
    11.                arr[j] = arr[j+1];
    12.                arr[j+1] = temp;
    13.             }
    14.          }
    15.  
    16.       }
    17.    }

    这个排序比较的次数是N*(N-1)/2次,交换的次数取决于原始数组的有序程度。如果原始数组就是有序的,那么交换次数为0。如果原始数组是随机的,交换次数也不会太大,因为每一个循环都在讲数组趋于有序,越往后交换的次数越少。

    时间复杂度为O(N2)。

    插入排序O(N2)

    插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

    1. public static void insertSort(int[] arr) {
    2.       int j, key;
    3.       int len = arr.length;
    4.       for (int i = 1; i < len; i++) {
    5.          key = arr[i];
    6.          //将第i个元素 插入到已经有序的前i-1个序列中
    7.          //查询插入位置的时候 从i-1往前进行遍历 不能反过来 因为此处不仅需要遍历 还需要往后平移
    8.          for (j = i - 1; j >= 0; j--) {
    9.             //没有找到插入位置 证明插入位置在前面 那么需要往后平移一位 方便前面的插入
    10.             //第一次平移会平移到i处
    11.             if (arr[j] > key)
    12.             {
    13.                arr[j + 1] = arr[j];
    14.             }
    15.             else
    16.             {
    17.                //找到插入位置 不再往前遍历了
    18.                break;
    19.             }
    20.          }
    21.          //插入到位置
    22.          arr[j + 1] = key;
    23.       }
    24.    }

    可以写的更简单一点:

    1. public static void insertSort(int[] arr) {
    2.       int j, key;
    3.       int len = arr.length;
    4.       for (int i = 1; i < len; i++) {
    5.          key = arr[i];
    6.          for (j = i - 1; j >= 0&&arr[j] > key; j--) {
    7.             arr[j + 1] = arr[j];
    8.          }
    9.          arr[j + 1] = key;
    10.       }
    11.    }

    这个排序比较的次数取决于原始数组的有序程度,最差是N*(N-1)/2次,如果原始数组是有序的,那么可以达到N。

    交换的次数也取决于原始数组的有序程度。如果原始数组就是有序的,那么交换次数为0。

    所以如果预先就是排好序的,那么它的时间时间复杂度可以达到O(N)。

    相对于复杂的Nlog(N)排序算法,数据量比较小而且基本有序的集合比较适合这些排序算法。

    上面的这种插入排序也叫直接插入排序,还有:

    折半插入排序:其它部分不变,只是寻找插入位置的时候,使用折半查找算法。

    希尔算法:下一章节讲述。

    希尔算法O(N2)

    希尔算法是基于插入排序的一下两点性质而提出改进方法的:
        *插入排序在对几乎已经排好的数据操作时,效率高,即可达到线性排序的效率;
        *但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;

    希尔排序的基本思想是:

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

    1. public static void shellSort(int[] arr) {
    2.       int len = arr.length;
    3.       int i,j;
    4.       //循环定义步长
    5.       for (int gap = len / 2; gap > 0; gap = gap / 2) {
    6.          //从i=gap开始 因为gap之前的序列都只有一个值 是排序好的
    7.          for(i = gap; i < len; i++)
    8.          {
    9.             int temp = arr[i];
    10.             for(j = i ; j>=gap&&temp < arr[j-gap]; j = j - gap)
    11.             {
    12.                arr[j] = arr[j-gap];
    13.             }
    14.             arr[j] = temp;
    15.          }
    16.       }
    17.    }

    希尔排序相对于直接插入排序的优势:

    { 2, 5, 8, 4, 6, 1, 9 } 我们要把1移动到最前面,如果使用直接插入排序和折半插入排序(折半插入排序只能减少插入插入点的对比次数,不能减少交换次数),都需要交换5次之多。

    如果使用希尔排序,第一次交换后1和8互换,第二次交换后就会到首位。只需要两次交换。

    希尔排序的增量序列和时间复杂度:

    • 希尔增量序列:首先,我们上面演示的例子使用的增量序列叫希尔增量序列。它的最坏情况下的复杂度为O(N2)。
    • Hibbard 增量序列:hi=2i−1,Hibbard 增量序列的递推公式为: h1=1,hi=2hi−1+1,Hibbard 增量序列的最坏时间复杂度为 Θ(N3/2);平均时间复杂度约为 O(N5/4)。
    • Sedgewick 增量序列,通项公式为: hi=max(94j−92j+1,4k−32k+1),Sedgewick 增量序列的最坏时间复杂度为 O(N4/3);平均时间复杂度约为 O(N7/6)。

    Sedgewick 增量序列是目前表现最好的增量序列,但是随着数学的研究,可能会有更好的增量序列出现。

    对希尔排序的不同增量序列的复杂度研究设计比较多的数学公式,我就不研究了。

    希尔算法的性能在实践中是可以完全接受的,即使对数以万计的N也是如此,所以如果数据量不是非常大,希尔算法的使用还是非常广泛的。

     

  • 相关阅读:
    java线程小结1
    String和StringBuffer
    java队列的实现
    java栈的实现
    java链表
    this与super关键字总结
    JVM内存杂记1
    面试题18:删除链表节点
    面试题17:打印从 1 到最大的 n 位数
    面试题16:数值的整数次方
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7183754.html
Copyright © 2011-2022 走看看