插入排序(dart实现)

插入排序

[toc]

把数字插入合适的位置

1.执行流程

  1. 在执行过程中,插入排序会讲序列分为两部分
    1. 头部是已经排好序的,尾部是待排序的
  2. 从头开始扫描每一个元素
    1. 每当扫描到一个原生,就将它插入到头部合适的位置,使得头部数据依然保持有序

2.dart代码


import 'sort.dart';

class InsertSort > extends Sort{
  @override
  sort() {
    for (var begin = 1; begin < list.length; begin++) {
      int cur = begin;
      while (cur >0 &&cmpWithIndex(cur, cur-1)<0) {
        swap(cur, cur-1);
        cur -= 1;
      }
      
    }
  }
  
}

2.1校验



main(List args) {
  // List list = IntergerTools.random(10000, 1, 20000); //生成10000个数,最小是1,最大是20000
  List list = IntergerTools.random(10, 1, 20); //生成10000个数,最小是1,最大是20000
  List sortList = [
    // HeapSort(),
    // SelectSort(),
    // BubbleSort2(),
    // BubbleSort1(),
    // BubbleSort(),
    InsertSort(),
    // InsertSort1()
  ];
  testSorts(list, sortList);
}

void testSorts(List list, List sorts) {
  print('排序前 :$list');
  for (Sort sort in sorts) {
    List newList = List.from(list);
    sort.setList(newList);
    sort.sortPrint();
    Asserts.test(IntergerTools.isAscOrder(sort.list));
     print('排序后 :${sort.list}');
  }
  sorts.sort(); //比较
  for (Sort sort in sorts) {
    print(sort);
  }
}

结果

排序前 :[11, 19, 18, 15, 5, 5, 13, 10, 14, 17]
排序后 :[5, 5, 10, 11, 13, 14, 15, 17, 18, 19]
【InsertSort】
耗时:0.001s (1ms)     比较:41    交换:24
-----------------

3. 优化1

思路是将[交换]转为[挪动]

  1. /// 先将待插入的元素备份
  2. /// 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置
  3. /// 将待插入元素放到最终的合适位置

3.1代码

///
/// Author: liyanjun
/// Date: 2020-11-01 16:13:16
/// FilePath: /algorithm/sort/insert_sort1.dart
/// Description: 插入排序的优化1:将交换改为挪动
/// 思路是将[交换]转为[挪动]
/// 先将待插入的元素备份
/// 头部有序数据中比待插入元素大的,都朝尾部方向挪动1个位置
/// 将待插入元素放到最终的合适位置
///

import 'sort.dart';

class InsertSort1> extends Sort{
  @override
  sort() {
    for (var begin = 1; begin < list.length; begin++) {
      int cur = begin;
      T v = list[cur];
      while (cur >0 &&cmpElement(v, list[cur-1])<0) {
         list[cur] = list[cur-1];
        cur -= 1;
      }
      list[cur] = v;
    }
  }
  
}

3.2 校验


main(List args) {
  // List list = IntergerTools.random(10000, 1, 20000); //生成10000个数,最小是1,最大是20000
  List list = IntergerTools.random(10, 1, 20); //生成10000个数,最小是1,最大是20000
  List sortList = [
    // HeapSort(),
    // SelectSort(),
    // BubbleSort2(),
    // BubbleSort1(),
    // BubbleSort(),
    // InsertSort(),
    InsertSort1()
  ];
  testSorts(list, sortList);
}

void testSorts(List list, List sorts) {
  print('排序前 :$list');
  for (Sort sort in sorts) {
    List newList = List.from(list);
    sort.setList(newList);
    sort.sortPrint();
    Asserts.test(IntergerTools.isAscOrder(sort.list));
     print('排序后 :${sort.list}');
  }
  sorts.sort(); //比较
  for (Sort sort in sorts) {
    print(sort);
  }
}

结果

排序前 :[18, 18, 8, 16, 1, 16, 7, 16, 9, 2]
排序后 :[1, 2, 7, 8, 9, 16, 16, 16, 18, 18]
【InsertSort1】
耗时:0.001s (1ms)     比较:46    交换:0
-----------------

4. 优化2

利用二分查找优化

  • 如果是一个无需数组,从第0个位置开始遍历搜索,平均时间复杂度:O(n)
  • 如果是有序数组,可以使用二分搜索,最坏时间复杂度:O(logn)

4.1 思路

二分的思路

  1. 假设在[begin, end)范围内搜索某个元素v, mid == (begin + end)/2
  2. 如果v < m,去[begin, mid)范围内二分搜索
  3. 如果v>m,去[mid+1,end)范围内二分搜索
  4. 如果v== m,直接返回mid

插入的思路

  1. 在元素v的插入过程中,可以先二分搜索出合适的插入位置,然后再将元素V插入
  2. 然后挪动运输

4.2 代码


import 'sort.dart';

class InsertSort2> extends Sort{
  @override
  sort() {
    for (var begin = 1; begin < list.length; begin++) {
      _insert(begin, _search(begin));
    }
  }
  
    /**
     * 将source位置的元素插入到dest位置
     * @param source
     * @param dest
     */
  _insert(int source,int dest){
    T v = list[source];
    for (int i = source; i > dest; i--) {
            list[i] = list[i - 1];
        }
        list[dest] = v;
  }

  /**
     * 利用二分搜索找到 index 位置元素的待插入位置
     * 已经排好序数组的区间范围是 [0, index)
     * @param index
     * @return
     */
     int _search(int index) {
        int begin = 0;
        int end = index;
        while (begin < end) {
            int mid = (begin + end) >> 1;
            if (cmpWithIndex(index, mid) < 0) {
                end = mid;
            } else {
                begin = mid + 1;
            }
        }
        return begin;
    }
}

4.3 校验


main(List args) {
  // List list = IntergerTools.random(10000, 1, 20000); //生成10000个数,最小是1,最大是20000
  List list = IntergerTools.random(10, 1, 20); //生成10000个数,最小是1,最大是20000
  List sortList = [
    // HeapSort(),
    // SelectSort(),
    // BubbleSort2(),
    // BubbleSort1(),
    // BubbleSort(),
    // InsertSort(),
    // InsertSort1(),
    InsertSort2()
  ];
  testSorts(list, sortList);
}

void testSorts(List list, List sorts) {
  print('排序前 :$list');
  for (Sort sort in sorts) {
    List newList = List.from(list);
    sort.setList(newList);
    sort.sortPrint();
    Asserts.test(IntergerTools.isAscOrder(sort.list));
     print('排序后 :${sort.list}');
  }
  sorts.sort(); //比较
  for (Sort sort in sorts) {
    print(sort);
  }
}

结果

排序前 :[16, 15, 10, 3, 7, 18, 2, 18, 3, 6]
排序后 :[2, 3, 3, 6, 7, 10, 15, 16, 18, 18]
【InsertSort2】
耗时:0.001s (1ms)     比较:41    交换:0
-----------------

4.4 注意

需要注意的是,使用了二分搜索后,只是减少了比较次数,但插入排序的平均时间复杂度依然是0(n^2)

5.逆序对

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 这个有序对称为 A 的一个逆序对,也称作逆序数

数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。

插入排序的时间复杂度与逆序对的数量成正比关系

逆序对的数量越多,插入排序的时间复杂度越高

  • 最坏的、平均时间复杂度:O(n^2)
  • 最好时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 属于稳定排序

当逆序对的数量极少时,插入排序的效率特别高,甚至速度比O(nlogn)级别的还要快

建议:数据量不是很大的时候,插入排序的效率也是非常好的

你可能感兴趣的:(插入排序(dart实现))