【剑指offer之和为s的两个数字VS和为s的连续正数序列】

【题目一】:输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可。例如输入数组{1,2,4,7,11,15}和数字15.由于4+11 = 15,因此输出4和11。

【技能get &&思路】:

                          

【代码】:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

bool find_number_with_sum(int arr[],int n,int sum,int* p1,int* p2)
{
    bool find = false;
    if(n<1 ||p1==NULL || p2==NULL) return find;
    int ahead_pos =n-1;
    int behind_pos=  0;
    while(ahead_pos>behind_pos)
    {
        LL cur_sum=arr[ahead_pos]+arr[behind_pos];
        if(cur_sum==sum)
        {
            *p1=arr[behind_pos];
            *p2=arr[ahead_pos];
            find=true;
            break;
        }
        else if(cur_sum>sum)ahead_pos--;
        else behind_pos++;
    }
    return find;
}

void Test(char* testName, int arr[], int length, int sum, bool expectedReturn)
{
    if(testName != NULL)
        printf("%s begins: ", testName);

    int num1, num2;
    int result = find_number_with_sum(arr, length, sum, &num1, &num2);
    if(result == expectedReturn)
    {
        if(result)
        {
            if(num1 + num2 == sum)
                printf("Passed. \n");
            else
                printf("Failed. \n");
        }
        else
            printf("Passed. \n");
    }
    else
        printf("Failed. \n");
}

/// 存在和为s的两个数字,这两个数字位于数组的中间
void Test1()
{
    int arr[] = {1, 2, 4, 7, 11, 15};
    Test("Test1", arr, sizeof(arr) / sizeof(int), 15, true);
}

/// 存在和为s的两个数字,这两个数字位于数组的两段
void Test2()
{
    int arr[] = {1, 2, 4, 7, 11, 16};
    Test("Test2", arr, sizeof(arr) / sizeof(int), 17, true);
}

/// 不存在和为s的两个数字
void Test3()
{
    int arr[] = {1, 2, 4, 7, 11, 16};
    Test("Test3", arr, sizeof(arr) / sizeof(int), 10, false);
}

/// 鲁棒性测试
void Test4()
{
    Test("Test4", NULL, 0, 0, false);
}

int main(int argc, char* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    return 0;
}


【后记】在上述代码中,ahead为较小的数字的下标(从后往前),behind为较大的数字的下标(从前往后)。由于数组是排好序的,因此较小的数字一定位于较大数字的前面,因此while循环如此。代码中只有一个while循环从两端向因中间扫描数组,因此复杂度O(N)。


【题目二】输入一个正数s,打印出所有和为s的连续正数序列(至少含两个数)。例如输入15,由于1+2+3+4+5 = 4+5+6 = 7+8=15,所以结果打印出3个连续序列1~5,4~6和7~8。

【技能get &&思路】:

 



【代码】:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

void print_continous_seq(int small,int big)
{
    for(int i=small; i<=big; ++i)
        printf("%d ",i);
    printf("\n");
}

void find_continous_seq(int sum)
{
    if(sum<3) return ;
    int small=1;
    int big  =2;
    int Big  =(1+sum)/2;
    int cur_sum=(small+big);
    while(small<Big)
    {
        if(cur_sum==sum)
            print_continous_seq(small,big);
        while(cur_sum>sum && small<Big)
        {
            cur_sum-=small;
            small++;
            if(cur_sum==sum)
            print_continous_seq(small,big);
        }
        big++;
        cur_sum+=big;
    }
}

/* ====================测试代码====================*/
void Test(char* testName, int sum)
{
    if(testName != NULL)
        printf("%s for %d begins: \n", testName, sum);
    find_continous_seq(sum);
}

int main(int argc,char* argv[])
{
    Test("test1", 1);
    Test("test2", 3);
    Test("test3", 4);
    Test("test4", 9);
    Test("test5", 15);
    Test("test6", 100);
    return 0;
}

【后记】 在上述代码中,求连续序列的和应用了一个小技巧。通常我们可以用循环求一个连续序列的和,但考虑到每一次操作之后的序列和操作之前的序列相比大部分数字都是一样的,只是增加或者减少了一个数字,因此我们可以在前一个序列的和的基础上求操作之后的序列的和。从而减少很多不必要的运算,提高代码效率。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值