Java~常见并且特别重要的俩个排序--快速排序(递归、非递归、优化)--归并排序(递归、非递归、处理大数据思想)

28 篇文章 0 订阅

快速排序-原理(不稳定)(重要)

  1. 从待排序区间选择一个数,作为基准值(baseValue)
  2. Partition: 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
  3. 采用分治思想,对左右两个小区间按照同样的方式处理,直到小区间的长度 =1,代表已经有序,或者小区间的长度= 0,代表没有数据

partition操作:

  • 可以选最后一个元素,也可以选最左边的元素,若果要选中间元素,就必须把这个中间元素交换到最前面或者,最后面。
  • 若是选取最左边为基准值,先让right从右往左走找到比基准值小的数据,再让left从左到右走,找比基准值大的数据。
  • 只要只是left下标小于right小标,交换left和right位置的元素。
  • 若是选取最左边为基准值那么在left和right重合位置的下标的元素一定是小于基准值的。(原因代码里讲)
  • 若是选取最右边为基准值那么在left和right重合位置的下标的元素一定是大于基准值的。(原因代码里讲)
  • 接下来把重合位置的元素和基准值进行交换,在返回重合位置的下标。
  • 再针对左右俩个区间,递归进行刚才的partition操作。
    在这里插入图片描述

注意

  • 进行完一次partition操作之后,基准值左边所有元素一定是小于基准值的,基准值右边所有元素一定是大于基准值的
  • 若是选取最左边为基准值,那么一定先让right下标走,若是选取最右边为基准值,那么一定先让left下标先走。**

实现:

//快速排序
    public void quickSort(int[] array) {
        quickSortDeal(array, 0, array.length - 1);
    }

    private void quickSortDeal(int[] array, int left, int right) {
        if(left >= right) {
            return;
        }
        int index = partition(array,left,right);
        quickSortDeal(array,left,index - 1);
        quickSortDeal(array,index + 1, right);
    }

    private int partition(int[] array, int left, int right) {
        //选取最左边为基准值
        int baseVaule = array[left];
        int leftIndex = left;
        int rightIndex = right;
        while (leftIndex < rightIndex) {
            while (leftIndex < rightIndex && array[rightIndex] >= baseVaule) {
                rightIndex --;
            }
            while (leftIndex < rightIndex && array[leftIndex] <= baseVaule) {
                leftIndex ++;
            }
            if(leftIndex < rightIndex) {
                int tmp = array[leftIndex];
                array[leftIndex] = array[rightIndex];
                array[rightIndex] = tmp;
            }
        }
        //此时leftIndex和rightIndex下标的元素一定小于或者等于我们选取的基准值
        //若是因为rightIndex下标导致结束循环,那么rightIndex下标未找到比基准值小的数据,最后导致重合。
        //若是因为leftIndex下标导致结束循环,那么说明rightIndex已经找到比基准值小的元素,最后重合与基准值交换。
        int tmp = array[leftIndex];
        array[leftIndex] = array[left];
        array[left] = tmp;
        return leftIndex;
    }

性能分析:

  • 时间复杂度
    O(NlogN)
  • 空间复杂度
    O(logN)

非递归实现快排:

思想:运用一个栈记住我们需要处理的left和right下标,再将下标出栈,进行partition操作。

实现:

//非递归实现快速排序
    public void quickSortByLoop(int[] array) {
        Stack<Integer> stack = new Stack<>();
        //左边界先入栈
        stack.push(0);
        stack.push(array.length - 1);
        while (!stack.isEmpty()) {
            //右边界先出栈
            int right = stack.pop();
            int left = stack.pop();
            if(left >= right) {
                continue;
            }
            int index = partition(array, left,right);
            //讲左边区间入栈,还要注意左边界先入栈
            stack.push(left);
            stack.push(index - 1);
            //将右边区间入栈
            stack.push(index + 1);
            stack.push(right);
        }
    }
    
    private int partition(int[] array, int left, int right) {
        //选取最左边为基准值
        int baseVaule = array[left];
        int leftIndex = left;
        int rightIndex = right;
        while (leftIndex < rightIndex) {
            while (leftIndex < rightIndex && array[rightIndex] >= baseVaule) {
                rightIndex --;
            }
            while (leftIndex < rightIndex && array[leftIndex] <= baseVaule) {
                leftIndex ++;
            }
            if(leftIndex < rightIndex) {
                int tmp = array[leftIndex];
                array[leftIndex] = array[rightIndex];
                array[rightIndex] = tmp;
            }
        }
        //此时leftIndex和rightIndex下标的元素一定小于或者等于我们选取的基准值
        //若是因为rightIndex下标导致结束循环,那么rightIndex下标未找到比基准值小的数据,最后导致重合。
        //若是因为leftIndex下标导致结束循环,那么说明rightIndex已经找到比基准值小的元素,最后重合与基准值交换。
        int tmp = array[leftIndex];
        array[leftIndex] = array[left];
        array[left] = tmp;
        return leftIndex;
    }

优化快速排序:

  • 快排的性能受基准值的影响很大,如果我们选的基准值接近数组中的中位数,此时左右俩个区间较均衡性能也最好。
  • 所以我们需要优化基准值,随机挑三个数字,取三个数字的中间值作为基准值。

实现:

//优化后的快速排序
    public void betterQuickSort(int[] array) {
        betterQuickSortDeal(array, 0, array.length - 1);
    }

    private void betterQuickSortDeal(int[] array, int left, int right) {
        if(left >= right) {
            return;
        }
        int index = betterQartition(array,left,right);
        betterQuickSortDeal(array,left,index - 1);
        betterQuickSortDeal(array,index + 1, right);
    }

    private int betterQartition(int[] array, int left, int right) {
        int baseVaule = findBetterBaseVaule(array, left, right);
        int leftIndex = left;
        int rightIndex = right;
        while (leftIndex < rightIndex) {
            while (leftIndex < rightIndex && array[rightIndex] >= baseVaule) {
                rightIndex --;
            }
            while (leftIndex < rightIndex && array[leftIndex] <= baseVaule) {
                leftIndex ++;
            }
            if(leftIndex < rightIndex) {
                int tmp = array[leftIndex];
                array[leftIndex] = array[rightIndex];
                array[rightIndex] = tmp;
            }
        }
        int tmp = array[leftIndex];
        array[leftIndex] = array[left];
        array[left] = tmp;
        return leftIndex;
    }

    private int findBetterBaseVaule(int[] array, int left, int right) {
        if(array.length < 3) {
            return array[left];
        }
        int[] nums = new int[3];
        nums[0] = array[left];
        nums[1] = array[right];
        Random rand = new Random();
        //选取(left,right)下标中间的一个随机值
        int ra = rand.nextInt(right) + left + 1;
        nums[2] = array[ra];
        Arrays.sort(nums);
        for (int i = 0; i < array.length; i++) {
            if(array[i] == nums[1]) {
                array[i] = array[left];
                array[left] = nums[1];
                break;
            }
        }
        return array[left];
    }
  • 还有优化方式就是当我们当前这个区间数组元素个数较少时,我们直接进行插入排序,
  • 再就是当我们递归达到一定深度的时候,此时待排区间还有很多时,我们使用堆排序。

归并排序-原理(稳定)(重要)

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并
在这里插入图片描述
实现:

//归并排序
    public void mergeSort(int[] array) {
        //归并排序区间采用左闭右开格式,快排采用左闭右闭格式
        mergeSortHealper(array, 0, array.length);
    }

    private void mergeSortHealper(int[] array, int left, int right) {
        if(right - left <= 1) {
            return;
        }
        int mid = (left + right) / 2;
        mergeSortHealper(array,left,mid);
        mergeSortHealper(array,mid,right);
        merge(array,left,mid,right);
    }
    //将俩个有序序列合并为一个有序序列
    private void merge(int[] array, int left, int mid, int right) {
        int leftIndex = left;
        int rightIndex = mid;
        int[] tmpNums = new int[right - left];
        int tmpIndex = 0;
        while (leftIndex < mid && rightIndex < right) {
            if(array[leftIndex] <= array[rightIndex]) {
                tmpNums[tmpIndex] = array[leftIndex];
                tmpIndex ++;
                leftIndex ++;
            }else {
                tmpNums[tmpIndex] = array[rightIndex];
                tmpIndex ++;
                rightIndex ++;
            }
        }
        while (leftIndex < mid) {
            tmpNums[tmpIndex] = array[leftIndex];
            tmpIndex ++;
            leftIndex ++;
        }
        while (rightIndex < right) {
            tmpNums[tmpIndex] = array[rightIndex];
            tmpIndex ++;
            rightIndex ++;
        }
        for (int i = 0; i < right - left; i++) {
            array[left + i] = tmpNums[i];
        }
    }

性能分析

-时间复杂度
O(NlogN)

-空间复杂度
O(N)

非递归实现:

思想:首先将gap=1也就是此时一组一个元素进行相邻排序合并,再将gap=2进行分组合并,依次类推,直到gap=array.length

实现:

//归并排序的非递归实现
    public void mergeSortByLoop(int[] array) {
        //gap表示多少个元素一组
        for (int gap = 1; gap < array.length; gap *= 2) {
            for (int left = 0; left < array.length; left += 2 * gap) {
                int mid = left + gap;
                int right = mid + gap;
                //防止数据越界访问
                if(mid > array.length) {
                    mid = array.length;
                }
                if(right > array.length) {
                    right = array.length;
                }
                merge(array,left,mid,right);
            }
        }
    }

    //将俩个有序序列合并为一个有序序列
    private void merge(int[] array, int left, int mid, int right) {
        int leftIndex = left;
        int rightIndex = mid;
        int[] tmpNums = new int[right - left];
        int tmpIndex = 0;
        while (leftIndex < mid && rightIndex < right) {
            if(array[leftIndex] <= array[rightIndex]) {
                tmpNums[tmpIndex] = array[leftIndex];
                tmpIndex ++;
                leftIndex ++;
            }else {
                tmpNums[tmpIndex] = array[rightIndex];
                tmpIndex ++;
                rightIndex ++;
            }
        }
        while (leftIndex < mid) {
            tmpNums[tmpIndex] = array[leftIndex];
            tmpIndex ++;
            leftIndex ++;
        }
        while (rightIndex < right) {
            tmpNums[tmpIndex] = array[rightIndex];
            tmpIndex ++;
            rightIndex ++;
        }
        for (int i = 0; i < right - left; i++) {
            array[left + i] = tmpNums[i];
        }
    }

处理大数据思想
外部排序(本质上还是学习归并排序细想):排序过程需要在磁盘等外部存储进行的排序。
前提:内存只有 1G,需要排序的数据有 100G。
因为内存中因为无法把所有数据全部放下,所以需要外部排序,而归并排序是最常用的外部排序。

  1. 先把文件切分成 200 份,每个 512 M。
  2. 分别对 512 M 排序,因为内存已经可以放的下,所以任意排序方式都可以。
  3. 进行 200 路归并,同时对 200 份有序文件做归并过程,最终结果就有序了。
  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值