快速排序(dart实现)

快速排序

[toc]

快速排序1960年由查尔斯安东尼理查德霍尔(Charles Antony Richard Hoare,缩写为C. A. R. Hoare)提出的
作者行业内昵称为东尼霍尔(Tony Hoare)

执行流程

  1. 从序列中选择一个轴点元素 (pivot)
    • 段设每次选择0位置的元素为轴点元素
  2. 利用pivot将序列分割成2个子序列
    • 将小于pivot的元素放在pivot前面(左侧)
    • 将大于pivot的元素放在pivot后面(右侧)
    • 等于pivot的元素放哪边都可以
  3. 对子序列进行1、2操作
    • 直到不能再分割(子序列中只剩下1个元素)

本质:逐渐将每一个元素都转化成轴点元素

选择轴点流程

如图


image.png
  1. 构造两个索引begin和end,begin指向头元素,end指向末尾元素,选择将第一个元素变成轴点6a,备份6a
  2. 从末尾开始扫描,也就是从end开始扫描,发现7>6,让end--,end指向5,如图第2行
  3. 再比较5<6,让5覆盖6a元素的位置,begin++,如图第3行,5是黑色表示是垃圾数据,该位置随时可能会被覆盖
  4. 再开始begin开始扫描,发现8>6,让8覆盖黑色5的位置,原来8的位置变成黑色,如果第4行,执行end--,end指向9
  5. 再比较9>6,直接让end--,end指向4,如果5行
  6. 再比较4<6,让4覆盖8a黑色位置,如图6行,begin++,end变成黑色即为垃圾位置
  7. 再比较8b>6,让8b覆盖4的位置,并执行end--,如图行7
  8. 比较6b=6a,可以不动,也可以移动,这里让6b移动,也就是放入8b黑色的位置,并执行begin++,如图行8
  9. 比较2<6,直接begin++,如图行9
  10. 此时begin==end,将6a放入此位置,轴点生成完成

代码


class QuickSort> extends Sort {
  @override
  sort() {
    int begin = 0;
    int end = list.length;
    _sort(begin,end);
  }

  ///
  /// Author: liyanjun
  /// description:
  ///对[[begin,end)],左闭右开的,范围内元素进行快速排序
  _sort(int begin, int end) {
    //元素必须大于2
    if (end - begin < 2) return;
    int mid = _pivotIndex(begin, end);//O(n)
    _sort(begin, mid); //左边子序列快速排序 
    _sort(mid + 1, end); //右边子序列快速排序
  }

  ///
  /// Author: liyanjun
  /// description:
  /// 构造出 [begin, end) 范围的轴点元素,返回轴点元素的最终位置
  ///
  int _pivotIndex(int begin, int end) {
    //将begin元素备份
    T pivot = list[begin];
    //由于是左闭右开,所以要让end--
    end--;
    while (begin < end) {//确定变换方向后继续执行
      while (begin < end) {//后面扫描
        if (cmpElement(list[end], pivot) > 0) {
          //右边元素大于轴点元素
          end--;
        } else {
          //右边元素小于等于轴点元素
          list[begin++] = list[end];
          break;
        }
      }
      while (begin < end) {//前面扫描
        if (cmpElement(list[begin], pivot) < 0) {
          //左边元素小于轴点元素
          begin++;
        } else {
          //右边元素>=轴点元素
          list[end--] = list[begin];
          break;
        }
      }
    }
    list[begin] = pivot; //// 将轴点元素放入最终的位置
    return begin; //此时begin==end
  }
}

验证


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(),
    // MergeSort(),
    QuickSort()
  ];
  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);
  }
}

执行结果

排序前 :[7, 9, 6, 15, 10, 19, 16, 18, 18, 6]
排序后 :[6, 6, 7, 9, 10, 15, 16, 18, 18, 19]
【QuickSort】
耗时:0.001s (1ms)     比较:22    交换:0
-----------------

时间复杂度

  • 在轴点左右元素数量比较均匀的情况下,同时也是最好的情况
    • 左边,右边
  • 如果轴点左右元素数量极度不均匀,最坏情况
    • 假如左边n-1个,右边1个
    • 左边,右边左边
  • 最好、平均时间复杂度:
  • 最坏时间复杂度:
  • 由于递归调用的缘故,空间复杂度:
  • 属于不稳定排序

为了降低最坏情况的出现概率,一般采取的做法是

随机选择轴点元素

优化代码

 ///
  /// Author: liyanjun
  /// description:
  /// 构造出 [begin, end) 范围的轴点元素,返回轴点元素的最终位置
  ///
  int _pivotIndex(int begin, int end) {
    
  // 随机选择一个元素跟begin位置进行交换
    //因为 Random().nextInt(max)是包括max的,所以应该end-1
        swap(begin, begin + Random().nextInt(end-1-begin));
    //将begin元素备份
    T pivot = list[begin];
    //由于是左闭右开,所以要让end--
    end--;
    while (begin < end) {//确定变换方向后继续执行  思想,掉头用两个while循环
      while (begin < end) {//后面扫描
        if (cmpElement(list[end], pivot) > 0) {
          //右边元素大于轴点元素
          end--;
        } else {
          //右边元素小于等于轴点元素
          list[begin++] = list[end];
          break;
        }
      }
      while (begin < end) {//前面扫描
        if (cmpElement(list[begin], pivot) < 0) {
          //左边元素小于轴点元素
          begin++;
        } else {
          //右边元素>=轴点元素
          list[end--] = list[begin];
          break;
        }
      }
    }
    list[begin] = pivot; //// 将轴点元素放入最终的位置
    return begin; //此时begin==end
  }
}

与轴点相等的元素

cmp 位置的判断分别改为 ≤、≥ 会起到什么效果
假如原数组为
[3,3,3,3,3,3]
如果判断为 ≤、≥ ,则会导致轴点元素处于两端,造成最坏情况的时间复杂度,所以不用

你可能感兴趣的:(快速排序(dart实现))