基础算法 (快速排序,归并排序,二分)

快速排序:

核心思想:分治

步骤:

1.确定分界点 $x$ 。

2.调整范围:该点x,将数划分为两边(左边都小于等于x,右边都大于等于x)。

注意:并不是把所有小于等于x的数都放在左边和把所有大于等于x的数都放在右边,例如当数等于x时左右两边都可以放。

3.递归处理左右两边。

但是怎么样去调整范围呢?(怎么样处理步骤二)

方法一:

可以先定义两个数组a[ ],b[ ],然后将数的范围里面小于等于x的数放在a[ ],将大于等于x的数放在b[ ]然后依次将a,b放入原数组中即可

方法二:

由于上面的方法过于暴力。为了显得优美些,我们可以设定两个指针 i和 j分别位于数组开头和末尾,并分别向中间靠拢。当 i指针指向的数大于等于x且 j指针指向的数小于等于x时则俩数交换直到 i和 j相遇为止。

 那我们不妨设x=3,数据为3 1 2 3 5,红箭头为i,绿箭头为j ,则有:

当 j指向的数大于等于x时,指针向左移动一位。此时 j指向的数等于x,i指向的数也等于x,则两数交换(注意此处两数相等也要交换)。

当 i指向的数小于等于x时,指针向右移动一位。此时 j指向的数小于x,则不动。

此时 i指向的数小于x,向右移动一位。

此时 i指向的数小于x,向右移动一位。

 两指针相遇后停止,这样我们两边的数都分别小于等于x和大于等于x了。

 快排模板:(注意边界问题)

void q_sort(int a[],int l,int r){
    if(l>=r)return;
    
    int x=a[(l+r)/2],i=l-1,j=r+1;
    while(i<j){
        do i++;while(a[i]<x);
        do j--;while(a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    q_sort(a,l,j);
    q_sort(a,j+1,r);
}

【代码实现】:

#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N];
void q_sort(int a[],int l,int r){
    if(l>=r)return;
    
    int x=a[(l+r)/2],i=l-1,j=r+1;
    while(i<j){
        do i++;while(a[i]<x);
        do j--;while(a[j]>x);
        if(i<j)swap(a[i],a[j]);
    }
    q_sort(a,l,j);
    q_sort(a,j+1,r);
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    q_sort(a,0,n-1);
    for(int i=0;i<n;i++){
        printf("%d ",a[i]);
    }
    return 0;
}

归并排序:

核心思想:分治

步骤:

1.确定分界点 x=(l+r)/2      (快排是将分界点设为数组里随机的一个数,而归并是将分界点设为数组中间下标的值)

2.将数组一分为二,一个无序的数组成为两个数组.

3.合二为一,将两个有序数组合并成为一个有序数组.

(如动图所示)

 归并排序里较为重要的一步就是第三个步骤:

在一二步完成之后,我们已得到两个有序数组,便于比较我们可以设两个指针分别指向两个数组的最左端(最小值的一端)。

 并比较两指针所指的数 ,将更小的数放入一个新数组中,然后将该指针往后挪一步。

我们不妨用图片表示:

设有两个有序数列:

上面数组的指针更小,则往后移一位,并存入新数组中。

                                                                 以此类推

‘当其中任意一个数组走完了,则将另一个数组全部放入新数组中

注意每次指针所指向的数都是该序列中的最小数。

【代码实现】:

#include<iostream>
using namespace std;
int n;
int tmp[1000010];
int q[1000010];
void m_sort(int l,int r){
    if(l>=r)return ;
    int mid=(l+r)>>1;
    m_sort(l,mid),m_sort(mid+1,r);
    int k=l;
    int i=l,j=mid+1;
    while(i<=mid && j<=r)
        if(q[i]<=q[j])tmp[k++]=q[i++];
        else tmp[k++]=q[j++];
    while(i<=mid)tmp[k++]=q[i++];
    while(j<=r)tmp[k++]=q[j++];
    for(int i=l;i<=r;i++)q[i]=tmp[i];
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        scanf("%d",&q[i]);
    }
    m_sort(0,n-1);
    
    for(int i=0;i<n;i++){
        printf("%d ",q[i]);
    }
}

二分:

例题:数的范围

核心:边界问题

步骤:

1.确定分界点mid。mid= ( left + right (+1)  ) / 2,视情况定要不要加1。

2.写两个二分,一个用来找到>=x的第一个数

当 a[ mid ]>=x 时,说明 mid 及其左边可能含有值为x的元素。

当a[mid]<x时,令l=mid+1,mid及其左边的位置被排除了,可能出现解的位置是mid+1及其后面的位置。

【模板】

int l=0,r=n-1;
while(l<r){
         int mid=(r+l)>>1;
         if(a[mid]>=x)r=mid;
         else l=mid+1;
}

3.第二个用来找到<=x的最后一个数

【模板】

int l=0,r=n-1;
while(l<r){
         int mid=(l+r+1)>>1;
         if(a[mid]<=x)l=mid;
         else r=mid-1; 
}

注意l=mid要写成(l+r+1)>>1。因为当 l =r-1时,mid就会等于l而导致死循环

每次使用这这两个模板的时候,先想是找这个区间的左端点还是还是右端点,然后选择用模板,最后再去写判断条件

【代码实现】:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
    }
    while(m--){
        int x;
        scanf("%d",&x);
        int l=0,r=n-1;
        while(l<r){
            int mid=(r+l)>>1;
            if(a[mid]>=x)r=mid;
            else l=mid+1;
        }
        if(a[l]!=x)cout<<"-1 -1"<<endl;
        else 
        {
            cout<<l<<" ";
            int l=0,r=n-1;
            while(l<r){
                int mid=(l+r+1)>>1;
                if(a[mid]<=x)l=mid;
                else r=mid-1;
            }
            cout<<l<<endl;
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值