基础算法01---快速排序

快速排序专题

快速排序

给定你一个长度为 n的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。

输入格式
输入共两行,第一行包含整数 n。
第二行包含 n个整数(所有整数均在 1∼109范围内),表示整个数列。

输出格式
输出共一行,包含 n个整数,表示排好序的数列。

数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5

算法思路

它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快排的思想是分治
我们有一个待排序的数组,长度为n。选定一个基准,将数组分成左右两部分,左边的数小于基准,右边的数大于基准。
然后我们分别看分割后左右两部分数组,如果元素个数大于1,就再次分割。
直到最后,我们得到了n个数组,每个数组含有1个元素。
这n个数组中,每两个挨着的,都是排好序的,所以整体有序。
最好情况下,每次将原来的数组分成两份,第二次将两份成四份…最后一个元素一份。每次分割是O(n),

代码实现:

import java.util.*;

public class Main {
    static int N = 100010;
    static int[] q = new int[N];
    
    private static void quickSort(int[] q, int l, int r) {
        if(l >= r) return;  //左右指针交汇,停止递归
        
        int i = l - 1, j = r + 1;
        int mid = q[l + r >> 1];
        
        while(i < j) {
            do i ++; while(q[i] < mid);
            do j --; while(q[j] > mid);
            if(i < j) {
                int temp = q[i];
                q[i] = q[j];
                q[j] = temp;
            }
        }
        quickSort(q, l, j);   //递归左区间
        quickSort(q, j + 1, r);  //递归右区间
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();
        
        for(int i = 0; i < n; i ++ ) {
            q[i] = sc.nextInt();
        }
        
        quickSort(q, 0, n - 1);
        
        for(int i = 0; i < n; i ++ ) {
            System.out.printf(q[i] + " ");
        }
        
        
    }
}

时间复杂度:假设分割次数为k,则2的k次方等于n,k = logn。每次分割的总时间复杂度是O(n),所以说,平均复杂度是O(n*logn)。
最坏就是原数组有序,每次只能分割开1个元素,需要分割n次,时间复杂度是O(n^2)。

错误代码
请添加图片描述

为什么注释中的代码是错误的呢? 我们mid本质是为了将q[mid]这个定值给记录下来作为标记,如果在while循环中再使用q[mid],而在while循环外不去标记的话,我们所标记的仅仅是mid的下标,而不是值,在swap交换代码的时候,mid这个位置的值可能会被交换掉,最后导致结果不一样

第k个数

给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k
个数。

输入格式
第一行包含两个整数 n和 k。
第二行包含 n个整数(所有整数均在 1∼109范围内),表示整数数列。

输出格式
输出一个整数,表示数列的第 k小数。

数据范围
1≤n≤100000,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3

算法思路

快速选择算法:

  1. 选取一个基准(参考快排),将数组分成左右两个部分,右边部分大于等于左边部分。
  2. 根据数组分界位置与k的大小关系,判断要找的数字是在左半部分还是右半部分。
  3. 对存在要找数字的那半部分数组递归处理,直到待处理的数组中只剩下一个数,这个数字就是要找的那个。

代码实现

代码一:

import java.util.*;

public class Main {
    static int N = 100010;
    static int[] q = new int[N];
    
    private static int quick_Sort(int[] q, int l, int r, int k){
        if(l >= r) return l;  // 返回第k个数的下标
        int i = l - 1, j = r + 1;
        int mid = q[l + r >> 1];
        while(i < j) {
             while(q[++ i] < mid);  //等价于 do i++; while(q[i] < mid);
             while(q[-- j] > mid);
             if(i < j) {
                 int temp = q[i];
                 q[i] = q[j];
                 q[j] = temp;
             }
        }
        
        int len = j - l + 1; //左区间长度
        
        if(k <= len) return quick_Sort(q, l, j, k);  // 选择左区间
        else return quick_Sort(q, j + 1, r, k - len); // 选择右区间
    }        
         
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();
        int k = sc.nextInt();
        
        for(int i = 0; i < n; i++ ){
            q[i] = sc.nextInt();
        }
        
      int result = quick_Sort(q, 0, n - 1, k);
      
      System.out.println(q[result]);
        
        
        
        
        
    }
}

代码二:这一种是最容易想到的,先对数组进行一次从小到大排序,然后选出第k大的数

import java.util.*;

public class Main {
    static int N = 100010;
    static int[] q = new int[N];
    
    private static void quick_Sort(int[] q, int l, int r ){
        if(l >= r) return;
        int i = l - 1, j = r + 1;
        int mid = q[(l + r) / 2];
        while(i < j) {
            do i ++; while(q[i] < mid);
            do j --; while(q[j] > mid);
            if(i < j) {
            int temp = q[i];
               q[i] = q[j];
               q[j] = temp;
            }
        }
        quick_Sort(q, l, j);
        quick_Sort(q, j + 1, r);
    }        
         
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int n = sc.nextInt();
        int k = sc.nextInt();
        
        for(int i = 0; i < n; i++ ){
            q[i] = sc.nextInt();
        }
        quick_Sort(q, 0, n - 1);
  
            System.out.println(q[k - 1]);
        
        
    }
}

时间复杂度(快速排序一样):假设分割次数为k,则2的k次方等于n,k = logn。每次分割的总时间复杂度是O(n),所以说,平均复杂度是O(n*logn)。
最坏就是原数组有序,每次只能分割开1个元素,需要分割n次,时间复杂度是O(n^2)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值