• 2sum、3sum、4sum以及任意连续的数的和为sum、任意连续或者不连续的数的和为sum


    2sum

    如果数组是无序的,先排序(n*logn),然后用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j]>sum,则要想办法让sum 的值减小,所以此刻i 不动,j--,如果某一刻a[i]+a[j]<sum,则要想办法让sum 的值增大,所以此刻i++,j 不动。所以,数组无序的时候,时间复杂度最终为O(n*logn+n)=O(n*logn),若原数组是有序的,则不需要事先的排序,直接O(n)搞定,且空间复杂度还是O(1),此思路是相对于上述所有思路的一种改进。

    Pair findSum(int *s,int n,int x)
    {
    //sort(s,s+n); 如果数组非有序的,那就事先排好序O(N*logN)
        int *begin=s;
        int *end=s+n-1;
        while(begin<end) //俩头夹逼,或称两个指针两端扫描法,很经典的方法,O(N)
        {
            if(*begin+*end>x)
            {
                --end;
            }
            else if(*begin+*end<x)
            {
                ++begin;
            }
            else
            {
                return Pair(*begin,*end);
            }
            178
        }
        return Pair(-1,-1);
    }

    3sum

     vector<vector<int> > threeSum(vector<int> &num) {
            if(num.empty())
                return vector<vector<int> >();
            sort(num.begin(),num.end());
            vector<vector<int> > ret;
            vector<int> tmp;
            int n=num.size();
            for(int i=0;i<n-2;i++)
            {
                if(i>0&&num[i]==num[i-1]) continue;//防止存在重复的元素
                int target=-num[i];
                int j=i+1;
                int k=n-1;
                while(j<k)
                {
                    if(j<k&&k<n-1&&num[k]==num[k+1])
                    {
                        k--;
                        continue;
                    }
                    if(num[j]+num[k]==target)
                    {
                        tmp={num[i],num[j],num[k]};
                        ret.push_back(tmp);
                        j++;
                        k--;
                    }
                    else if(num[j]+num[k]<target)
                    {
                        j++;
                    }
                    else if(num[j]+num[k]>target)
                        k--;
                }
            }
            return ret;
        }

    4sum

        vector<vector<int> > fourSum(vector<int> &num,int target)
        {
            if(num.empty())
                return vector<vector<int> >();
            sort(num.begin(),num.end());
            vector<vector<int> > ret;
            int n=num.size();
            int i,j;
            for(i=0; i<n-3; i++)
            {
                //只保留第一个不重复的,其余的都删了,因为left会选择重复的
                if(i>=1&&num[i]==num[i-1])
                    continue;
                for(j=n-1; j>i+2; j--)
                {
                    //只保留最后一个不重复的,其余的都删了,因为right会选择重复的
                    if(j<n-1&&num[j+1]==num[j])
                       continue;
                    int left=i+1;
                    int right=j-1;
                    vector<int> tmp;
                    while(left<right)
                    {
                        //只保留最后一个不重复的,其余的都删了,因为left会选择重复的
                        if(right<j-1&&num[right]==num[right+1])
                        {
                            right--;
                            continue;
                        }
                        if(num[i]+num[j]+num[left]+num[right]==target)
                        {
                            tmp= {num[i],num[left],num[right],num[j]};
                            ret.push_back(tmp);
                            left++;
                            right--;
                        }
                        else if(num[i]+num[j]+num[left]+num[right]<target)
                            left++;
                        else if(num[i]+num[j]+num[left]+num[right]>target)
                            right--;
                    }
                }
            }
            return ret;
        }

    任意连续数的和为sum(假设至少存在两个数)

    void sum(int sum,int n)
    {
        if(sum<0||n<2)
            return -1;
        int cursum=0;
        cursum=1+2;
        int i=0,j=1;
        while(j<=n)
        {
            if(cursum==sum)
            {
                int k=i;
                while(k<=j)
                {
                    cout<<k<<' ';
                    k++;
                }
                cout<<endl;
                cursum-=i;
                i++;
            }
            else if(cursum<sum)
            {
                j++;
                cursum+=j;
            }
            else if(cursum>sum)
            {
                cursum-=i;
                i++;
            }
        }
    }

    任意数的和为sum

    用回溯的方法实现:

    #include<iostream>
    #include<vector>
    using namespace std;
    
    void findhelper(int m,int n,int start,vector<int> &path)
    {
        if(m<0)
            return;
        if(m==0)
        {
            for(auto a:path)
                cout<<a<<' ';
            cout<<endl;
            return;
        }
        for(int i=start; i<=n; ++i)
        {
            path.push_back(i);
            findhelper(m-i,n,i+1,path);
            path.pop_back();
        }
    }
    void findSum(int m,int n)
    {
        vector<int> path;
        findhelper(m,n,1,path);
    }
    
    int main()
    {
        findSum(15,20);
    }

     用0-1背包法实现:

    注意到取n,和不取n个区别即可,考虑是否取第n个数的策略,可以转化为一个只和前n-1个数相关的问题。

    • 如果取第n个数,那么问题就转化为“取前n-1个数使得它们的和为sum-n”,对应的代码语句就是sumOfkNumber(sum - n, n - 1);
    • 如果不取第n个数,那么问题就转化为“取前n-1个数使得他们的和为sum”,对应的代码语句为sumOfkNumber(sum, n - 1)。

    实现代码:

    #include<iostream>
    #include<vector>
    using namespace std;
    
    void findhelper(int sum,int n,vector<int> &path)
    {
      //递归出口
    if(sum<=0||n<=0) return;
      //输出找到的结果
    if(sum==n) { for(int i=path.size()-1; i>=0; --i) cout<<path[i]<<' '; cout<<n<<endl; } path.push_back(n); //典型的01背包问题 findhelper(sum-n,n-1,path);//放第n个元素 path.pop_back(); findhelper(sum,n-1,path); //不放第n个元素 } void findSum(int m,int n) { vector<int> path; findhelper(m,n,path); } int main() { findSum(15,20); }

     存在重复元素时,求和为sum

    #include<iostream>
    #include<vector>
    #include<algorithm>
    using namespace std;
    
    void helper(vector<int> &num,vector<int> &path,int start,int sum)
    {
        if(sum==0)
        {
            for(int i=0;i<path.size();++i)
                cout<<path[i]<<' ';
            cout<<endl;
            return;
        }
        if(sum<0)
            return;
        for(int i=start;i<num.size();++i)
        {
            if(i>start&&num[i]==num[i-1])
                continue;
            path.push_back(num[i]);
            helper(num,path,i+1,sum-num[i]);
            path.pop_back();
        }
    }
    void findSum(vector<int> &num,int sum)
    {
        vector<int> path;
        sort(num.begin(),num.end());
        helper(num,path,0,sum);
    }
    
    int main()
    {
        vector<int> num={1,2,1,3,0,1};
        findSum(num,3);
    }
  • 相关阅读:
    java 显示当前的时间
    java RandomAccessFile 向文件中写入数据,怎么样不覆盖原来的数据
    NCARG 6.2.0 安装方法
    【转】在Fedora 9下用IFC安装MM5
    Linux下GrADS的安装
    我的博客园
    nyist 2 括号配对问题
    nyist 100 1的个数
    进制(堆栈)
    进制
  • 原文地址:https://www.cnblogs.com/wuchanming/p/4374185.html
Copyright © 2020-2023  润新知