• Java实现快速排序


    1.介绍

           快排分为两种:

           1.lomuto分区算法  (快慢指针)(单边)

           2.Hoare分区算法   (前后指针)(双边)

    快排主要思想:选一个基准元素分为两部分,先让左边排一下序再让右边排序 

    2.思路分析

    1.lomuto分区算法 

    默认:最右边的元素作为基准点

    1.设置两个指针(dest , cur),通过使用dest找比基准点大的,cur找比基准点小的

    2.当同时停下并且不相等进行交换,这样会达到一种dest是比基准点小的分界点,左边全是比基准点小的,走到最后一步只需要与基准点交换即可。遍历序列并交换元素:从序列的第一个元素开始,逐个与基准元素进行比较,将小于等于基准元素的元素交换到序列的左侧。

    3.更新基准元素位置:遍历完成后,将基准元素交换到正确的位置上,使得基准元素左侧的元素都小于等于它,右侧的元素都大于它。

    4.分治递归:对基准元素左右两侧的子序列分别重复以上步骤,直到每个子序列只剩下一个元素或为空。

    平均复杂度:O(nlogn)

    最坏情况:O(n^2)

    2.分析普通快排存在的缺点

            1.在最坏情况下,即每次划分都导致一个子序列为空

            我们可以通过随机基准点进行优化,以防止出现这种情况

            2.数据量很少的时候,快排并不有优势,反倒不如插入排序,所以我对其进行了当元素小于10时候,我就使用插入排序。

    3.Hoare分区算法

     默认: 最左边的元素为基准点

    注意点:一定要先移动右指针,在移动左指针,因为右指针找是比基准点小的,谁先走就意味着最后一轮谁过后谁先移动去找另一个,而右指针最后一次交换存的是比基准点大的,左指针是比基准点小的,要是先移动左指针就会使一个比基准点大的元素放在基准点前面     

    3.代码实现

    1.lomuto分区算法 

    1. /*
    2. *实现快速排序(单边循环)
    3. *
    4. * lomuto快速排序
    5. * */
    6. public class sortTest9 {
    7. private int[] data;
    8. public void sort(int[] data){
    9. this.data = data;
    10. dfs(0,data.length - 1);
    11. }
    12. private void dfs(int start, int end) {
    13. //分区的时候只有一个元素结束递归
    14. if (start == end) return;
    15. //分区(已基准点分区)
    16. int j = partition(start,end);
    17. //左分区
    18. dfs(j - 1, end);
    19. //右分区
    20. dfs(start, j + 1);
    21. }
    22. private int partition(int start, int end) {
    23. //定义基准数
    24. int prev = data[end];
    25. int less = start;//慢指针
    26. int great = start;//快指针
    27. for (; great < end - 1; great++) {
    28. if (data[great] < prev){
    29. //防止无意义的空交换
    30. if (great != less) swap(data,less,great);
    31. less++;
    32. }
    33. }
    34. //到最后交换基准数(也就是end的值)和慢指针less的值
    35. swap(data,less,end);
    36. //慢指针所在的位置就是分区的位置
    37. return less;
    38. }
    39. private void swap(int[] data, int less, int great) {
    40. int temp = data[less];
    41. data[less] = data[great];
    42. data[great] = temp;
    43. }
    44. }

    2.Hoare分区算法

    1. /*
    2. *实现快速排序(双边循环)
    3. *
    4. * */
    5. public class sortTest10 {
    6. private int[] data;
    7. public void sort(int[] data){
    8. this.data = data;
    9. dfs(0,data.length - 1);
    10. }
    11. private void dfs(int start, int end) {
    12. int i = start;
    13. int j = end;
    14. //定义基准数
    15. int prev = data[start];
    16. if (i > j){
    17. return;
    18. }
    19. //处理重复元素(相等的也交换)并且双指针都移动
    20. while(i != j){
    21. //后指针找比基准数小的
    22. while(true) {
    23. //等于不等于都一样(基准数左边可以含有等于基准点的数右边也可以)
    24. if (i < j && data[j] < prev) {
    25. break;
    26. }
    27. j--;
    28. }
    29. //前指针找比基准数大的
    30. while(true){
    31. if (i < j && data[i] > prev) break;
    32. i++;
    33. }
    34. swap(i,j);
    35. j--;
    36. i++;
    37. }
    38. //确定了两指针指向头一个与基准数交换
    39. swap(j,start);
    40. //左分区
    41. swap(start,j - 1);
    42. //右分区
    43. swap(j + 1,end);
    44. }
    45. //用来交换元素
    46. private void swap(int less, int great) {
    47. int temp = data[less];
    48. data[less] = data[great];
    49. data[great] = temp;
    50. }
    51. }

    3.缺点优化

            1.随机基准点

            2.处理重复元素

            3.当递归到元素小于10的时候转为插入排序

    1. import java.util.Arrays;
    2. import java.util.Random;
    3. public class sortTest11 {
    4. private static final int INSERTION_SORT_THRESHOLD = 10;
    5. public static void quickSort(int[] array) {
    6. quickSort(array, 0, array.length - 1);
    7. }
    8. private static void quickSort(int[] array, int start, int end) {
    9. if (start < end) {
    10. // 当元素个数小于等于阈值时,使用插入排序
    11. if (end - start + 1 <= INSERTION_SORT_THRESHOLD) {
    12. insertionSort(array, start, end);
    13. } else {
    14. // 随机选择基准值,并进行划分
    15. int pivotIndex = randomPartition(array, start, end);
    16. // 对基准值左侧的子数组递归排序
    17. quickSort(array, start, pivotIndex - 1);
    18. // 对基准值右侧的子数组递归排序
    19. quickSort(array, pivotIndex + 1, end);
    20. }
    21. }
    22. }
    23. private static int randomPartition(int[] array, int start, int end) {
    24. int randomIndex = new Random().nextInt(end - start) + start;
    25. swap(array, randomIndex, start);
    26. return partition(array, start, end);
    27. }
    28. private static int partition(int[] array, int start, int end) {
    29. int pivot = array[start];
    30. int left = start + 1;
    31. int right = end;
    32. while (left <= right) {
    33. while (left <= right && array[left] <= pivot) {
    34. left++;
    35. }
    36. while (left <= right && array[right] > pivot) {
    37. right--;
    38. }
    39. if (left < right) {
    40. swap(array, left, right);
    41. left++;
    42. right--;
    43. }
    44. }
    45. swap(array, start, right);
    46. return right;
    47. }
    48. private static void insertionSort(int[] array, int start, int end) {
    49. for (int i = start + 1; i <= end; i++) {
    50. int key = array[i];
    51. int j = i - 1;
    52. while (j >= start && array[j] > key) {
    53. array[j + 1] = array[j];
    54. j--;
    55. }
    56. array[j + 1] = key;
    57. }
    58. }
    59. private static void swap(int[] array, int i, int j) {
    60. int temp = array[i];
    61. array[i] = array[j];
    62. array[j] = temp;
    63. }
    64. }

  • 相关阅读:
    java经典笔试题大全(50道含答案)
    matplotlib基操(三)
    dcatadmin批量操作时异步请求获取当前选中的id
    docker简单快速使用上手
    前端文件下载方法总结
    [附源码]Python计算机毕业设计Django校园招聘系统设计
    【数据结构】单值二叉树 & 相同的树 & 翻转二叉树(五)
    LeetCode 2678. 老人的数目
    基于java+springboot+mybatis+vue+elementui的网上书籍购买商城
    DS相关题目
  • 原文地址:https://blog.csdn.net/m0_74749208/article/details/133829893