【算法笔记4.6小节 - two pointers】双指针

本文探讨了在递增正整数序列中找到两数之和等于特定值的算法,以及实现序列合并、归并排序、快速排序和随机选择算法的详细过程。通过具体示例,展示了不同排序算法的运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 给定一个递增的正整数序列a和一个正整数m,求序列中的两个不同位置的数a和b,使他们的和恰好为m,输出所有满足条件的方案。

    #include <stdio.h>
    /*
    给定一个递增的正整数序列a和一个正整数m,求序列中的两个不同位置
    的数a和b,使他们的和恰好为m,输出所有满足条件的方案。
    */
    int main()
    {
       int a[6] = {1,2,3,4,5,6};
       int i=0, j=5,m=8;
       while(i<j)
       {
           if(a[i]+a[j]==m)
           {
               printf("%d %d\n",a[i],a[j]);
               i++;
               j--;
           }
           else if(a[i]+a[j]<m) i++;
           else  j--;
       }
       return 0;
    }
    /*运行结果
    2 6
    3 5
    
    */

     

  2. 序列合并问题。假设有两个递增序列a和b,要求把他们合并成一个递增序列c.

    #include<stdio.h>
    int merge(int a[], int b[], int c[], int n,int m)
    {
        int i=0,j=0,index = 0;
        while(i<n&&j<m)
        {
            if(a[i]<=b[j])
                c[index++] = a[i++];
            else
                c[index++] = b[j++];
        }
        while(i<n) c[index++] = a[i++];
        while(j<m) c[index++] = b[j++];
        return index;//返回序列c的长度
    }
    int main()
    {
        int a[5]={1,3,5,7,9}, b[4]={2,4,6,8};
        int c[12];
        int len = merge(a,b,c,5,4);
        for(int i=0; i<len; i++)
        {
            printf("%d",c[i]);
            if(i<len-1)
                printf(" ");
            else
                printf("\n");
        }
        return 0;
    }
    //运行结果 1,2,3,4,5,6,7,8,9

     

  3. 2-路归并排序( nlogn)

例如序列{66, 12, 33, 57,  64, 27, 18}

第一趟新序列    { 12, 66 33, 57,  27, 6418 }

第二趟新序列    { 12, 33, 57, 66 18, 27, 64 }

第三趟新序列    {12, 18, 27, 33,  57, 64, 66 }

#include<stdio.h>
/*
归并排序递归实现
*/
void merge(int a[], int left1, int right1, int left2, int right2)
{
    int i=left1, j = left2;    //  i指向a[L1],j指向a[L2]
    int temp[100], index = 0;   //  temp临时存放合并后的数组
    while(i<=right1&&j<=right2)
    {
        if(a[i]<=a[j]) temp[index++] = a[i++];
        else           temp[index++] = a[j++];
    }
    while(i<=right1) temp[index++] = a[i++];
    while(j<=right2) temp[index++] = a[j++];
    for(i=0; i<index; i++)
        a[left1+i] = temp[i];    //将合并后的序列赋值回数组a
}

void mergeSort(int a[], int left, int right)
{
    if(left<right)  // 只要left小于right
    {
        int mid = (left + right)/2;
        mergeSort(a, left, mid);    //递归左子区间
        mergeSort(a, mid+1, right); //递归右子区间
        merge(a, left, mid,mid+1, right);   //将左右区间合并
    }
}
int main()
{
    int a[7] = {66,12,33,57,64,27,18};
    mergeSort(a, 0,6);
    for(int i=0; i<7; i++)
    {
        printf("%d",a[i]);
        if(i<6) printf(" ");
        else printf("\n");
    }
}
//运行结果 12 18 27 33 57 64 66
#include<stdio.h>
#include<algorithm>
using namespace std;
/*
归并排序非递归实现
*/
void merge(int a[], int left1, int right1, int left2, int right2)   //两个序列合并
{
    int i=left1, j = left2;    //  i指向a[L1],j指向a[L2]
    int temp[100], index = 0;   //  temp临时存放合并后的数组
    while(i<=right1&&j<=right2)
    {
        if(a[i]<=a[j]) temp[index++] = a[i++];
        else           temp[index++] = a[j++];
    }
    while(i<=right1) temp[index++] = a[i++];
    while(j<=right2) temp[index++] = a[j++];
    for(i=0; i<index; i++)
        a[left1+i] = temp[i];    //将合并后的序列赋值回数组a
}

void mergeSort(int a[], int n)
{
    //step为组内元素个数
    //step/2 为左子区间元素个数,注意等号可以不取
    for(int step=2; step/2<=n; step*=2){
        //每step个元素一组,组内前step/2和后step/2元素进行合并
        for(int i=0; i<n; i+=step)
        {
            int mid = i + step/2 - 1;//左子区间元素个数为step/2
            if(mid+1<=n)    //右子区间存在元素则合并
            //左子区间为[i, mid], 右子区间为[mid+1,min(i+step-1, n)]
                merge(a,i, mid, mid+1, min(i+step-1, n));
        }
         for(int i=0; i<7; i++)
        {
            printf("%d",a[i]);
            if(i<6) printf(" ");
            else printf("\n");
        }
    }
}


int main()
{
    int a[7] = {66,12,33,57,64,27,18};
    mergeSort(a, 6);

}
//运行结果 12 18 27 33 57 64 66

4. 快速排序( nlogn)

#include<stdio.h>
#include<algorithm>
#include<stdlib.h>
#include<time.h>
#include<math.h>
using namespace std;
/*
快速排序
*/

//对区间[left, right]进行一次划分
int Partition(int a[], int left, int right)
{
    //生成[left,right]内的随机数
    int p = (round(1.0*rand()/RAND_MAX*(right-left)+left));
    swap(a[p], a[left]);//交换a[p]和a[left]

    int temp = a[left];         //以a[left]为枢轴
    while(left<right)           //只要left和right不相遇
    {
        while(left<right&&a[right]>temp) right--;
        a[left] = a[right];
        while(left<right&&a[left]<=temp) left++;
        a[right]=a[left];//将a[left]挪到a[right]
    }
    a[left] = temp; //把temp放到left与right相遇的地方
    return left;//返回相遇的下标
}

//快速排序,left与right初值为序列首尾下标
void quickSort(int a[], int left, int right)
{
    if(left < right)
    {
        int pos = Partition(a, left, right);
        quickSort(a, left, pos-1);
        quickSort(a, pos+1, right);
    }
}
int main()
{
    int a[7] = {66,12,33,57,64,27,18};
    quickSort(a, 0, 6);
    for(int i=0; i<7; i++)
        printf("%d ", a[i]);


}
//运行结果 12 18 27 33 57 64 66

5. 随机选择算法(O(n))

(408真题)给定一个由整数组成的集合,集合中的整数各不相同,现在要将它分为两个子集合,使得这两个子集合的并为原集合,交为空集,同时在两个子集合的元素个数n1和n2之差的绝对值|n1-n2|可能小的前提下,要求他们各自的元素之和s1与s1之差的绝对值|s1-s2|尽可能大。求这个|s1-s2|等于多少?


#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stdlib.h>
#include<time.h>
/*
如何从一个无序的数组中求出第K大的数,(假设数组中的数各不相同)
随机选择算法 O(n)
*/
using namespace std;
int a[6];
//随机划分
int randPartition(int a[], int left, int right)
{
    //生成[left,right]内的随机数
    int p = (round(1.0*rand()/RAND_MAX*(right-left)+left));
    swap(a[p], a[left]);//交换a[p]和a[left]

    int temp = a[left];         //以a[left]为枢轴
    while(left<right)           //只要left和right不相遇
    {
        while(left<right&&a[right]>temp) right--;
        a[left] = a[right];
        while(left<right&&a[left]<=temp) left++;
        a[right]=a[left];//将a[left]挪到a[right]
    }
    a[left] = temp; //把temp放到left与right相遇的地方
    return left;//返回相遇的下标
}
int randSelect(int a[], int left, int right, int k)
{
    if(left==right) return a[left];//边界
    int p = randPartition(a, left, right);//随机划分
    int m = p-left+1;
    if(k==m) return a[p];
    if(k<m)     //第k大的数在主元左侧
        return randSelect(a,left, p-1,k);
    else
        return randSelect(a, p+1, right, k-m);
}
int main()
{
    srand((unsigned)time(NULL));
    int n;
    int sum=0, sum1=0;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
    }

    randSelect(a,0,n-1, n/2);//寻找第n/2的数,并进行切分
    for(int i=0; i<n/2; i++)
        sum1+=a[i];
    printf("%d\n",(sum-sum1)-sum1);
    return 0;
}
/*
13
1 6 33 18 4 0 10 5 12 7 2 9 3
80

*/

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值