c++数据结构与算法(3)——数组(前缀和数组,差分数组)

数组的题型最多,而且感觉更多的是考验算法技巧的使用,而非简单的数据结构,毕竟数组也没啥数据结构可言啊,本文是前缀和数组和差分数组

典型题目:

303. 区域和检索 - 数组不可变(中等)

304. 二维区域和检索 - 矩阵不可变(中等)

560. 和为K的子数组(中等)

1109. 航班预订统计(中等)

1094. 拼车(中等)

-------------------------------------------

前缀和数组基础,也是leetcode的303题

class NumArray {
public:
    vector<int> sum;
    NumArray(vector<int>& nums) {
        int size = nums.size();
        sum.resize(size+1);
        sum[0] = 0;
        for (int i = 0; i < size; i++) {
            sum[i+1] = sum[i] + nums[i];
        }
    }
    
    int sumRange(int left, int right) {
        return sum[right+1] - sum[left];
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * int param_1 = obj->sumRange(left,right);
 */

二维区域检索

偷张图,理解起来会好很多,红色区域的面积等于绿色区域减去蓝色区域减去橙色区域加上粉色区域。把红色区域想象为num(i,j),那么num(i,j) = sum(i,j) - sum(i)(j-1) - sum(i-1)(j) + sum(i-1)(j-1); 变形可得sum(i,j) = sum(i)(j-1) + sum(i-1)(j) -sum(i-1)(j-1)+num(i,j)。

class NumMatrix {
public:
    vector<vector<int>> sum;
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        if (m == 0) return;
        int n = matrix[0].size();
        sum.resize(m+1, vector<int>(n+1));
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                sum[i+1][j+1] = sum[i+1][j] + sum[i][j+1] - sum[i][j] + matrix[i][j];
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        return sum[row2+1][col2+1] - sum[row1][col2+1] - sum[row2+1][col1] + sum[row1][col1];
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */

和为k的数组,这个理解挺麻烦的,不过做下来感觉hash优化适用所有类似双层嵌套,map通过存储以及其查找速度O(1)特性,优化了j遍历层的速度,结合暴力解法看下。

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int count = 0;
        int size = nums.size();
        vector<int> prefix(size+1, 0);
        for (int i = 0; i < size; i++) {
            prefix[i+1] = prefix[i] + nums[i];
        }
        for (int i = 0; i < size; i++) {
            for (int j = i; j < size; j++) {
                if (prefix[j+1] - prefix[i] == k)
                    count++;
            }
        }
        return count;
    }
};
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int count = 0;
        int size = nums.size();
        vector<int> prefix(size, 0);
        prefix[0] = nums[0];
        for (int i = 1; i < size; i++) {
            prefix[i] = prefix[i-1] + nums[i];
        }
        unordered_map<int, int> mp;
        mp[0] = 1; //令prefix[-1] = 0; prefix[0] - prefix[-1] = nums[0]
        for (int i = 0; i < size; i++) {
            if (mp.find(prefix[i] - k) != mp.end()) {
                count += mp[prefix[i] - k];
            }
            //mp实际储存prefix[j],且0<=j<=i
            mp[prefix[i]]++;
        }
        return count;
    }
};

差分数组基础

// 差分数组工具类
class Difference {
    // 差分数组
    vector<int> diff;
    
    /* 输入一个初始数组,区间操作将在这个数组上进行 */
    Difference(vector<int> nums) {
        diff.resize(nums.size());
        // 根据初始数组构造差分数组
        diff[0] = nums[0];
        for (int i = 1; i < nums.size(); i++) {
            diff[i] = nums[i] - nums[i - 1];
        }
    }

    /* 给闭区间 [i,j] 增加 val(可以是负数)*/
    public void increment(int i, int j, int val) {
        diff[i] += val;
        if (j + 1 < diff.length) {
            diff[j + 1] -= val;
        }
    }

    /* 返回结果数组 */
    vector<int> result() {
        vector<int> res; //结果数组
        // 根据差分数组构造结果数组
        res[0] = diff[0];
        for (int i = 1; i < diff.size(); i++) {
            res[i] = res[i - 1] + diff[i];
        }
        return res;
    }
}

航班预定统计,增加了个数据抽象思维,把实际场景设计为向量,感觉这种题很容易考,数据抽象是很重要的能力,个人华为机试考过类似的题目。

class Solution {
public:
    void increment(vector<int> &diff, int l, int r, int val) 
    {
        diff[l] += val;
        if (r+1 < diff.size())
            diff[r+1] -= val;
    }
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        vector<int> diff(n, 0);
        int size = bookings.size();
        for (int i = 0; i < size; i++) {
            increment(diff, bookings[i][0]-1, bookings[i][1]-1, bookings[i][2]);
        }
        for (int i = 1; i < n; i++) {
            diff[i]+=diff[i-1];
        }
        return diff;
    }
};

拼车,一样数据抽象

class Solution {
public:
    void increment(vector<int> &diff, int l, int r, int val)
    {
        diff[l] += val;
        if (r+1 < diff.size())
            diff[r+1] -= val;
    }
    bool carPooling(vector<vector<int>>& trips, int capacity) {
        vector<int> diff(1001,0);
        int size = trips.size();
        int max = 0;
        for (int i = 0; i < size; i++) {
            if (trips[i][2] > max) {
                max = trips[i][2];
            }
            increment(diff, trips[i][1], trips[i][2]-1, trips[i][0]);
        }
        if (diff[0] > capacity) return false;
        for (int i = 1; i < max; i++) {
            diff[i] += diff[i-1];
            if (diff[i] > capacity) return false;
        }
        return true;
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值