快速排序和递归

一.快速排序

1.思想

类似于分治法,将数组中的数字分解为规模为原来一半的数组,并且该数组每次分治后,数组分为以某一个基准数字分为两部分的数组(左边比基准数字小右边比基准数字大)

2.图片演示

 

format,png

3.优缺点

1.一般的快速排序是不稳定的,其时间复杂度为 O(nlogn),其中n是所排序序列的大小。但是在选取的基准值为所排序序列的最值时(例如{12, 1, 2, 7, 9, 11, 4, 5, 6, 8})其时间复杂度会退化成O(n^2)

2.但如果采用随机选取基准值的方法,可以使得快速排序变得稳定许多,时间复杂度为 O(nlogn)

4.过程

首先要明白我们要做放事情:找到一个基准值,让它的左边的数比它小右边的数比它大,假设原数组

{6, 1, 2, 7, 9, 11, 4, 5, 10, 8};这里随便定一个基准值为6,我们要把他围绕基准值6数组排序成

{4, 1, 2, 5, 6, 11, 9, 7, 10, 8}

以下是过程

1.定义数组

public static void main(String[] args) {
    int[] arr = {6, 1, 2, 7, 9, 11, 4, 5, 10, 8};
    //调用方法排序,传递需要排序的范围,这里先从数组第一个到最后一个
    quickSort(arr, 0, arr.length - 1);
    //检查排序
    System.out.println(Arrays.toString(arr));
}
    while (left < right && arr[right] >= base) { //(条件:arr[right] >= base)
        right--;
    }
    //右指针停止后左指针开始向右移动,同理,找到比基准值大的数停止,否则一直右移(条件arr[left] <= base)
    while (left < right && arr[left] <= base) { //
        left++;
    }
    //现在已经找到左右两边开始数起,分别第一个出现的符合条件的数,也就是7和5
    //将两个数交换位置
    int tem = arr[right];
    arr[right] = arr[left];
    arr[left] = tem;

得到结果{6, 1, 2, 5, 9, 11, 4, 7, 10, 8},

此时可以交换第一次出现的一对符合条件的数,但是目标是整个数组,因此需要让这个过程继续重复执行,在这之前

有人会好奇while内left<right这个条件,这里是因为:

假设数组是{6,7,5,3}

第一次交换:{6,3,5,7}此时left指针指向3,right指向7

第二次:{6,3,5,7}中,右指针从7开始,左移到5时候,比基准值小,停止

左指针指到3以后右移到5,此时,两指针指向同一个数5,此时停止,

因此这里是left<right时候符合条件执行循环

 

对于arr[right] >= base左右指针是否该写=,需要考虑极端条件,比如数组:{6,6,,7,5,3} 如果写成arr[right] > base或者arr[left] <base时,当有重复值 第一次交换: 右指针指向3;左指针第二个6不复合条件,因此指向7,交换后:{6,6,3,5,7} 第二次交换: 右指针指向5,左指针指向5时两指针相遇,停止循环得到的数组{6,6,3,5,7}不符合左边小右边大的预期 因此需要写=,写==时: 第一次:{6,3,7,5,6} 第二次:{6,3,5,7,6} 将6和5交换得到预期数组{5,3,6,7,6}

这里同理,当第一次交换后,为了将每一个数排好,在外层加一个循环

while (left < right) {
    while (left < right && arr[right] >= base) {
        right--;
    }
    //右指针停止后左指针开始向右移动,同理,找到比基准值大的数停止,否则一直右移
    while (left < right && arr[left] <= base) {
        left++;
    }
    //现在已经找到左右两边开始数,分别第一个出现的符合条件的数,也就是7和5
    //将两个数交换位置
    int tem = arr[right];
    arr[right] = arr[left];
    arr[left] = tem;
}
//交换两个值
 arr[start] = arr[left]; //将指针指向赋值给第一个数6
        arr[left] = base; // 将第一个数6 给指针指向值(base=6)

结束条件是当左右指针相遇时停止,最后将两指针停止的索引对应的值与选取的基准值交换

因为left与right此时相同,因此随意选取一个进行交换

此时运行代码

 

5da4b77a05fa081d7c21a65e50bcf7f3.png

 

接下来只需要将6左边的数和右边的数进行一个快速排序,也就是重新调用排序方法,但是改变需要排序的范围

//对左边数进行快排
quickSort(arr,start,left-1);
//对6右边进行快排
quickSort(arr,left+1,end);

并且给出递归结束条件;当不断分治到只有两个数时返回,例如数组:

{6, 1, 2, 7, 9, 11, 4, 5, 10, 8}不断进行递归:

{4, 1, 2, 5, 6, 11, 9, 7, 10, 8}

{4, 1, 2, 5, 6, 11, 9, 7, 10, 8}

{2,1,4,5,6,8,9,7,10,11} //4和11为基准

{2,1,4,5,6,8,9,7,10,11} //2和8为基准,此时2和1交换后满足条件start>=end,返回右边{8,9,7,10}继续进行快排

右边以此类推...

if(start>=end){
    return;
}

整个方法:

public static void quickSort(int[] arr, int start, int end) {
    if(start>=end){
        return;
    }
​
    //将定义两个指针为left和right分别在数组索引的0和length-1位置
    int left = start;
    int right = end;
    //随意选取一个基准值,这里选取第一个数,也就是索引为0;
    int base = arr[start];
    //先从右指针开始寻找比基准值小的数,如果比基准值大则继续向左移动右指针,如果找到比基准值小的数停止
    while (left < right) {
        while (left < right && arr[right] >= base) {
            right--;
        }
        //右指针停止后左指针开始向右移动,同理,找到比基准值大的数停止,否则一直右移
        while (left < right && arr[left] <= base) {
            left++;
        }
        //现在已经找到左右两边开始数,分别第一个出现的符合条件的数,也就是7和5
        //将两个数交换位置
        int tem = arr[right];
        arr[right] = arr[left];
        arr[left] = tem;
    }
​
    arr[start] = arr[left];
    arr[left] = base;
​
    //对左边数进行快排
    quickSort(arr,start,left-1);
    //对6右边进行快排
    quickSort(arr,left+1,end);
}

运行

 

 

7c5b474962cfeb63064baf8310447fe3.png

二.递归

 

1.概念

  • 递归是一种自调用函数,也就是说函数内部调用函数本身,每次调用时传入不同的变量。

  • 递归有助于编程者解决复杂的问题,同时让代码变得简洁。

2 算法

递归算法中包含:递推、回推、终止条件

  • 方法内部调用方法自己的过程称为递推。

  • 方法内部返回到上一次调用的过程称为回推。

  • 满足方法内部不在调用方法本身的条件称为递归的终止条件

3 示例

1.题目:求n!

分析: n的阶乘可以等价于n * (n-1)! 等价于n * (n-1) * (n-2)! 等价于n * (n-1) * (n-2) * (n-3)!

直到乘1为止

public static int f(int n){
if(n==1){ //终止条件
return 1; //回推
}
return n * f(n-1); //递推
}
public static void main(String[] args) {
int result = f(4);
System.out.println(result);
}

过程

 

 

27f5e518f22c005c8f62bae611c4714e.png

 

 

 

4.规则

  1. 指向递归方法时,要创建一个新的受保护的独立空间(栈空间)

  2. 方法的局部变量是独立的,不会相互影响,比如变量n

  3. 如果方法中使用的是引用类型的变量(比如数组),就会共享该引用变量

  4. 递归必须向终止条件逼近,否则就是无限递归,就会出现 StackOverflowError(死龟)

  5. 当递归方法遇到return,就开始回推,回推的过程中遵循谁调用就将结果返回 给谁。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值