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

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 算法进阶之路(二):递归、归并排序和快速排序,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、递归

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

真题分析

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

二、归并排序

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)

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

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

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

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