排序算法:计数排序、快速排序、三路快排、冒泡排序、选择排序、插入排序

导读:本篇文章讲解 排序算法:计数排序、快速排序、三路快排、冒泡排序、选择排序、插入排序,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

排序算法:计数排序、三路快排、冒泡排序、选择排序、插入排序

1. 冒泡/选择/插入

冒泡 / 选择 / 插入

2. 计数排序

算法思想
用空间换时间,并不是基于元素的比较,而是利用数组的索引确定元素的位置
统计每个元素的个数sum,将sum放入与元素相同的下标位置处
数组中元素处于一个区间内,创建一个length等于max-min+1的数组
算法原理

  1. 找出数组nums中最大值max和最小值min,创建一个新数组arr[max-min+1]
  2. 遍历数组中第i个元素nums[i],将nums[i]与arr的下标相同的位置处arr[nums[i]-min] += 1
  3. 将原数组修改为排序后的数组
    在这里插入图片描述
    代码实现
/*
	计数排序
*/
public  void countSort(int[] nums){
        //1.获取数组最大/小值
        int max = Arrays.stream(nums).max().getAsInt();
        int min = Arrays.stream(nums).min().getAsInt();
        //2.创建新数组
        int[] arr = new int[max-min+1];
        //3.遍历元素个数,将个数填入与元素对应的下标处
        // 找到元素与索引对应的位置,让索引处的值+1
        for (int i = 0; i < nums.length; i++) {
            arr[nums[i]-min] += 1;
        }
        //4.填充目标数组
        int curIndex = 0;//记录遍历填充数组的索引
        for (int i = 0; i< arr.length; i++) {
            while (arr[i]>0){
            	//原数组元素-min = 存储索引  原数组元素 = 存储索引 + min
                nums[curIndex++] = i+min;
                arr[i]--;
            }
        }
    }

3. 快速排序

算法思想
从数组中找一个基准值 v,使得每次排序后的数组中 比v小的都在v的左边,比v大的都在v的右边
放好 v 的位置后,再对左边分区or右边分区排序
在这里插入图片描述
算法原理

  1. 选取一个元素作为基准值。可以使用random随即获取一个元素作为基准值,防止在数组偏向顺序or逆序时 快排 性能退化
  2. 将 大于等于 v 的元素放到 v 的右边,小于 v 的放在 v 的左边,一次操作称为分区
  3. 每次分区后,分别递归地对两边的区域进行分区,直到排序完成
    在这里插入图片描述
    在这里插入图片描述

代码实现

	/**
     * 快速排序
     * @param nums 目标数组
     */
    public void quickSort(int[] nums){
        quickSort(nums, 0, nums.length-1);
    }

    /**
     * 快速排序
     * @param nums 目标数组
     * @param left 左边界
     * @param right 右边界
     */
    private   void quickSort(int[] nums, int left, int right) {
        //递归终止条件
        if(left>=right){
            return;
        }

        //随机化基准值,防止遇顺序或逆序数组性能退化
        int ran = left +1 + random.nextInt(right-left);
        swap(nums,left,ran);

        //递归操作
        int val = nums[left];//找一个基准值
        int i = left + 1; //当前操作元素的索引
        int j = left; //指向 小于V 的最后一个元素的索引
        for (; i <= right; i++) {
            if(nums[i]<val){
                swap(nums,i,++j);
            }
        }
        if(j!=left){
            swap(nums,j, left);
        }
        // 递归调用
        quickSort(nums, left, j - 1);
        quickSort(nums, j + 1, right);
    }
    
    //交换数组中的两个元素
	private  void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

4. 双路快排

单路快排是将=v的元素放在左侧or右侧
数组中有大量重复元素时,会导致左右两侧元素数量不平衡,性能下降
如图所示:
在这里插入图片描述
双路快排的思想是将等于v 的元素平均分配在两侧
步骤:

  1. 建立索引
    left 左边界,
    right 右边界
    i 判断所指元素是否< V
    j 判断所指元素是否> V
  2. 选取随机元素作为基准值
  3. 遍历数组
    循环 若 i 所指元素 < v,指针后移 ( 当i>right 结束)
    循环 若 j 所指元素 > v,指针前移 ( 当j<left+1 结束)
    两次循环结束,交换i,j位置的元素, i++; j–
    当 i 与 j 重合后,结束本次排序, j所指位置就是=v的,直接与nums[left]交换
  4. 递归调用在这里插入图片描述

代码实现:

/**
* 双路快排
* @param nums 目标数组
*/
public void sort2Ways(int[] nums){
   if (nums == null || nums.length == 0) {
       throw new IllegalArgumentException("nums is empty");
   }
   sort2Way(nums, 0, nums.length - 1);
}

private void sort2Way(int[] nums, int left, int right) {
   //递归到底
   if (left >= right) {
       return;
   }

   //随机化基准值,防止遇顺序或逆序数组性能退化
   int ran = left + 1 + random.nextInt(right - left);
   swap(nums, left, ran);

   //选取基准值
   int v = nums[left];
   //创建辅助索引
   int i = left + 1;//比较当前元素是否 < v
   int j = right;   //比较当前元素是否 > v
   while (true){
       //若 i 所指元素 < v,将i后移,当 nums[i] >v && i>right时结束遍历
       while (nums[i]<v && i<=right){
           i++;
       }
       //若 j 所指元素 > v,将j后移,当nums[j]<v && j<left+1时结束遍历
       while (nums[j]>v && j>=left+1){
           j--;
       }
       if(i>j){
           break;
       }
       //将左边的大值与右边的小值交换
       swap(nums,i,j);
       //交换完,指针后移,继续遍历
       i++;
       j--;
   }
   //找到基准值的位置-->每次交换完后j所指的位置
   swap(nums, left, j);
   //对分区分别排序
   sort2Way(nums, left, j-1);
   sort2Way(nums, j+1, right);
}


private void swap(int[] arr, int a, int b) {
   int temp = arr[a];
   arr[a] = arr[b];
   arr[b] = temp;
}

5. 三路快排

重复元素越多,三路快排效率越高
算法思想
将数组分为 <V=V>V 三个部分
在这里插入图片描述
在遍历完成后,将数组划分为3个部分,对<V 、>V的部分 递归调用三路快排
算法原理

  1. 建立辅助索引
    lt 小于V的最后一个元素索引 (lt是<v和=v的分界)
    gt 大于V的第一个元素索引 (gt是>v和=v的分界)
    i 当前遍历的索引
    在这里插入图片描述

    //操作元素
    int i = left + 1; 
    //小于val的最后一个元素  注意: lt不能使用left-1,否则会在第一次交换时出问题
    int lt = left;  
    //大于val的第一个元素,    虚拟位置,初始并没有大于val的区域
    int gt = right + 1;
    
  2. 遍历数组

    1. 若当前 i 所指元素 等于 V ,直接跳过.
    i++;
    

    在这里插入图片描述
    2. 若当前 i 所指元素 小于 V, 将该元素与 ==v 部分的第一个元素做交换.lt指针后移 ,i 前移

    swap(nums, i, lt + 1);
    lt++;
    i++;
    

    在这里插入图片描述
    3. 若当前 i 所指元素 大于 V ,将该元素与 >V 区域第一个元素的前一个元素(nums[gt-1])做交换.
    注意:此时 i 不用++ ,因为是和 gt-1 处换过来的元素,还没有遍历过

    swap(nums, i, gt - 1);
    gt--;
    

    在这里插入图片描述
    4. 当 i 与 gt 重合时,遍历结束
    在这里插入图片描述
    此时将left处的V与 lt 所指的元素 交换,就完成了一轮排序.
    在这里插入图片描述
    5. 之后对 >V 和 <V 的部分递归调用三路排序的方法,==V 部分的第一次就排好了.

代码实现

	/**
     * 三路快排
     * @param nums array
     */
    public void sort3Ways(int[] nums) {
        if (nums == null || nums.length == 0) {
            throw new IllegalArgumentException("nums is empty");
        }
        sort3Way(nums, 0, nums.length - 1);
    }

    /**
     * 三路快排递归方法
     * @param left  左边界
     * @param right 右边界
     * @nums array
     */
    private void sort3Way(int[] nums, int left, int right) {
        //递归到底
        if (left >= right) {
            return;
        }

        //随机化基准值,防止遇顺序或逆序数组性能退化
        int ran = left + 1 + random.nextInt(right - left);
        swap(nums, left, ran);

        //递归操作
        int val = nums[left]; //基准元素
        int i = left + 1; //操作元素
        int lt = left;   //小于val的最后一个元素  注意: lt不能使用left-1,否则会在第一次交换时出问题
        int gt = right + 1;//大于val的第一个元素,    虚拟位置,初始并没有大于val的区域
        //i<=right  nums.length-1=right
        for (; i <= right && i < gt; i++) {
            int curElem = nums[i];
            if (curElem == val) {
                //1.若当前元素 = 基准元素val,指针后移
                continue;
            } else if (curElem < val) {
                //2.当前元素 < 基准元素
                swap(nums, i, lt + 1);
                lt++;
            } else {
                //3.当前元素 > 基准元素
                swap(nums, i, gt - 1);
                gt--;
                //i指向的是交换后的未遍历的元素,不用++
                i--;
            }
        }
        swap(nums, left, lt);
        sort3Way(nums, left, lt - 1);
        sort3Way(nums, gt, right);
    }

    private void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15592.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!