leetcode笔记之数组

数组要注意越界问题

一.数组的和

一般转化为有序,但是如果是返回位置而不是数的集合,就要用到hashtable

1.两数之和(数组无序,返回位置)

1.申请一个hash_table
unordered_map<int, int> hash;
2.遍历数组,令num = target - nums[i];
2.1.如果num已经在hash_map中了,那就返回hash[num]和i
2.2.否则把nums[i]-i存在hash_map

for (int i = 0; i < nums.size(); i++) {
    int num = target - nums[i];
    if (hash.find(num)!=hash.end()) //存在
        return {hash[num],i};
    hash[nums[i]] = i;
}
return result;

167. 两数之和 II - 输入有序数组

while(i<j)//双向遍历
{
     if(numbers[i]+numbers[j]==target)
         return {i+1,j+1};
     if(numbers[i]+numbers[j] < target) ++i;
     if(numbers[i]+numbers[j] > target) --j;
 }

15. 三数之和(数组无序,返回元素)

sort(nums.begin(), nums.end());//1.排序
for (int i = 0; i < n - 2; ++i)//2. 0-n-3遍历
{//先确定一个值,再双向遍历找另外两个
    int l = i + 1, r = n - 1;//2.1双向遍历
    //2.1.和为0一定有正有负或者3个数为0,如果三个数没有负值,那就return吧 
    if (nums[i] > 0) return v;  
    //2.2.最小的sum都大于target,因此return;
    if (nums[i] + nums[l] + nums[l+1] > 0) break;
    //2.3.找到可以组成targrt的最小的num;num+sum2_max<target则continue;
    if (nums[i] + nums[n-1] + nums[n-2] < 0) continue;
    while (l < r) //3.前后双向遍历,
    {
        if (nums[i] + nums[l] + nums[r] == 0)//3.1.如果找到了另外2个值
        {
            v.push_back({nums[i], nums[l], nums[r]});//就把其放入v
            //然后跳过重复值
            while(nums[i] == nums[i + 1]) ++i;
            while(nums[l] == nums[l+1]) ++l;
            while(nums[r] == nums[r-1]) --r;
            //移动
            --r;++l;
        }
        else if (nums[i] + nums[l] + nums[r] < 0) ++l;//3.2 ++l       
        else --r; //3.3 --r
    }
}
return v;

18.四数之和(数组无序,返回元素)

sort(nums.begin(), nums.end());//1.排序
for (int i = 0; i < n - 3; i ++) //2.确定第1个值
{
     if (nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) //2.1.最小的sum都大于target,因此return
         return v;
     if (nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3] < target) //2.2找到可以组成targrt的最小的num;num+sum3_max<target则continue
         continue;
      for (int j = i + 1; j < n - 2; j ++) //3.确定第2个值
      {
          int l = j + 1; int r = n - 1;
           //3.1 nums[j]的值过大,导致2.2.最小的sum都大于target,因此本次所有j遍历结束,下一个i
          if (nums[i] + nums[j] + nums[l] + nums[l + 1] > target) break;
          //3.2 本次nums[j]再加sum2_max都小于target,continue吧
          if (nums[i] + nums[j] + nums[n - 1] + nums[n - 2] < target) continue;
           while (l < r) //4.双向遍历
           {
               if (nums[i] + nums[j] + nums[l] + nums[r] == target) //4.1满足条件
               {
                   v.push_back({nums[i], nums[j], nums[l], nums[r]});
                   //4.1.1跳到最左/右边的重复值
                   while (nums[i] == nums[i + 1]) ++i;
                   while (nums[j] == nums[j + 1]) ++j;
                   while (nums[l] == nums[l + 1]) ++l;
                   while (nums[r] == nums[r - 1]) --r;
                   ++l;
                   --r;
               } 
               else if (nums[i] + nums[j] + nums[l] + nums[r] < target) //4.2 sum < targe,l右移
                   l++;
               else r--;//4.3 sum > targer,左移
           }
    }
}
return v;

16. 最接近的三数之和

sort(nums.begin(),nums.end());//1.先排序
int min_dif=INT_MAX,dif;
for(int i =0;i<n-1;++i){//2.确定第一个数
    j=i+1;k=n-1;
    while(j<k)//3.双向遍历,重复的数也可以使用
    {
        sum = nums[i] + nums[j] + nums[k];
        dif = sum-target;
        if(abs(dif)<min_dif)//更新 
        {
            res=sum;
            min_dif=abs(dif);
        }
        //移动
        if(dif<0) j++;
        if(dif>0) k--;
        if(dif==0) return res;
    }
}
return res;

二. 组合总和

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取

vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
    sort(candidates.begin(), candidates.end());//1.排序
    vector<vector<int> > v;
    vector<int> tmp;
    combinationSum(candidates, target, v, tmp, 0);//调用递归函数
    return v;
}

void combinationSum(vector<int>& candidates, int target, vector<vector<int> >& v, vector<int>& tmp, int begin) {
    if (target==0) //1.只有target恰好减为0时,才加入v;
    {
        v.push_back(tmp);
        return;
    }
    //每一层都有一个for,以此来找最终符合target==0条件的tmp
    for (int i = begin; i != candidates.size() && target >= candidates[i]; ++i) //从begin开始遍历
    {
        tmp.push_back(candidates[i]);
        //只要target >= candidates[i],就往下一层递归
        //target 不断减小,combination不断加入新的值,begin在++
        combinationSum(candidates, target - candidates[i], v, tmp, i);
        tmp.pop_back();//下一层不满足target=0,上一层就pop_back,
    }
}

40. 组合总和 II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次

vector<vector<int> > combinationSum2(vector<int>& candidates, int target) {
   sort(candidates.begin(),candidates.end());//1.排序
    vector<vector<int> > v;
    vector<int> tmp;
    solver(candidates, target, v, tmp, 0);//2.递归
    return v;
}

void solver(vector<int>& candidates, int target, vector<vector<int> >& v, vector<int>& tmp, int beg)
{
    if(target==0)//3.最终恰好减为1,tmp才符合条件
    {
        v.push_back(tmp);//3.1加入v
        return;
    }
    for (int i=beg; i<candidates.size() && candidates[i]<=target; i++)//4.每一递归层,满足candidates[i]<=target,则继续for遍历
    {
        if (i!=beg && candidates[i]==candidates[i-1]) //**4.1 保证每个数字在每个组合中只能使用一次
            continue;
        tmp.push_back(candidates[i]);//4.2
        //**4.3. 递归, target-candidates[i] ,i+1保证每个数字在每个组合中只能使用一次
        solver(candidates, target-candidates[i], v, tmp , i+1);
        tmp.pop_back();//4.3 保证每层只push_back一个元素
    }
}

216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有1 - 9的正整数,并且每种组合中不存在重复的数字。
注:0~9中k个数和为n,就是在 组合总和II 的基础上,
限制了0<target<501<k<10
数组中的元素为[1~9],并且无重复;
并且限制了元素个数为k。

vector<vector<int>> combinationSum3(int k, int n) {
    vector<vector<int> > v;
    vector<int> tmp;
    //1.因为是已排序,不用再排序,可以看成0~9,只不过从1开始算,则是为了保证下标和数值是一致的    
    help(k, n, v, tmp, 1);    
    return v;
}
 void help(int k, int target, vector<vector<int> >& v, vector<int>& tmp, int beg) 
{
    if (target < 0 || k > 9|| beg > 10 || tmp.size() > k  ) //2.特殊条件判断
        return;
    if (target == 0 && tmp.size() == k) //3.恰好target == 0,并且同时tmp.size() == k才放入v
    {
        v.push_back(tmp);
        return;
    }
    for (int i = beg; i < 10 && i<=target; i++) //4.i<=target
    {
        tmp.push_back(i);
        help(k, target - i, v, tmp, i + 1); //5.递归;i+1,保证不重复
        tmp.pop_back();
    }
} 

53. 最大子序和(动态规划)

dp[i]:以i为最后一个元素,最大 连续子数组和:dp[i] = nums[i] + max(dp[i - 1] , 0 );,可以看出只要maxsum>0,他就是有价值的。

int maxSubArray(vector<int>& nums) {
    int n = nums.size();
    vector<int> dp(n,0);
    dp[0] = nums[0];
    int maxsum = dp[0];  
    for(int i = 1; i < n; i++)
    {
        dp[i] = nums[i] + max(dp[i - 1] , 0 );
        maxsum = max(maxsum, dp[i]);
    }        
    return maxsum;
}

二.移除数组中的元素/合并区间/插入

26. 删除排序数组中的重复项

int resi=0;
for(int i=0;i<n;++i)//1.遍历,把不重复的值和v[resi]处的值交换,然后++resi
{
    int tmp=nums[i];
    //1.2 因为下面的交换,使得nums[resi]处的值变成了nums[i],
    std::swap(nums[i],nums[resi++]);//1.3 交换,1.4 个数,也是尾后
    while(i+1<n && tmp==nums[i+1]) ++i;//1.5 忽略重复值,别忘记i+1<n
}

80. 删除排序数组中的重复项 II

在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
遍历,把连续重复不超过2次的值和v[resi]处的值交换,然后++resi

int resi=0;
for(int i=0;i<n;++i)
{
    int tmp=nums[i];
    std::swap(nums[i],nums[resi++]);
    //到倒数第2个重复值的位置;(之前的那个题是到最后一个重复值得位置);别忘记i+2<n!
    while(i+2<n && tmp==nums[i+2]) ++i;//因为是排序的数组,可以不用考虑i+1
}
return resi;

27. 移除元素

元素顺序可以改变,可以遇到val就把他交换到最后

for(int i=0;i<tail+1;++i)
    if(nums[i]==val)
        std::swap(nums[i--],nums[tail--]);//i--:因为刚交换过来的值可能是val
return tail+1;

35. 排序数组搜索插入位置

存在nums[i]==target,则返回i位置
不存在就找到符合条件:nums[i] < target && nums[i+1] >= target的位置

if(!n || nums[0]>=target)//1.0位置
    return 0;
if(nums[n-1] < target)//n位置
    return n;
for(int i=0;i<n-1;++i)
{
    if(nums[i]==target)//1.返回i
        return i;
    if(nums[i] < target && nums[i+1] >= target)//2.返回i+1
        return i+1;
}

56. 合并区间

1.排序
2.先入第一个节点
3.遍历数组
如果v.back().end < intervals[i].start,直接插入intervals[i]
其余情况改变v.back().end 的值即可:v.back().end = max(v.back().end, intervals[i].end);

vector<Interval> merge(vector<Interval>& intervals) {//数组要注意越界问题
    vector<Interval> v;
    int n=intervals.size();
    if(!n)
        return v;
    sort(intervals.begin(),intervals.end(),cmp);//1.先排序,注意比较函数
    v.push_back(intervals[0]);//2.先入第一个节点
    for (int i = 1; i < n; i++)//3.遍历 
    {
        if (v.back().end < intervals[i].start) // 3.1v.back().end < intervals[i].start,直接插入
            v.push_back(intervals[i]);
        else//3.2 其余情况改变v.back().end 的值即可
            v.back().end = max(v.back().end, intervals[i].end);
    }
    return v;
}
static bool cmp(Interval x,Interval y)//注意比较函数声明为static
{  
  return x.start < y.start;
}  

猜你喜欢

转载自blog.csdn.net/qiangzhenyi1207/article/details/80173487