算法进阶之路(二):递归、归并排序和快速排序

一、递归

递归的本质是利用系统栈的数据结构,完成遍历,因此任何递归行为都可以改为非递归。
递归的时间复杂度分析:
在这里插入图片描述
任何递归的时间复杂度最终都可以使用上面的表达式来表示,其时间复杂度按照公式分析即可。

真题分析

使用递归的方式,判断数组任意范围内的最大值,代码实现如下:
在这里插入图片描述

二、归并排序

1.思路分析

在这里插入图片描述

2.代码实现

public static void main(String[] args) {
        int[] array = new int[]{5, 6, 1, 9, 7, 2, 3, 8, 9};
        process(array, 0, array.length - 1);
        System.out.println(Arrays.toString(array));
}
    
private static void process(int[] arr,int L,int R){
        // 递归出口
        if(arr==null||L==R){
            return;
        }
        // 计算中间的下标
        int M = L+((R-L)>>1);
        // 左边排好序
        process(arr,L,M);
        // 右边排好序
        process(arr,M+1,R);
        // 合并左边和右边,则最终有序
        merge(arr,L,M,R);
    }

    private static void merge(int[] arr,int L,int M,int R){
        // 定义一个新数组,存储排好序的数组
        int[] temp = new int[R-L+1];
        // 定义新数组的下标,从0开始移动
        int i = 0;
        // 定义两个指针,一个从左边开始移动,一个从右边开始移动
        int left = L;
        int right = M+1;
        while(left<=M&&right<=R){
            if(arr[left]<arr[right]){
                temp[i] = arr[left++];
            }else{
                temp[i] = arr[right++];
            }
            i++;
        }
        // 最终会只剩下左边或者右边的一部分数据还没有排好序,此时剩余数字都是有序的,直接拼在后面就行
        // 如果是左边的一部分数据还没有排好序
        while(left<=M){
            temp[i++]=arr[left++];
        }
        // 如果是右边的一部分数据还没有排好序
        while(right<=R){
            temp[i++]=arr[right++];
        }

        // 用临时数组替换原数组中对应的元素
        for(int j=0;j<temp.length;j++){
            arr[L+j]=temp[j];
        }
    }

3.时间复杂度:

在这里插入图片描述

4.非递归方式实现

在这里插入图片描述
其实是自己定义了递归的最小边界mergeSize=1,然后逐渐翻倍,至递归的最大边界mergeSize>=N。

5.面试题试炼

在这里插入图片描述
答案解析:
!

在这里插入图片描述

三、快速排序

快速排序(Quicksort)是对冒泡排序算法的一种改进。快速排序实现的重点在于数组的拆分,通常我们将数组的最后一个元素定义为比较元素,然后将数组中小于比较元素的数放到左边,将大于比较元素的放到右边,

这样我们就将数组拆分成了左右两部分:小于比较元素的数组;大于比较元素的数组。我们再对这两个数组进行同样的拆分,直到拆分到不能再拆分,数组就自然而然地以升序排列了。

1. 快速排序第一版:

每次都以最后一个元素作为比较元素:
在这里插入图片描述在这里插入图片描述

2. 快速排序第二版:

每次排序确定好一批数(即如果存在重复数字的情况,每次将该重复数字的所有数字都确定好位置)的位置。
在这里插入图片描述

    // 荷兰国旗问题,返回左边界和右边界
    private static int[] netherlandsFlag(int[] arr,int L,int R){
        // 将数组中小于n的数放在左边,大于n的数放在右边,等于n的数在中间
        if(L>R){
            return new int[]{-1,-1};
        }
        if(L==R){
            return new int[]{L,R};
        }
        // 定义左边界
        int left = L-1;
        // 定义右边界,因为用R来作为比较的数,因此先把R跳过,最后处理
        int right = R;
        // 定义数组的指针,从L开始右移
        int index = L;
        // 当index移动至R-1时,停止移动
        while(index<right){
            // 如果相等,则直接跳过
            if(arr[index]==arr[R]){
                index++;
            // 小于temp的数和left+1位置的数交换位置,同时index移动至下一位
            }else if(arr[index]<arr[R]){
                swap(arr,index++,++left);
             // 大于temp的数和right位置的数交换位置,同时right指针向左移动一位,index指针不变
            }else if(arr[index]>arr[R]){
                swap(arr,index,--right);
            }
        }
        // 最后将R位置的数和right+1位置的数交换位置
        swap(arr,R,right);
        // 返回等于R的索引的左边界和右边界
        return new int[]{left+1,right};
    }
   // 交换数组arr中L和R位置的数
    private static void swap(int[] arr,int L,int R){
        if(L==R||arr[L]==arr[R]){
            return;
        }
        arr[L]=arr[L]^arr[R];
        arr[R]=arr[L]^arr[R];
        arr[L]=arr[L]^arr[R];
    }
3. 快速排序第三版:

每次在范围内随机取一个数作为比较元素
在这里插入图片描述
时间复杂度:O(N*logN)

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值