冒泡排序的优化与快速排序

漫画:什么是冒泡排序?
快速排序分治法

冒泡排序

冒泡排序的思想,相邻的元素两两比较,根据大小来交换元素的位置
原始的冒泡排序是稳定排序。由于该排序算法的每一轮要遍历所有元素,轮转的次数和元素数量相当,所以时间复杂度是O(N^2) 。

int[] array = new int[]{5,8,6,3,9,2,1,7};

//冒泡排序
public void BubbleSort1(int arr[]){
        for (int i = 0; i <arr.length; i++){
            System.err.println("i="+i);
            //经过一次排序之后,最后一个元素最大/小 所以第二次只需要比较(arr.length - 1)个元素
            //    i          ,右侧的i个元素有序 所以第i次只需要比较前(arr.length- i)个元素
            for (int j = 0; j < arr.length - 1 - i; j++){
                if (arr[j] > arr[j + 1]){
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;                   
                }
                System.err.println(Arrays.toString(arr));
            }
        }
    }

i=5时(第六轮)
这里写图片描述

整个数列已然是有序的了。可是我们的排序算法仍然继续执行第七轮、第八轮。

优化方案1

如果我们能判断出数列已经有序,并且做出标记,剩下的几轮排序就可以不必执行,提早结束工作。

    public void BubbleSort2(int arr[]){
        for (int i = 0; i <arr.length; i++){
            System.err.println("i="+i);
            //有序标记,每一轮的初始是true
            boolean  isSorted = true;
            //经过一次排序之后,最后一个元素最大/小 所以第二次只需要比较(arr.length - 1)-1
            //    i         最后i个元素最大/小(有序) 所以第i次只需要比较(arr.length-1) - i
            for (int j = 0; j < arr.length - 1 - i; j++){
                if (arr[j] > arr[j + 1]){
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    //只要有交换就说明整体任然无序
                    isSorted = false;
                }
            }

            if(isSorted){//没有任何交换,整体有序,跳出循环
                break;
            }
            System.err.println(Arrays.toString(arr));
        }
    }

...........
i=5
[1, 2, 3, 5, 6, 7, 8, 9]
i=6(没有任何交换,整体有序,跳出循环)

优化方案2

{3,4,2,1,5,6,7,8};
这个数列的特点是前半部分(3,4,2,1)无序,后半部分(5,6,7,8)有序

定义一个变量n来保存一趟交换中最后一次发生交换的位置,并把它传递给下一趟交换

public void BubbleSort3(int arr[]){
        //记录最后一次交换的位置
        int lastExchangeIndex =0;
        //无序数列的边界,每次比较只需要比到这里为止
        int sortBorder=arr.length - 1;
        for (int i = 0; i <arr.length; i++){
            System.err.println("i="+i);
            //有序标记,每一轮的初始是true
            boolean  isSorted = true;
            for (int j = 0; j < sortBorder; j++){
                if (arr[j] > arr[j + 1]){
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    //有元素交换,所以不是有序,标记变为false
                    isSorted = false;
                    //把无序数列的边界更新为最后一次交换元素的位置
                    lastExchangeIndex=j;
                }
            }
            sortBorder=lastExchangeIndex;
            if(isSorted){
                break;
            }
            System.err.println(Arrays.toString(arr));
        }
    }
i=0
[3, 2, 1, 4, 5, 6, 7, 8]
i=1
[2, 1, 3, 4, 5, 6, 7, 8]
i=2
[1, 2, 3, 4, 5, 6, 7, 8]
i=3

快排

1.挖坑法
2.指针交换法
3.非递归实现:因为我们代码中一层一层的方法调用,本身就是一个函数栈。每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。
所以,我们可以把原本的递归实现转化成一个栈的实现,在栈当中存储每一次方法调用的参数:
这里写图片描述

挖坑法代码优化
    public void quickSort(int[] arr, int startIndex, int endIndex) {
        // 递归结束条件:startIndex大等于endIndex的时候
        if (startIndex >= endIndex) {
            return;
        }
        // 得到基准元素位置
        int pivotIndex = partition(arr, startIndex, endIndex);
        // 用分治法递归数列的两部分
        quickSort(arr, startIndex, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, endIndex);
    }

    private int partition(int[] arr, int startIndex, int endIndex) {
        // 取第一个位置的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;
        // 坑的位置,初始等于pivot的位置
        // int index = startIndex;
        // 大循环在左右指针重合或者交错时结束
        do {
            while ((arr[right] > pivot) && (left < right))
                right--;
            if (left < right) {
                arr[left] = arr[right];
                left++;
            }
            while ((arr[left] < pivot) && (left < right))
                left++;
            if (left < right) {
                arr[right] = arr[left];
                right--;
            }
        } while (left < right);
        arr[right] = pivot;
        return right;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值