【快速排序&&归并排序&&二分】

目录

快速排序

思路

模板

练习题

 改进

归并排序

思路

模板

练习题

二分

思路

模板


快速排序

思路

利用分治的思想。将待排序的区间分为两部分:全部小于等于一个数x,右边一部分全部大于等于一个数x。层层递归至区间没有数或者只剩一个数时。<--粉色高光是递归出口

基本步骤

1.确定分界点x(这里x的选择大有名堂,涉及到后面的卡边界)

主要可以包括,左、右、中间或者随机下标的数

2.调整区间:就是“实现全部小于等于一个数x,右边一部分全部大于等于一个数x”这一过程。

简单汪两句:

用双指针算法,i,j分别从头尾开始遍历,当i找到大于等于x的值时停下来,当j找到小于等于x的值停下来。如果没有出现i,j交叉就swap交换两个数。

模板

#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N];
int n;

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

}

int main(){
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    quick_sort(0,n-1);
    for(int i=0;i<n;i++) printf("%d ",q[i]);
    return 0;

}

练习题

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

    quick_sort(0,n-1);
    printf("%d",q[k-1]);
    return 0;
}

可以注意一下:这里分界点取得是q[(r+l)/2],如果是取q[l]或者q[r]的话会TLE ,这里就引入下面的改进算法啦!

 改进

1.就是取中点下标的值作为分界点,就是上面的做法。

2.取随机下标的值:int index=rand()%(r-l+1)+l;(其中r-l+1为数组个数)

#include<iostream>
#include<algorithm>
#include<ctime>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
const int N=1e5+10;
int n,k;
int q[N];

void quick_sort(int l,int r){
    if(l>=r) return;
    int y=rand()%(r-l+1)+l;
    int i=l-1,j=r+1,x=q[y];
    while(i<j){
        do i++; while(q[i]<x);
        do j--; while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    
    quick_sort(l,j);
    quick_sort(j+1,r);
}

int main(){
    cin>>n>>k;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    quick_sort(0,n-1);
    cout<<q[k-1];
    return 0;
}

3.仅限于第k个数这一道题。

将k传入排序函数,判断k在左右哪一边,然后就仅仅排序所在的边。

#include<iostream>
#include<algorithm>
using namesapce std;
const int N=1e5+10;
int n,k;
int q[N];
int quick_sort(int l,int r,int k){
    if(l>=r) return q[l];
    int i=l-1,j=r+1,x=q[(l+r)/2];
    while(i<j){
        do i++; while(q[i]<x);
        do j--; while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    int s=j-l+1;
    if(s>=k) return quick_sort(l,j,k);
    else return quick_sort(j+1,r,k-s);
}


int main(){
    cin>n>>k;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    cout<<quick_sort(0,n-1,k);
    return 0;
}

归并排序

思路

将数组分成两个部分,先用递归排好两部分的序(从小到大)。在此基础上,我们进行归并排序:用两个指针分别指向第一个数组a,第二个数组b的第一下标i,j。然后比较a[i]与a[j]的大小。把较小的那个数放入临时数组tmp[k]。知道任意一个数组走到尾。最后将临时数组覆盖待排序的数组。

将两个排好序(从小到大)的数组合并成一个从小到大的数组。

基本步骤:

1.确定分界点(一般就是对半分)

2.递归排序两部分

3.归并两部分到一个临时数组

4.临时数组覆盖

模板

#include<iostream>
using namespace std;
const int N=1e5+10;
int n;
int q[N],tmp[N];

void merge_sort(int l,int r){
    if(l>=r) return;
    int mid=(l+r)/2;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    
    int i=l,j=mid+1,k=0;
    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(i=l,k=0;i<=r;i++,k++) q[i]=tmp[k];
    return;
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    merge_sort(0,n-1);
    for(int i=0;i<n;i++) cout<<q[i]<<" ";
    return 0;

}

(标点符号真的会让我哭泣)

练习题

这里主要是理解归并的过程,并作出适当的改动

我们可以想到,在合并过程中,a数组下标在前,如果恰好a[i]>b[j],则a数组后面的所有数都是相对于b[j]的逆序对,个数(mid-i+1),那就直接开一个全局变量咯?

#include<iostream>
using namespace std;
const int N=1e5+10;
int n,q[N],tmp[N];
long long int res;

void merge_sort(int l,int r){
    if(l>=r) return;
    int mid=(l+r)>>1;
    merge_sort(l,mid);
    merge_sort(mid+1,r);

    int i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r){
        if(q[i]<=q[j]) tmp[k++]=q[i++];
        else{
            res+=(mid-i+1);
            tmp[k++]=q[j++];
        }
    }
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r) tmp[k++]=q[j++];
    
    for(int i=l,j=-;i<=r;) q[i++]=tmp[j++];
}

int main(){
    cin>>n;
    for(int i=0;i<n;i++) scanf("%d",&q[i]);
    merge_sort(0,n-1);
    cout<<res;
    return 0;

}

注意注意res可能很大,所以我们来一个long long吧。 

其实不用全局变量也可以

long long int merge_sort(int l,int r){
    if(l>=r) return 0;
    int mid=(l+r)>>1;
    long long int res=merge_sort(l,mid)+merge_sort(mid+1,r);
    .....
}

二分

思路

核心不一定是单调性,单调性可以二分,但没有单调性的一些情况也可以二分。

总的思路就是二分找到某一个性质的分界点。(应该算是一种思想上的二分)

整数二分:

分为两种情况:

 如图:红绿代表两种性质,他们之间有分界点,二分就是取他们的分界点。

第一种情况:     取红色边界

先进行对半取(l+r)/2,验证他是否满足红色部分的性质,如果满足就将区间缩小至[mid,r](这里尤其要注意,mid满足该性质所以可以去到mid),不满足就将区间缩小至[l,mid-1],继续进行二分。跳出的条件就是l、f是否重合or交叉。

说明一点特殊情况:由于整数除法是整除,会进行向下取整。所以这里当区间调整至[mid,r]时,需要在mid的赋值时变为(l+r+1)/2;

这个例子当l与r就相差1时,范围就会一直是[l,r]死循环。

第二种情况   取绿色边界

思路大概跟上面时相同的,只要记住改变后的区间一定是满足这个判断性质的点

这里因为更新的是r,mid赋值就直接可以(l+r)/2

模板

一道例题送给大家

#include<iostream>
using namespace std;
const int N=1e5+10;
int num[N],n,q,k;


int main(){
    cin>>n>>q;
    for(int i=0;i<n;i++) scanf("%d",&num[i]);
    while(q--){
        cin>>k;
        int l=0,r=n-1;
        while(l<r){
            int mid=(l+r)/2;
            if(num[mid]>=k) r=mid;
            else l=mid+1;
        }
        if(num[l]!=k) cout<<"-1 -1";
        else{
            cout<<l<<" ";
            l=0,r=n-1;
            while(l<r){
                int mid=(l+r+1)/2;
                if(num[mid]<=k) l=mid;
                else r=mid-1;
            }
         cout<<l;
        }
    }
    return 0;

}

这个例子可以很好地帮助我们理解二分!!

浮点数二分

相较于整数二分他没有这么多的边界问题,有的话那也只是精度控制。

我们直接给一个例题的板子

#include<iostream>
using namespace std;
int main(){
    double n,mid;
    cin>>n;
    double l=-10000,r=10000;
    while(r-l>1e-8){
        mid=(l+r)/2;
        if(mid*mid*mid>n) r=mid;
        else l=mid;
    }
    printf("%.6lf",mid);
    return 0;
}

 

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 二路归并排序是一种经典的排序算法,其运行结果取决于输入序列的大小和内容。下面是一个简单的示例,展示了如何使用 Python 实现二路归并排序并输出结果: ```python def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) return merge(left, right) def merge(left, right): result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result += left[i:] result += right[j:] return result arr = [4, 2, 7, 1, 3, 6, 5, 8] print(merge_sort(arr)) ``` 在这个例子中,我们将一个包含 8 个整数的数组 `[4, 2, 7, 1, 3, 6, 5, 8]` 作为输入序列进行排序。程序输出的结果是一个排好序的列表,即 `[1, 2, 3, 4, 5, 6, 7, 8]`。 请注意,程序的运行结果可能因输入序列的不同而有所不同。 ### 回答2: 二路归并排序是一种经典的排序算法,其基本思想是将待排序的序列不断地划分成较小的子序列,直到只剩下一个元素,然后再将这些子序列两两合并,直到最终得到有序的序列。 在进行二路归并排序时,首先将待排序的序列平均分成两部分,然后对每一部分分别进行二路归并排序,递归地将其划分为较小的子序列。接着,再对这些子序列进行两两合并,不断地将相邻的子序列合并为更大的有序序列,直到只剩下一个有序序列为止。 二路归并排序的运行结果是将输入序列重新排列成一个有序序列。具体步骤如下: 1. 将待排序序列分成两个子序列,分别进行二路归并排序。 2. 对每个子序列递归执行上述步骤,直到只剩下一个元素。 3. 对相邻的子序列进行两两合并,得到更大的有序序列。 4. 不断地执行步骤3,直到最终合并成一个有序序列。 二路归并排序的时间复杂度为O(nlogn),其中n表示待排序序列的长度。尽管它的时间复杂度相对较高,但由于它的稳定性和可预测性,二路归并排序被广泛应用于各种排序问题中。 总结而言,二路归并排序是一种高效、稳定的排序算法,它能够将待排序序列重新排列成一个有序序列。通过不断地划分和合并子序列,二路归并排序能够实现快速、准确地排序大规模数据。 ### 回答3: 二路归并排序是一种常用的排序算法,它将一个序列划分为两个子序列,分别对这两个子序列进行递归排序,然后将两个有序子序列合并,从而得到一个有序的序列。 具体运行过程如下: 1. 将待排序序列不断二分,直到每个子序列只有一个元素,即认为这个子序列是有序的。 2. 递归合并相邻的子序列,每次合并后得到的子序列长度是原来的两倍。 3. 不断地进行子序列的合并,直到最终得到一个完整有序的序列。 举例说明: 假设待排序序列为[5, 2, 9, 1, 6, 4, 8, 3, 7]。 1. 将序列二分为[5, 2, 9, 1]和[6, 4, 8, 3, 7]两个子序列。 2. 对两个子序列分别进行递归排序。 对左边子序列进行递归排序,将其划分为[5, 2]和[9, 1],再分别对两个子序列进行递归排序,得到[2, 5]和[1, 9]。 对右边子序列进行递归排序,将其划分为[6, 4, 8]和[3, 7],再分别对两个子序列进行递归排序,得到[4, 6, 8]和[3, 7]。 3. 将已排序的子序列进行合并。 先合并两个长度为1的子序列[2]和[5],得到[2, 5]。 再合并两个长度为1的子序列[1]和[9],得到[1, 9]。 合并两个长度为2的子序列[2, 5]和[1, 9],得到[1, 2, 5, 9]。 合并两个长度为3的子序列[4, 6, 8]和[3, 7],得到[3, 4, 6, 7, 8]。 4. 最后合并两个有序子序列[1, 2, 5, 9]和[3, 4, 6, 7, 8],得到完整有序序列[1, 2, 3, 4, 5, 6, 7, 8, 9]。 二路归并排序运行结果是将待排序序列按照从小到大的顺序进行了排序,得到一个有序的序列。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值