快速排序,归并排序,二分

快排模版

const int dd = 1e5 + 10;
int a[dd];
int n;
void quick_sort(int a[], int l, int r);
int main()
{

    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    quick_sort(a, 0, n - 1);
    for (int i = 0; i < n; i++)printf("%d ", a[i]);
    return 0;
}
void quick_sort(int a[], int l, int r)
{
    if (l >= r)return;
    int qian = l - 1, hou = r + 1;//先取边界,配合 do while;
    int x = a[(l + r) / 2];//任意值都可以 ,建议取中间值
    while (qian < hou)//结束时,两指针重合
    {
        do qian++; while (a[qian] < x);//do while 作用是先进行指针移动,后判断,防止出现相同值时死循环;
        do hou--; while (a[hou] > x);
        if (qian < hou) { swap(a[qian], a[hou]); }
    }
    quick_sort(a, l, hou);//hou和qian两个指针都可(因为两者重合)
    quick_sort(a, hou + 1, r);
}

注意点:

1.如代码中的注释;

2.条件判断用<和>,而不用<=和>=。防止取的值刚好为数组中最大或最小值时导致数组越界;

3.取x的值要和递归的区间分布相适应(防止死循环,该情况会出现在递归只剩2个数时出现)

当x取a[l]时(即左端点值),递归区间划分要以(l,hou)和(hou+1,r)

当x取a[r]时(即右端点值),递归区间划分要以(l,hou-1)和(hou,r)

当x取a[(l+r)/2]时(因为向下取整,所以会“向左靠”,),递归区间划分要以(l,hou)和(hou+1,r)。

当x取a[(l+r+1)/2]时(“向右靠”),递归区间划分要以(l,hou-1)和(hou,r)

时间复杂度:好:O(nlogn)坏:O(n^2)空间复杂度:1(只用了指针,没开新空间)

算法稳定性:不稳定(不保证相等的数据按顺序扫描和排放)

归并排序 

原理:快排是先排序后划分,归并排序是先划分再排序然后再组合在一起;

模版:
int n;
int a[1000000], tmp[1000000];//创建临时数组
void merge_sort(int a[], int l, int r)
{
    if (l >= r)return;
    int mid = (r + l) >> 1;
    merge_sort(a, l, mid), merge_sort(a, mid + 1, r);//先划分
    int k = 0, qian = l, hou = mid+1;
    while (qian <= mid && hou <= r)
    {
        if (a[qian] <= a[hou])tmp[k++] = a[qian++];//这里等于号可有可无
        else tmp[k++] = a[hou++];//小的插前面,大的插后面
    }
    while(qian<=mid)tmp[k++] = a[qian++];
    while(hou<=r)tmp[k++] = a[hou++];//两行确保每一个数都被纳入临时数组
    for (int j = 0, dd = l; dd <= r; j++, dd++)a[dd] = tmp[j];//把临时数组的数放入原数组
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    merge_sort(a, 0, n - 1);
    for (int i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }
}

找逆序对个数:

int n;
int a[1000000], tmp[1000000];
long long jian = 0;
void merge_sort(int a[], int l, int r)
{
    if (l >= r)return;
    int mid = (r + l) >> 1;
    merge_sort(a, l, mid), merge_sort(a, mid + 1, r);
    int k = 0, qian = l, hou = mid + 1;
    while (qian <= mid && hou <= r)
    {
        if (a[qian] <= a[hou]) { tmp[k++] = a[qian++]; }
        else { tmp[k++] = a[hou++]; jian += mid - qian+1; }//唯一不同的地方(jian加上每一个右半边数的在其右边的左边数的个数(有点拗口...)
    }
    while (qian <= mid) { tmp[k++] = a[qian++];}
    while (hou <= r) { tmp[k++] = a[hou++];}
    for (int j = 0, dd = l; dd <= r; j++, dd++)a[dd] = tmp[j];
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    merge_sort(a, 0, n - 1);
    cout << jian;
}

时间复杂度:O(nlogn)

稳定性:稳定

二分

应用条件:

不一定要有单调性,只要可以找到一个条件把数组分为左右两种情况即可

整数二分:二分的是整数

模版(条件为与某个数的大小关系):

以求有序数组中某个数的区间的题目为例:

int n, m, target;
int a[1000010];
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    while (m--)
    {
        cin >> target;
        int l = 0, r = n - 1; int mid;
        while (l < r)
        {
            mid = l + r >> 1;
            if (a[mid] < target)l = mid + 1;//这里是否加一由mid是否可能为答案确定,若没可能,就加1
            else r = mid;//l与r的行为由具体题目确定
        }
        if (a[l] != target)cout<< "-1 -1"<<endl;//这里l和r都可,因为l,r重叠。
        else
        {
            cout << l << ' ';
            l = 0, r = n - 1;
            while (l < r)
            {
                mid = l + r +1 >> 1;
                if (a[mid] > target)r = mid - 1;
                else l = mid;
            }
            cout << l << endl;
        }
    }
    return 0;
}

注意点:

1.如注释

2.mid取法要与mid等于什么相适应(还是防止死循环,当两指针分别指向相邻的数时有可能死循环,是整数除法的向下取整导致的)

若l=mid,mid更新时要等于(l+r+1)/2,强迫mid“往右靠”;

若r=mid,mid更新时要等于(l+r)/2,使mid“往左靠”;

3.指针l、r一定会重合,即二分一定有答案,但题目不一定有答案,所以要判断,如if (a[l] != target)

时间复杂度:O(logn)

小数二分

概念:二分的是小数,指针是小数

模拟(以求一个数的三次方根为例):

double x;
int main()
{
    cin >> x;
    double tmp = abs(x);//x有可能是小数
    double l = 0, r = 1e8, mid=0;//r不能等于x(防止x为小数)
    while ( r - l >= 1e-8)//确定精度
    {
        mid = l/2+r/2;//防止上溢
        if (pow(mid, 3) <= tmp)l = mid;
        else r = mid;
    }
    l = l * (x / tmp);//三次方根符号不变,x / tmp确定符号,l r都行,因为重合
    printf("%.6lf", l);
}

简单许多,不用考虑mid取法要与mid等于什么相适应的问题和是否加一的问题(即不会死循环和不用考虑mid是否可能为答案--因为根本没有正确答案)

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值