LeetCode【剑指offer】系列(数组篇)

剑指offer03.数组中重复的数字

题目链接

题目:设备中存有n个文件,文件id记于数组documents。若文件id相同,则定义为该文件存在副本。请返回任一存在副本的文件id

思路:用哈希表来存已经访问过的文件id,当在表里找到某一项id存在时,说明之前已经访问过了。

通过代码:

cpp 复制代码
class Solution {
public:
    int findRepeatDocument(vector<int>& documents) {
        unordered_map<int, bool> exist;
        for(int num : documents)
        {
            if(exist.find(num) != exist.end())
                return num;
            exist[num] = true;
        }
        return -1;
    }
};

剑指offer04.二维数组中的查找

题目链接

题目:m*n的二维数组plants记录了园林景观的植物排布情况,具有以下特性:

  • 每行中,每棵植物的右侧相邻植物不矮于该植物;
  • 每列中,每棵植物的下侧相邻植物不矮于该植物。

请判断 plants 中是否存在目标高度值 target

思路:暴力不讲了。从右上角开始,如果比target大,就往左找;如果比target小,就往下找。

通过代码:

cpp 复制代码
class Solution {
public:
    bool findTargetIn2DPlants(vector<vector<int>>& plants, int target) {
        if(plants.size() == 0 || plants[0].size() == 0)
            return false;
        int i = 0, j = plants[0].size() - 1;
        while(i < plants.size() && j >= 0)
        {
            if(target == plants[i][j])
                return true;
            else if(target > plants[i][j])
                i++;
            else
                j--;
        }
        return false;
    }
};

剑指offer11.旋转数组的最小数字

题目链接

题目:仓库管理员以数组stock形式记录商品库存表。stock[i]表示商品id,可能存在重复。原库存表按商品id升序排列。现因突发情况需要进行商品紧急调拨,管理员将这批商品id提前依次整理至库存表最后。请你找到并返回库存表中编号的最小的元素以便及时记录本次调拨。

思路:暴力法不讲了。直接讲二分。这道题其实说的不清楚。我们将整个数组分为左排序数组(简称左支)和右排序数组(简称右支),其中左支的任一元素大于等于右支的任一元素 。并且可能有只有右支的情况。

当我们用二分法的时候,能确定high肯定在右支,但low不一定在左支。因此,后面的比较都是和high比。

  • 当mid小于high时,由于左支的任一元素大于等于右支的任一元素 ,所以mid不可能在左支。因此mid右边的就没必要遍历了,直接high = mid。为什么不是mid-1?因为mid-1可能越过旋转点,导致high跑到左支上。而我们要确保high在右支上。
  • 当mid大于high时,我们能确定mid在左支。因为mid在右支就必然小于high。所以mid左边的就没必要遍历了,直接low = mid + 1。由于mid可能是左支的最后一个,因此mid + 1之后low可能越过左支到达旋转点的位置。一旦low和high都在右支,此时就只会出现mid小于high的情况,因此low的位置就不会动了,因此最后要返回stock[low]
  • 当mid等于high时,不能确定high或low该如何移动,只能确定high指向的元素有备份,因此high--

为什么mid和high比不和low比?因为有可能出现没有左支的情况。这样当mid大于low时,mid其实是在右支;如果有左支的话,这种情况mid应该在左支。这就无法判断了。

通过代码:

cpp 复制代码
class Solution {
public:
    int inventoryManagement(vector<int>& stock) {
        int low = 0, high = stock.size() - 1;
        while(low < high)
        {
            int mid = low + (high - low) / 2;
            if(stock[mid] < stock[high])
                high = mid;
            else if(stock[mid] > stock[high])
                low = mid + 1;
            else
                high--;
        }
        return stock[low];
    }
};

剑指offer14-I.剪绳子

题目链接

题目:现需要将一根长为正整数bamboo_len的竹子砍为若干段,每段长度均为正整数。请返回每段竹子长度的最大乘积是多少。

思路一:动态规划。dp[i]表示长度为i的竹子的拆分后的最大乘积。对于dp[i],我们可以先砍一段长度为1的,剩下长度为i-1的可以保留也可以继续砍,如果保留乘积就是1*(i - 1),如果继续砍就是1*dp[i - 1]。两者取最大值就是砍长度为1的一段的最大乘积。当然我们也可以砍一段长度为2,3,...,i-1。用一个循环遍历取最大值即可。

通过代码:

cpp 复制代码
class Solution {
public:
    int cuttingBamboo(int bamboo_len) {
        vector<int> dp(bamboo_len + 1);
        dp[1] = 1;
        for(int i = 2; i <= bamboo_len; i++)
        {
            for(int j = 1; j < i / 2 + 1; j++)
            {
                int maxn = max(j * (i - j), j * dp[i - j]);
                dp[i] = max(dp[i], maxn);
            }    
        }
        return dp[bamboo_len];
    }
};

思路二:贪心。结论,长度大于4时,优先切长度为3的段。长度为4时,切成2*2;长度为3时切成1*2;长度为2时切成1*1。

通过代码:

cpp 复制代码
class Solution {
public:
    int cuttingBamboo(int bamboo_len) {
        if(bamboo_len < 4)
            return bamboo_len - 1;
        int res = 1;
        while(bamboo_len > 4)
        {
            res *= 3;
            bamboo_len -= 3;
        }
        return res * bamboo_len;
    }
};

用循环一次次减还是没必要,直接算可以切几个3然后用幂运算即可。比较特殊的是模3余1的时候,这时候留一个3和余数凑一个4更划算。

cpp 复制代码
class Solution {
public:
    int cuttingBamboo(int bamboo_len) {
        if(bamboo_len < 4)
            return bamboo_len - 1;

        if(bamboo_len % 3 == 0)
            return pow(3, bamboo_len / 3);
        else if(bamboo_len % 3 == 1)
            return 4 * pow(3, bamboo_len / 3 - 1);
        else
            return 2 * pow(3, (bamboo_len - 2) / 3);
    }
};

剑指offer14-II.剪绳子II

题目链接

题目:现需要将一根长为正整数bamboo_len的竹子砍为若干段,每段长度均为正整数。请返回每段竹子长度的最大乘积是多少。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

思路:这道题是上一题的数据加强版。哪怕是long long也没法存下这么大的数了。这时候就没办法用动态规划了。因为求模运算在通过max的时候没有传递性。例如,6和9在模8的情况下。

所以要用上面的思路二,也就是贪心的方法。由于在求幂的过程中可能会溢出,所以自己写一个my_pow用来求幂。由于bamboo_len最大才1000,所以循环求幂就可以了。如果数据规模更大的话,还能用快速幂加速。

通过代码:

cpp 复制代码
class Solution {
public:
    int mod = 1e9 + 7;

    long long my_pow(int n){
        long long res = 1;
        while(n--)
        {
            res = (res * 3) % mod;
        }
        return res;
    }

    int cuttingBamboo(int bamboo_len) {
        if(bamboo_len < 4)
            return bamboo_len - 1;
        
        if(bamboo_len % 3 == 0)
            return my_pow(bamboo_len / 3);
        else if(bamboo_len % 3 == 1)
            return (4 * my_pow(bamboo_len / 3 - 1)) % mod;
        else
            return (2 * my_pow(bamboo_len / 3)) % mod;
    }
};

剑指offer17.打印从1到最大的n位数

题目链接

题目:实现一个十进制数字报数程序,请按照数字从小到大的顺序返回一个整数数列,该数列从数字1开始,到最大的正整数cnt位数字结束。

思路:cnt位的最大数字位 1 0 c n t − 1 10^{cnt}-1 10cnt−1,循环遍历将数添加进vector即可。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> countNumbers(int cnt) {
        vector<int> res;
        for(int i = 1; i <= pow(10, cnt) - 1; i++)
            res.push_back(i);
        return res;
    }
};

剑指offer21.调整数组顺序使奇数位于偶数前面

题目链接

题目:教练使用整数数组actions记录一系列核心肌群训练项目编号。为增强训练趣味性,需要将所有奇数编号训练项目调整至偶数编号训练项目之前。请将调整后的训练项目编号以数组形式返回。

思路:双指针。i从前往后找偶数,j从后往前找奇数,找到就交换。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> trainingPlan(vector<int>& actions) {
        int i = 0, j = actions.size() - 1;
        while(i < j)
        {
            while(i < j && actions[i] % 2 == 1)
                i++;
            while(i < j && actions[j] % 2 == 0)
                j--;
            int tmp = actions[i];
            actions[i++] = actions[j];
            actions[j--] = tmp;
        }
        return actions;
    }
};

剑指offer39.数组中出现次数超过一半的数字

题目链接

题目:仓库管理员以数组stock形式记录商品库存表。stock[i]表示商品id,可能存在重复。请返回库存表中数量大于stock.length / 2的商品id

思路:哈希表可以做,很简单,就不说了,时间复杂度和空间复杂度都为O(n)。这里介绍摩尔投票法。先选一个候选者target,支持候选者(num == target)votes加一,反对候选者(num != target)votes减一。经过一轮投票,若votes为0,重新选当前数为候选者,知道最后一轮的候选者就是绝对众数(数量超过半数的数)。

通过代码:

cpp 复制代码
class Solution {
public:
    int inventoryManagement(vector<int>& stock) {
        int target, votes = 0;
        for(int num : stock)
        {
            if(votes == 0)
                target = num;
            votes += (num == target ? 1 : -1);
        }
        return target;
    }
};

剑指offer40.最小的k个数

题目链接

题目:仓库管理员以数组stock形式记录商品库存表,其中stock[i]表示对应商品库存余量。请返回库存余量最少的cnt个商品余量,返回 顺序不限。

思路:排序的方法就不说了。这里介绍堆的方法。首先建立一个大顶堆,然后把前cnt个商品先放到堆里。然后遍历剩余的商品,如果有比堆顶更小的,弹出原堆顶并入堆。也就是说,堆里维护的就是最小的k个数。最后将这k个数拿出来放进vector里即可。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> inventoryManagement(vector<int>& stock, int cnt) {
        vector<int> res(cnt);
        if(cnt == 0)
            return res;

        priority_queue<int> heap;
        for(int i = 0; i < cnt; i++)
            heap.push(stock[i]);
        for(int i = cnt; i < stock.size(); i++)
        {
            if(stock[i] >= heap.top())
                continue;
            heap.pop();
            heap.push(stock[i]);
        }
        
        for(int i = 0; i < cnt; i++)
        {
            res[i] = heap.top();
            heap.pop();
        }
        return res;
    }
};

剑指offer41.数组流中的中位数

题目链接

题目:中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。

思路:如果维护一个有序的数组,那么找中位数很快,只要O(1);但是插入数据需要O(n)。最优解应该是小顶堆+大顶堆。小顶堆保存较大的一半;大顶堆保存较小的一半;这里规定总数为奇数时,多出来的一个放在小顶堆。

当有一个新的数要插入时,如果根据它的值来判断插在哪个堆就太麻烦了。我们可以通过在两个堆之间倒腾进行维护。例如,目前总数为偶数,即两个堆size一样,根据之前的规定,多出来的放在小顶堆。但是这个数可能属于较小的一半,直接放小顶堆不合适。因此,我们先把他插在大顶堆,然后从大顶堆的堆顶拿一个放到小顶堆。这样就保证了小顶堆保存较大的一半。

查询中位数时,如果是奇数个,小顶堆的堆顶就是中位数;如果是偶数个,两个堆各取堆顶再平均即可。

通过代码:

cpp 复制代码
class MedianFinder {
public:
    priority_queue<int> big_heap;
    priority_queue<int, vector<int>, greater<int>> small_heap;

    MedianFinder() {
    }
    
    void addNum(int num) {
        if(big_heap.size() == small_heap.size())
        {
            big_heap.push(num);
            small_heap.push(big_heap.top());
            big_heap.pop();
        }
        else
        {
            small_heap.push(num);
            big_heap.push(small_heap.top());
            small_heap.pop();
        }
    }
    
    double findMedian() {
        if(big_heap.size() == small_heap.size())
            return (big_heap.top() + small_heap.top()) / 2.0;
        else
            return small_heap.top();
    }
};

剑指offer45.把数组排成最小的数

题目链接

题目:闯关游戏需要破解一组密码,闯关组给出的有关密码的线索是:

  • 一个拥有密码所有元素的非负整数数组password
  • 密码是password中所有元素拼接后得到的最小的一个数

请编写一个程序返回这个密码。

思路:自定义排序。如果ab<ba,那么a应该排在b前面。其中,ab表示拼接之后的值。

通过代码:

cpp 复制代码
class Solution {
public:

    static bool cmp(int a, int b){
        string s1 = to_string(a) + to_string(b);
        string s2 = to_string(b) + to_string(a);
        return s1 < s2;
    }

    string crackPassword(vector<int>& password) {
        sort(password.begin(), password.end(), cmp);
        string res;
        for(int num : password)
            res += to_string(num);
        return res;
    }
};

剑指offer49.丑数

题目链接

题目:给你一个整数n,请你找出并返回第n个丑数 。说明:丑数是只包含质因数 2、3 和/或 5 的正整数;1 是丑数。

思路:动态规划。由于丑数乘上2,3或5还是丑数,所以可以从第一个丑数开始,乘上2,3,5后向后扩展。dp[i]表示第i个丑数,然后用3个指针分别表示乘2,3,5,代表下一个丑数是当前指针指向的丑数乘以对应的质因数。每次扩展只取最小的填入dp[i],然后将对应的指针向后移一位,其他两个指针不动。

通过代码:

cpp 复制代码
class Solution {
public:
    int nthUglyNumber(int n) {
       vector<int> dp(n + 1);
       dp[1] = 1;
       int p2 = 1, p3 = 1, p5 = 1;
       for(int i = 2; i <= n; i++)
       {
            dp[i] = min({dp[p2] * 2, dp[p3] * 3, dp[p5] * 5});
            if(dp[i] == dp[p2] * 2)
                p2++;
            if(dp[i] == dp[p3] * 3)
                p3++;
            if(dp[i] == dp[p5] * 5)
                p5++;
       }
       return dp[n];
    }
};

剑指offer51.数组中的逆序对

题目链接

题目:在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录record,返回其中存在的「交易逆序对」总数。

思路一:归并排序。归并排序通过分治的思想将数组划成两半,分别排序后再合并。这道题就可以在合并的时候动手脚。合并的时候两半的数组都是有序的。所谓逆序对就可以在前一半里找一个大的,在后一半里找一个小的。

通过代码:

cpp 复制代码
class Solution {
public:
    int mergeSort(vector<int> &record, vector<int> &tmp, int l, int r){
        if(l >= r)
            return 0;
        int mid = (l + r) / 2;
        int res = mergeSort(record, tmp, l, mid) + mergeSort(record, tmp, mid + 1, r);
        int i = l, j = mid + 1, pos = l;
        while(i <= mid && j <= r)
        {
            if(record[i] <= record[j])
            {
                tmp[pos] = record[i];
                i++;
                res += j - (mid + 1);
            }
            else
            {
                tmp[pos] = record[j];
                j++;
            }
            pos++;
        }
        for(int k = i; k <= mid; k++)
        {
            tmp[pos++] = record[k];
            res += j - (mid + 1);
        }
        for(int k = j; k <= r; k++)
            tmp[pos++] = record[k];
        copy(tmp.begin() + l, tmp.begin() + r + 1, record.begin() + l);
        return res;
    }

    int reversePairs(vector<int>& record) {
        int n = record.size();
        vector<int> tmp(n);
        return mergeSort(record, tmp, 0, n - 1);
    }
};

思路二:离散化树状数组。将树状数组当桶来用,每个桶里面表示对应下标的数有几个。从后往前遍历record数组,对每一项查询树状数组中的前缀和,这就表示比当前数小的数有几个。由于我们是逆序遍历的,已经在桶中的数其实是在这一项后面的,这就形成了逆序对。查询完之后将当前项加入到桶中,通过树状数组的更新操作是对应桶加1。

通过代码:

cpp 复制代码
class BIT{
private:
    vector<int> tree;
    int n;
public:
    BIT(int len): tree(len + 1), n(len) {}

    int lowbit(int x){
        return x & -x;
    }

    int query(int x){
        int sum = 0;
        for(;x > 0; x -= lowbit(x))
            sum += tree[x];
        return sum;
    }

    void update(int x, int val){
        for(;x <= n; x += lowbit(x))
            tree[x] += val;
    }
};

class Solution {
public:

    int reversePairs(vector<int>& record) {
        vector<int> tmp(record);
        sort(tmp.begin(), tmp.end());
        // 离散化,将record中的值用索引表示
        for(int &num : record)
            num = lower_bound(tmp.begin(), tmp.end(), num) - tmp.begin() + 1;
        // 树状数组求逆序对个数
        int res = 0;
        BIT bit(record.size());
        for(int i = record.size() - 1; i >= 0; i--)
        {
            res += bit.query(record[i] - 1);// 由于前缀和包括自己,所以要减一
            bit.update(record[i], 1);
        }
        return res;
    }
};

剑指offer53-I.在排序数组中查找数字

题目链接

题目:某班级考试成绩按非严格递增顺序记录于整数数组scores,请返回目标成绩target的出现次数。

思路:二分法。由于数组有序,可以用二分法分别找到目标的左边界和右边界。直接做差计算得到出现次数。

通过代码:

cpp 复制代码
class Solution {
public:
    int countTarget(vector<int>& scores, int target) {
        int idx = lower_bound(scores.begin(), scores.end(), target) - scores.begin();
        int end_idx = upper_bound(scores.begin(), scores.end(), target) - scores.begin();
        return end_idx - idx;
    }
};

剑指offer53-II.0~n-1中缺失的数字

题目链接

题目:某班级 n 位同学的学号为 0 ~ n-1。点名结果记录于升序数组records。假定仅有一位同学缺席,请返回他的学号。

思路:二分法。因为有一个缺席,所以从缺席的位置开始,后面的数字和下标都是错位的。缺席的位置之前,学号和下标相同。因此,我们要找的是错位的第一位。利用这一点,我们就可以用二分法。如果mid == records[mid],说明mid左边都是正常的,错位的位置在mid右边。

通过代码:

cpp 复制代码
class Solution {
public:
    int takeAttendance(vector<int>& records) {
        int i = 0, j = records.size() - 1;
        while(i <= j)
        {
            int mid = (i + j) / 2;
            if(mid == records[mid])
                i = mid + 1;
            else
                j = mid - 1;
        }
        return i;
    }
};

剑指offer57-I.和为s的两个数字

题目链接

题目:购物车内的商品价格按照升序记录于数组price。请在购物车中找到两个商品的价格总和刚好是target。若存在多种情况,返回任一结果即可。

思路:双指针。哈希表可以做,但是双指针更快一点。从两端向中间遍历,如果指针指向的数的和大于target,说明右边的数太大了,j--;反之,左边的数太小了,i++

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& price, int target) {
        int i = 0, j = price.size() - 1;
        while(i < j)
        {
            int s = price[i] + price[j];
            if(s == target)
                return {price[i], price[j]};
            else if(s > target)
                j--;
            else
                i++;
        }
        return {};
    }
};

剑指offer57-II.和为s的连续正数序列

题目链接

题目:待传输文件被切分成多个部分,按照原排列顺序,每部分文件编号均为一个 正整数(至少含有两个文件)。传输要求为:连续文件编号总和为接收方指定数字 target的所有文件。请返回所有符合该要求的文件传输组合列表。

注意,返回时需遵循以下规则:

  • 每种组合按照文件编号升序排列;
  • 不同组合按照第一个文件编号升序排列。

思路:双指针。本题是对上一题的扩展,只不过数量从两个变成了若干个。用双指针维护一个候选区间,左闭右开。同时用sum维护这个区间的和。若sum > target,说明太大了,左区间向右移一个。若sum < target,说明和太小了,右区间向右扩展一下。若sum == target,保存一下答案。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> fileCombination(int target) {
        vector<vector<int>> res;
        int i = 1, j = 2, sum = 1;
        while(i <= target / 2)
        {
            if(sum == target)
            {
                vector<int> tmp;
                for(int k = i; k < j; k++)
                    tmp.push_back(k);
                res.push_back(tmp);
                sum -= i;
                i++;
            }
            else if(sum < target)
            {
                sum += j;
                j++;
            }
            else
            {
                sum -= i;
                i++;
            }
        }
        return res;
    }
};

剑指offer59-I.滑动窗口的最大值

题目链接

题目:科技馆内有一台虚拟观景望远镜,它可以用来观测特定纬度地区的地形情况。该纬度的海拔数据记于数组heights,其中heights[i]表示对应位置的海拔高度。请找出并返回望远镜视野范围limit内,可以观测到的最高海拔值。

思路:单调队列。《代码随想录系列》有这道题,这里简单说一下思想。窗口内会有一个最大值,我们知道随着窗口移动这个最大值最终会移出窗口。当这个最大值移出去的时候,接替他的值一定在他后面。假设窗口内第二大的数在最大值前面,由于最大值不出去,所以这个第二大的数实际是不发挥作用的。真正发挥作用的是最大值后面出现的数。所以我们要维护一个单调队列,从窗口内最大值开始,第二大数是最大值后面第二大的,而不是全局最大的,第三大数是第二大数后面最大的......

用双端队列来实现,当有一个新数进入窗口时,将队列末尾比这个新数小的都出队,然后新数再入队。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> maxAltitude(vector<int>& heights, int limit) {
        vector<int> res;
        if(heights.size() == 0)
            return res;
        deque<int> window;
        for(int i = 0; i < limit; i++)
        {
            while(!window.empty() && heights[i] > window.back())
                window.pop_back();
            window.push_back(heights[i]);
        }
        res.push_back(window.front());
        for(int i = limit; i < heights.size(); i++)
        {
            if(heights[i - limit] == window.front())
                window.pop_front();
            while(!window.empty() && heights[i] > window.back())
                window.pop_back();
            window.push_back(heights[i]);
            res.push_back(window.front());
        }
        return res;
    }
};

剑指offer61.扑克牌的顺子

题目链接

题目:展览馆展出来自 13 个朝代的文物,每排展柜展出 5 个文物。某排文物的摆放情况记录于数组 places,其中 places[i] 表示处于第 i 位文物的所属朝代编号。其中,编号为 0 的朝代表示未知朝代。请判断并返回这排文物的所属朝代编号是否能够视为连续的五个朝代(如遇未知朝代可算作连续情况)。

思路:关键在于理解题目。他所说的连续的朝代不一定要按照数组中的顺序,只要能凑出排序后是连续的五个朝代就行。五个连续的朝代最大值 - 最小值 < 5

通过代码:

cpp 复制代码
class Solution {
public:
    bool checkDynasty(vector<int>& places) {
        unordered_set<int> hash;
        int ma = 0, mi = 14;
        for(int num : places)
        {
            if(num == 0)
                continue;
            ma = max(ma, num);
            mi = min(mi, num);
            if(hash.find(num) != hash.end())
                return false;
            hash.insert(num);
        }
        return ma - mi < 5;
    }
};

剑指offer64.求1+2+...+n

题目链接

题目:请设计一个机械累加器,计算从1、2... 一直累加到目标数值target的总和。注意这是一个只能进行加法操作的程序,不具备乘除、if-else、switch-case、for循环、while 循环,及条件判断语句等高级功能。

思路:递归。递归需要一个出口的判断,正常来说要用到if。但是可以用&&来代替。A && B只有A为true时,B才会被执行。所以我们可以让B为target += mechanicalAccumulator(target - 1),A为target即可。

通过代码:

cpp 复制代码
class Solution {
public:
    int mechanicalAccumulator(int target) {
        target && (target += mechanicalAccumulator(target - 1));
        return target;
   }
};

思路二:还有一种骚操作就是用sizeof函数来计算乘法。例如n*(n+1),其值等于bool a[n][n+1]; sizeof(a)

cpp 复制代码
class Solution {
public:
    int mechanicalAccumulator(int target) {
        bool a[target][target + 1];
        return sizeof(a) >> 1;
   }
};

剑指offer66.构建乘积数组

题目链接

题目:为了深入了解这些生物群体的生态特征,你们进行了大量的实地观察和数据采集。数组arrayA记录了各个生物群体数量数据,其中arrayA[i]表示第i 个生物群体的数量。请返回一个数组arrayB,该数组为基于数组arrayA中的数据计算得出的结果,其中arrayB[i]表示将第i个生物群体的数量从总体中排除后的其他数量的乘积。

思路:将B数组看成一个矩阵。排除第i个群体就相当于主对角线上全是1。然后我们能观察到,下三角矩阵部分B[i] = B[i - 1] * A[i - 1]。所以可以先计算下三角部分,将结果放在B数组中,然后计算上三角部分。上三角部分也是类似的计算方式,只不过要倒序遍历,并用一个tmp变量保存乘积。

通过代码:

cpp 复制代码
class Solution {
public:
    vector<int> statisticalResult(vector<int>& arrayA) {
        int n = arrayA.size();
        vector<int> arrayB(n, 1);
        for(int i = 1; i < n; i++)
            arrayB[i] = arrayB[i - 1] * arrayA[i - 1];

        int tmp = 1;
        for(int i = n - 1; i >= 0; i--)
        {
            arrayB[i] *= tmp;
            tmp *= arrayA[i];
        }
        return arrayB;
    }
};
相关推荐
Zer0_on1 小时前
C++string类
开发语言·c++
Lenyiin1 小时前
02.01、移除重复节点
c++·算法·leetcode
Lulsj4 小时前
代码随想录day22 | leetcode 39.组合总和 40.组合总和II 131.分割回文串
算法·leetcode
yvestine7 小时前
数据挖掘——支持向量机分类器
人工智能·算法·机器学习·支持向量机·分类·数据挖掘·svm
阿正的梦工坊7 小时前
PyTorch到C++再到 CUDA 的调用链(C++ ATen 层) :以torch._amp_update_scale_调用为例
c++·人工智能·pytorch
robin_suli7 小时前
穷举vs暴搜vs深搜vs回溯vs剪枝系列一>
算法·剪枝·深度优先遍历·回溯
魂兮-龙游8 小时前
C语言中的printf、sprintf、snprintf、vsnprintf 函数
c语言·开发语言·算法
陈序缘8 小时前
PyTorch快速入门
人工智能·pytorch·python·深度学习·算法·机器学习
KeyPan8 小时前
【视觉SLAM:四、相机与图像】
人工智能·深度学习·数码相机·算法·机器学习·计算机视觉
微凉的衣柜8 小时前
【C++/CMake】从静态库到动态库:一行改动解决 “找不到 -ljsoncpp” 链接报错
开发语言·c++