Acwing基础算法课第一课之快速排序、归并排序、二分查找

本文详细介绍了快速排序的基本思想、调整区间方法,以及Java实现;归并排序的递归过程和合并操作;以及二分查找的基本思路和应用场景,包括查找数的范围和浮点数三次开方。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

AcWing基础算法课程笔记
第一部分快排和归并排序,二分查找。根据模板套入题目调试。


提示:以下是本篇文章正文内容,下面案例可供参考

一、快速排序

快速排序基于分治的一种排序算法

1.基本思想:

(1)确定分界点,为了划分两个区间。x可以取最左边的,右边的,中间的,或者数组中随机的一个数。
(2)根据分界点调整左右两个区间,左边都小于等于x,右边都大于等于x(关键部分)
(3)然后递归处理左右两端,即分别再对左右两个区间排序

2.调整区间思路:

(1)暴力方法,两个数组a[]和b[]分别存放大于x的和小于等于x的值,然后再放入原来的数组
(2)双指针的方法,i和j对应数组下标,i从左边开始移动,移动到第一个大于x的数停止,j从右边开始移动,移动到小于x的数停止。然后交换,i和j指向的数。继续移动交换i和j知道两个数重合。

3.注意事项

取第三步递归分区间取[l,i-1]和[i,r]时,x不可以去q[l]即数组左边值。比如代入[1,2]会陷入死循环。同理分区取[l,j]和[i+1,r]时,x不可以去q[r]即数组右边值。比如代入[1,2]会陷入死循环。

如果取mid时,mid= (l+r)/2,可以用j直接划分,如果用i划分是需要mid+1。计算机计算(0+1)/2等于0。我们需要加1避免mid的值为最左值。因为最左值就会无限循环的划分。这里代入数组[0,1]可以轻松理解。
在这里插入图片描述
这里有个典型的错误,比如3,8,6,8两个8交换后,就i,j同时到6,没有完成循环就终止了。

4.代码(JAVA)

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner myScanner = new Scanner(System.in);
        int n = myScanner.nextInt();
        int []a = new int[n];
        for(int i = 0; i < n;i++){
            a[i] = myScanner.nextInt();           //创建数组
        }
        quick_sort(a,0,n-1);
        for(int i = 0;i<n ;i++){
            System.out.print(a[i]+" ");
        }
    }
    
    public static void quick_sort(int[] a ,int l,int r){   //快排函数
        if(l >= r) return;     //就一个数或者没有数,嘿嘿嘿
        int x = a[(l+r)>>1] ; //取中间数作为分界点
        int i = l - 1;
        int j = r + 1;
        while(i<j){
            while(a[++i]<x);
            while(a[--j]>x);
            if(i<j){
                int temp = a[i];
                a[i] = a[j];
                a[j] = temp;
               
            }
        }
        quick_sort(a,l,j);
        quick_sort(a,j+1,r);
    }
}

4.快排一些应用

1.二分类(比如奇数偶数分类O(n)遍历一遍就可以解决)三分类问题(先划分成两个问题,然后对两个子问题递归排序,最后再合并。)
2.找第k小问题:(这里只需要递归需要的部分即可)
比如以某一元素为媒介进行一次快排,小于它的在左边,大于它的在右边,
(1)如果左边数字个数等于k-1,那么第k小就是这个元素了
(2)如果左边个数小于k-1,那么第k小在右面集合
(3)如果左边个数大于k-1,那么第k小就在左面集合

二、归并排序

归并排序也是基于分治的排序算法。
快排是先划分区间,再分别递归两边。
而归并是先递归排序两边,再合并排序。

1.基本思路

(1)确定分界点mid=(l+r)/2
(2)递归排序左边和右边
(3)合并排序(关键),利用双指针合并两个有序链表

2.代码

代码如下(示例):

public static void merge_sort(int[] a,int l ,int r){
        if(l>=r) return;
        int mid = (l+r)>>1;     //进行二分递归  
        merge_sort(a,l,mid);
        merge_sort(a,mid+1,r);
        int i = l;
        int j = mid+1;          //  利用双指针合并
        int count =0;
        int[] t = new int[r-l+1]; 
        while(i<=mid && j<=r){
            if(a[i]<=a[j])
                t[count++] = a[i++];
            else
                t[count++] = a[j++];
        }
        while(i <= mid) t[count++] = a[i++];
        while(j <= r) t[count++] = a[j++];
        for(int m = l,q=0;m<=r;m++,q++){
            a[m] = t[q];
        }
    }

3.逆序对的数量


二、二分

最常见的二分就是在排好的有序序列中查找你已知数的位置,每次用mid作为分界点,把一整个序列分成两个部分。通过不断缩写搜寻范围,最终高效查找数据。
二分查找不只可以利用在单调区间的查找上,也可以用于查找边界,比如左侧一部分满足一个条件,右侧一部分不满足这个条件,二分的核心就是每个循环二分区间只搜索一般。

1.基本思路

(1)先找到一个中间值mid=(l+r)/2,或者(l+r+1)/2.
(2)然后根据中间值将原区间划分成两个区间,[l,mid]和[mid+1,r]。([l,mid-1],[mid,r]是另一种划分,根据情况而定)。
(3)划分区间需要写一个check判断条件,选择两个条件中的一个作为新的区间。
注意:[l,mid]和[mid+1,r]这种区间划分时,mid = (l+r+1)/2。另一种区间划分不需要+1。比如当在0,1中查找0时会造成死循环。(即存在l=mid,就必须+1)。

2.查找数的范围

在这里插入图片描述

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int e = scanner.nextInt();
        int[] a = new int[n];
        int i = 0 ,m =0,p=-1,q=-1,lmid,rmid;
        
        for(i = 0;i < n;i++)
            a[i] = scanner.nextInt();
        for(i = 0;i < e;i++ ){
            m = scanner.nextInt();      //有左右边界之分,先找左边界,再找右边界
            int l = 0, r =n-1;
            while(l < r){               //i==j时就找到我们所需要的解了
                lmid = (l + r ) >>1;
                if(a[lmid] >= m)             //a[lmid]大于等于m,目标在左半区
                    r = lmid ;
                else                    //a[lmid]<m,目标在右半区
                    l = lmid+1;
            }
            if(a[l] != m )
                p = -1;
            else
                p = l;
            System.out.print(p+" ");
            l = 0;
            r = n-1;
            while(l < r){               //找寻右边界
                rmid = (l + r + 1) >>1;
                if(a[rmid] <= m)        //a[rmid]<=m 右边界一定在[rmid,r];
                    l = rmid;
                else
                    r = rmid-1;
            }
            if(a[l] != m )
                p = -1;
            else
                p = l;
            System.out.println(p+" ");
        }
    }
    
}

2求一个浮点数的三次开方(-10000到10000之间的数)

这个比较简单,主要注意 l =-10000,而不是0,因为可能是负数。

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner  s = new Scanner(System.in);
        double b = s.nextDouble();
        //double l = 0 ,r = b;
        double l = -10000, r=10000;
        while(r-l > 1e-8){
            double mid = (r+l)/2;
            if(mid*mid*mid >= b)
                r = mid ;
            else 
                l = mid ; 
        }
        System.out.printf("%.6f",l);
    }
}
```c
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值