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也是如此,所以如果数据量不是非常大,希尔算法的使用还是非常广泛的。

     

  • 相关阅读:
    grunt in webstorm
    10+ Best Responsive HTML5 AngularJS Templates
    响应式布局
    responsive grid
    responsive layout
    js event bubble and capturing
    Understanding Service Types
    To add private variable to this Javascript literal object
    Centering HTML elements larger than their parents
    java5 新特性
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7183754.html
Copyright © 2011-2022 走看看