LeetsGoAgain

数组

数组理论知识

因为个人比较熟悉了,这里略过。

二分查找

https://leetcode.cn/problems/binary-search/description/

我写的

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size()-1;
        while(left <= right) {
            if (nums[left] == target) return left;
            if (nums[right] == target) return right;
            int mid = (left + right) / 2;
            if (nums[mid] < target){
                left = mid + 1;
                continue;
            } else if (nums[mid] > target) {
                right = mid - 1;
                continue;
            } else if(nums[mid] == target) return mid;
        }
        return -1;
    }
};

这道题问题不大,一定要注意边界条件的处理,防止死循环。这题其实很简单,但是已经可以看出,刷题其实已经没有大一的时候熟练了。

二分查找相关题目推荐:

// 一次过,问题不大
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while(left <= right) {
            if (nums[left] == target) return left;
            if (nums[right] == target) return right;
            int mid = (left + right) / 2;
            if(nums[mid] < target){ 
                left = mid + 1;
                continue;
            } else if(nums[mid] > target) {
                right = mid - 1;
                continue;
            } else return mid;
        }
        return left;
    }
};

这题有点意思,我想到的思路是,比如 [5, 7, 7, 8, 8, 10]。找8。 找到8是非常简单的,但是如何找到区间比较有意思。

比如,left = 7, right = 10, 此时 mid = 第一个8 那么可以在 left, mid 的区间找第一个8,mid, 10的区间找最后一个8。

但是这样我不确定是否是 logn。先用这个思路写,看看能不能过。(个人感觉不是logn)。

这个是我写的,全部通过。

class Solution {
public:
    std::vector<int> find_forward_and_backward(const vector<int>& nums, int target, int current_idx) {
        assert(nums[current_idx] == target);
        std::vector<int> res;
        int left_ret = current_idx;
        int right_ret = current_idx;
        while(1) {
            if(left_ret <= 0) break;
            if(nums[left_ret] == target && nums[left_ret - 1] != target) break;
            left_ret--;
        }
        while(1) {
            if(right_ret >= nums.size()-1) break;
            if(nums[right_ret] == target && nums[right_ret + 1] != target) break;
            right_ret++;
        }
        return {left_ret, right_ret};
    }
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if (nums.size() == 1) {
            if (nums[0] == target) return {0, 0};
            return {-1, -1};
        }
        int left = 0;
        int right = nums.size() - 1;
        while (left <= right) {
            if(nums[left] == target) {
                return find_forward_and_backward(nums, target, left);
            } if (nums[right] == target) {
                return find_forward_and_backward(nums, target, right);
            }
            int mid = (left + right) / 2;
            if(nums[mid] < target) {
                left = mid + 1;
                continue;
            } else if(nums[mid] > target) {
                right = mid - 1; 
                continue;
            } else if(nums[mid] == target) {
                return find_forward_and_backward(nums, target, mid);
            }
        }
        return {-1, -1};
    }
};

学习了一下 Carl 的做法,他通过两个函数,寻找左边界和寻找右边界去做的。她这个是完全是 logn

这里值得注意,Carl 的做法,是可以得到边界的。注意 if else 的设置。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // 情况二
        return {-1, -1};
    }
private:
     int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
};

这题我的思路是二分查找。不知道是不是准确的,先试试。 比如10,一开始就是1,10这个区间,然后二分是5,5的平方大于10,所以是1,5的区间,然后是2,5的区间 …

自己写的,也通过了。

class Solution {
public:
    int mySqrt(int x) {
        if (x == 1) return 1;
        if (x == 0) return 0;
        long long left = 1;
        long long right = x;
        while (left <= right) {
            if(left * left == x) return left;
            if(right * right == x) return right;
            long long mid = (left + right) / 2;
            if (mid * mid == x || (mid * mid < x && (mid+1)*(mid+1)>x)) return mid;
            else if(mid * mid < x) {
                left = mid;
                continue;
            } else if (mid * mid > x) {
                right = mid;
                continue;
            }
        }
        assert(false);
    }
};

这题carl没有给结果,看了下题解和我这个基本是一样的,问题不大。

这题和上面基本一致。一次通过。

class Solution {
public:
    bool isPerfectSquare(int x) {
        if (x == 1) return true;
        long long left = 1;
        long long right = x;
        while (left <= right) {
            if(left * left == x) return true;
            if(right * right == x) return true;
            long long mid = (left + right) / 2;
            if (mid * mid == x) return true;
            if (mid * mid < x && (mid+1)*(mid+1) > x) return false;
            else if(mid * mid < x) {
                left = mid;
                continue;
            } else if (mid * mid > x) {
                right = mid;
                continue;
            }
        }
        assert(false);     
    }
};

移除元素

https://leetcode.cn/problems/remove-element/description/

好的方法肯定是双指针,这个方法后续肯定是很常用的。 慢指针,指向后续东西填补过来的位置。快指针寻找填补过来的数字。 我自己先写一个版本。

我自己写的这个版本,用双指针,但是用了-1去记录数组的数字是否被移动过。感觉不是最优写法。不过也通过了,总体来说比较顺利,效率也是ok的。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if(nums.size() == 0) return 0;
        int slow = 0;
        while(slow < nums.size() && nums[slow] != val) slow++;
        int fast = slow;
        while(fast < nums.size()) {
            while(slow < nums.size() && nums[slow] != val && nums[slow] != -1) slow++;
            if(fast < slow) fast = slow;
            // 此时 slow 指向第一个需要删除的数字
            while(fast < nums.size() && nums[fast] == val) fast++;
            // 此时 fast 指向一个需要移动的数字
            if(fast >= nums.size() || slow >= nums.size()) break;
            nums[slow++] = nums[fast];
            nums[fast++] = -1; // 用 -1 记录这个位置可以被覆盖。
        }
        return slow;
    }
};

看看carl的写法。

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
    public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

用 [0, 1, 2, 2, 3, 0, 4, 2], val=2 去理解了一下这个写法,他可以重复赋值的,就不用做这么多判断了,确实更简洁。

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

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() == 1) return 1;
        int slow = 0;
        int fast = 1;
        while(fast < nums.size()) {
            while(1) {
                if(fast < nums.size() && nums[slow] == nums[fast]) fast++;
                else break;
            }
            if(fast >= nums.size()) break;
            // 此时 slow 和 fast 指向的数字不一样
            slow++;
            assert(slow < nums.size());
            nums[slow] = nums[fast++];
        }
        return ++slow;
    }
};

直接通过,没啥问题,在草稿纸上做过一般都没问题。

283. 移动零

这题个人感觉很简单,slow指针指向0,fast指向非0,交换即可。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        if (nums.size() == 1) return;
        int slow = 0;
        int fast = 0;
        while(fast < nums.size()) {
            while(slow < nums.size() && nums[slow] != 0) slow++;
            while(fast < nums.size() && nums[fast] == 0) fast++;
            if(slow > fast) {
                fast = slow;
                continue;
            }
            if(fast >= nums.size()) break;
            std::swap(nums[fast], nums[slow]);
            slow++;
        }
    }
};

问题不大,顺利通过

844. 比较含退格的字符串

这个题怎么感觉用栈做是最好的,先试试用栈。不过这样空间就不是O(1)了。

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        std::stack<int> st1;
        std::stack<int> st2;
        for (const auto& e : s) {
            if (e == '#' && st1.size() > 0) st1.pop();
            else if (e == '#' && st1.size() == 0) continue;
            else st1.push(e);
        }
        for(const auto& e : t) {
            if (e == '#' && st2.size() > 0) st2.pop();
            else if (e == '#' && st2.size() == 0) continue;
            else st2.push(e);
        }
        return st1 == st2;
    }
};

用栈没问题,速通。

试一下用双指针,空间用 O(1)

这题做的有点慢,不过最后也是做出来了。

class Solution {
private:
    int simplify(std::string& str) {
        // int current_size = str.size();
        int fast = 0;
        int slow = 0;
        for(int i = 0; i < str.size(); ++i) {
            if(str[i] == '#') {
                fast++;
                if(slow > 0) slow--;
            } else if(str[i] != '#') {
                slow++;
                fast++;
            }
            if(fast >= str.size()) break;
            str[slow] = str[fast];
        }
        return slow; // slow的大小表示字符串长短
    }
public:
    bool backspaceCompare(string s, string t) {
        // 使用双指针
        // fast指针为探索指针
        // slow指针为有效位置指针
        // fast指针负责探索前面路况,如果遇到#,则有效位置--,同时str的长度--
        // fast指针如果遇到有效字符,则str长度不变,如果此时fast和slow不在同一位置,则覆盖
        int ssize = simplify(s);
        std::cout << ssize << ":" << std::string(s.begin(), s.begin()+ssize) << std::endl;
        int tsize = simplify(t);
        std::cout << tsize << ":" << std::string(t.begin(), t.begin()+tsize) << std::endl;
        return (std::string(s.begin(), s.begin()+ssize) == std::string(t.begin(), t.begin()+tsize)) || (ssize == 0 && tsize == 0);
        // 最后这里重新构造string,表示切割后的字符串
    }
};

977. 有序数组的平方

这一题最好是用归并的思想。

之前的一类题:两个升序的序列,合成一个升序序列,双指针归并思想。题目没说空间要 O(1)。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        std::vector<int> ans;
        int right = 0;
        while(right < nums.size() && nums[right] < 0) right++;
        int left = right - 1;
        while(right < nums.size() && left >= 0) {
            size_t left_ans = nums[left] * nums[left];
            size_t right_ans = nums[right] * nums[right];
            if(left_ans < right_ans) {left--; ans.push_back(left_ans);}
            else {right++; ans.push_back(right_ans);}
        }
        while(left >= 0) ans.push_back(nums[left]*nums[left--]);
        while(right < nums.size()) ans.push_back(nums[right]*nums[right++]);
        return ans;
    }
};

其实思路就是归并的思路。但是过程不算太顺利,这也说明了,不熟练了。问题不大大。这个是 O(n) 的。

⻓度最⼩的⼦数组

https://leetcode.cn/problems/minimum-size-subarray-sum/

我能直接想到滑动窗口的双指针解法。区间和大了,就slow++,区间和小了就fast++。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 处理下特殊情况
        if (nums.size() == 1) {
            if(nums[0] >= target) return 1;
            else return 0;
        }
        int len = INT32_MAX; // 滑动窗口大小
        int slow = 0;
        int fast = 0;
        int current_sum = nums[0];
        while(fast < nums.size()) {
            if (current_sum >= target) {
                int cur_len = fast-slow+1;
                if(cur_len < len) len = cur_len;
                if(slow == fast) {slow++; fast++; current_sum = nums[fast];}
                else {current_sum-=nums[slow++];}
            } else if(current_sum < target) {
                fast++;
                if(fast >= nums.size()) break;
                current_sum += nums[fast];
            }
        }
        return len == INT32_MAX ? 0 : len;
    }
};

我的思路没问题,和carl的一样,顺利通过。

904. 水果成篮

这题题目有点难理解,我理解了很久。还是打算用滑动窗口去做即可。

这题真的有点难,先按照自己的思路写一下,写了很久,主要是指针有点难管理清楚。

class Solution {
private:
    bool in_pair(std::pair<int,int>& p, int x) {
        // if (x == p.first) return true;
        // if (x == p.second) {
        //     std::swap(p.first, p.second);
        //     return true;
        // }
        // return false;
        return p.second == x || p.first == x;
    }
public:
    int totalFruit(vector<int>& fruits) {
        // 这个题目太难理解了
        // 我理解了之后,翻译成人话:找一个最长的,只包含两种元素的子序列(即这个子序列set之后的size为2)
        // 我用一个 pair 来记录哪些类型是记录过的
        if(fruits.size() == 1) return 1;
        // 初始化
        int len = 2;
        int slow = 0;
        int fast = 0;
        // 初始化
        while(fast < fruits.size()) {
            std::pair<int, int> p;
            p.first = fruits[slow];
            p.second = -1;
            while(fast < fruits.size()) {
                if(in_pair(p, fruits[fast])) {
                    fast++;
                    continue;
                }
                else if(p.second == -1) {
                    p.second = fruits[fast];
                    fast++;
                    continue;
                }
                else if(in_pair(p, fruits[fast]) == false && p.first != -1 && p.second != -1) break;
            }
            // if(fast >= fruits.size()) break;
            int cur_len = fast - slow;
            // std::cout << cur_len << std::endl;
            if(cur_len > len) len = cur_len;
            // 下一轮迭代
            if(fast >= fruits.size()) break;
            assert(fruits[fast]!=fruits[fast-1]);
            int temp = fruits[fast-1];
            slow = fast-1;
            while(fruits[slow]==temp) slow--;
            slow++;
        }
        return len;
    }
};

不过最后也是艰难通过。

76. 最小覆盖子串

class Solution {
private:
    std::unordered_map<char, int> m;
    std::unordered_map<char, int> m_for_check;
private:
    void init_check_map(const std::string& t) {
        for(const auto& e : t) {
            m_for_check[e] += 1;
        }
    }
    bool is_valid(const std::string& t) {
        for (const auto& e : t)
            if (m[e] < m_for_check[e])
                return false;
        return true;
    }
    void print_m() {
        for (const auto& e : m)
            std::cout << e.first << ":" << e.second << std::endl;
    }
public:
    std::string minWindow(std::string s, std::string t) {
        // 处理边界条件
        if (s.size() == 1) {
            return s == t ? s : "";
        }
        // 开始处理
        init_check_map(t);
        int slow = 0;
        int fast = 0;
        int len = INT32_MAX;
        std::string ret_string;
        m[s[fast]] += 1;
        while (fast < s.size()) {
            bool res = is_valid(t);
            // std::cout << slow << ":" << fast << std::endl; print_m();
            // std::cout << res << std::endl; exit(1);
            if (res) {
                int cur_len = fast - slow + 1;
                if (cur_len < len) {
                    len = cur_len;
                    ret_string = std::string(s.begin() + slow, s.begin() + fast + 1);
                    // std::cout << "更新 ret: " << ret_string << std::endl; 
                }
                m[s[slow]] -= 1;
                slow++;
            } else {
                fast++;
                m[s[fast]] += 1;
            }
        }
        return ret_string;
    }
};

写了一个版本,但是没有通过,超出时间限制了,明天再看看吧。

题解:

class Solution {
public:
    unordered_map <char, int> ori, cnt;
    bool check() {
        for (const auto &p: ori) {
            if (cnt[p.first] < p.second) {
                return false;
            }
        }
        return true;
    }
    /* 我大概看明白了
        ori这个map是用来作对照的
        cnt这个map是用来记录t中字符出现的次数的
        这里和我的区别就是,我的cnt(m)记录所有s中的字符,其实不需要记录这么多,记录和t相关的即可了
        check()函数用来表示当前子串是否合法
     */
    string minWindow(string s, string t) {
        for (const auto &c: t) {
            ++ori[c]; // 这里先初始化ori
        }
        int l = 0, r = -1;
        int len = INT_MAX, ansL = -1, ansR = -1;

        while (r < int(s.size())) {
            if (ori.find(s[++r]) != ori.end()) {
                ++cnt[s[r]]; // 存在所需要的字符,则cnt
            }
            while (check() && l <= r) {
                if (r - l + 1 < len) {
                    len = r - l + 1; // 更新最新的len
                    ansL = l; // 记录下标而已,其实我用res_string也是一样的,都是可以的
                }
                if (ori.find(s[l]) != ori.end()) {
                    --cnt[s[l]]; // 因为l指针准备向右了,先看看这个指针指向的字符是不是t所需的,如果是,才需要控制cnt,否则直接++l即可了
                }
                ++l;
            }
        }

        return ansL == -1 ? string() : s.substr(ansL, len);
    }
};

我的思路是完全没问题的,个人感觉超时问题就是出在 cnt (m) 这个map里面,答案里cnt只管理有用字符,而我管理了全部字符。

螺旋矩阵II

https://leetcode.cn/problems/spiral-matrix-ii/description/

这道题⽬可以说在⾯试中出现频率较⾼的题⽬,本题并不涉及到什么算法,就是模拟过程,但却⼗分考察对代码的掌控能⼒。

这题我先按自己的思路试一下吧。

class Solution {
private:
    struct self_reference_pair {
        std::string first;
        self_reference_pair* second;
        bool operator==(const self_reference_pair& self) { return first == self.first; }
    }; //
private:
    bool is_location_valid(const std::vector<std::vector<bool>>& check_vec, const std::pair<int, int>& loc) {
        int limit = check_vec.size();
        if (loc.first >= 0 && loc.second >= 0 && loc.first < limit && loc.second < limit && check_vec[loc.first][loc.second] == false)
            return true;
        return false;
        // return check_vec[loc.first][loc.second] == false;
    } //
public:
    std::vector<std::vector<int>> generateMatrix(int n) {
        auto check_vec = std::vector<std::vector<bool>>(n, std::vector<bool>(n, false));
        auto res_vec = std::vector<std::vector<int>>(n, std::vector<int>(n, 0));
        // 构建一个mode的简易链表
        self_reference_pair down_mode, left_mode, up_mode; // 提前声明
        self_reference_pair right_mode = { "right_mode", &down_mode };
        down_mode = { "down_mode", &left_mode };
        left_mode = { "left_mode", &up_mode };
        up_mode = { "up_mode", &right_mode };
        // 开始循环
        int current_n = 1;
        self_reference_pair current_mode = right_mode; // 一开始向右
        std::pair<int, int> location = { 0, 0 };
        while (current_n <= n * n) {
            if (current_mode == right_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= n * n) {
                    // if (current_n == n * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    // std::cout << "is_valid: " << is_valid << std::endl;
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        // std::cout << location.first << ":" << location.second << std::endl;
                        location.first += 1;
                        location.second -= 1;
                        // std::cout << location.first << ":" << location.second << std::endl;
                        if (current_n == n * n) {
                            // std::cout << "call1" << std::endl;
                            res_vec[location.first][location.second] = current_n;
                            return res_vec;
                        }
                        break;
                    }
                    res_vec[location.first][location.second] = current_n;
                    check_vec[location.first][location.second] = true;
                    // if (current_n == n * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first;
                    location.second++;
                }
            } else if (current_mode == down_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= n * n) {
                    // if (current_n == n * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first -= 1;
                        location.second -= 1;
                        if (current_n == n * n) {
                            // std::cout << "call2" << std::endl;
                            res_vec[location.first][location.second] = current_n;
                            return res_vec;
                        }
                        break;
                    }
                    res_vec[location.first][location.second] = current_n;
                    check_vec[location.first][location.second] = true;
                    // if (current_n == n * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first++;
                    location.second;
                }
            } else if (current_mode == left_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                // std::cout << location.first << ":" << location.second << std::endl;
                while (current_n <= n * n) {
                    // if (current_n == n * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    // std::cout << "here" << std::endl;
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first -= 1;
                        location.second += 1;
                        if (current_n == n * n) {
                            // std::cout << "call3" << std::endl;
                            res_vec[location.first][location.second] = current_n;
                            return res_vec;
                        }
                        break;
                    }
                    res_vec[location.first][location.second] = current_n;
                    check_vec[location.first][location.second] = true;
                    // if (current_n == n * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first;
                    location.second--;
                }
            } else if (current_mode == up_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= n * n) {
                    // if (current_n == n * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first++;
                        location.second++;
                        if (current_n == n * n) {
                            // std::cout << "call4" << std::endl;
                            res_vec[location.first][location.second] = current_n;
                            return res_vec;
                        }
                        break;
                    }
                    res_vec[location.first][location.second] = current_n;
                    check_vec[location.first][location.second] = true;
                    // if (current_n == n * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first--;
                    location.second;
                }
            } else {
                assert(false);
            }
        }
        return res_vec;
    }
};

调试了一个下午终于通过了。

54. 螺旋矩阵 LCR 146. 螺旋遍历二维数组 这两个题是一样的,顺利通过了,和上面的题类似,大部分代码可以重复使用。

class Solution {
private:
    struct self_reference_pair {
        std::string first;
        self_reference_pair* second;
        bool operator==(const self_reference_pair& self) { return first == self.first; }
    }; //
private:
    bool is_location_valid(const std::vector<std::vector<bool>>& check_vec, const std::pair<int, int>& loc) {
        int limit = check_vec.size();
        int m = check_vec.size();
        int n = check_vec[0].size();
        if (loc.first >= 0 && loc.second >= 0 && loc.first < m && loc.second < n && check_vec[loc.first][loc.second] == false)
            return true;
        return false;
    } //
public:
    std::vector<int> spiralOrder(std::vector<std::vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        auto check_vec = std::vector<std::vector<bool>>(m, std::vector<bool>(n, false));
        auto res_vec = std::vector<int>(); // 初始化
        // 构建一个mode的简易链表
        self_reference_pair down_mode, left_mode, up_mode; // 提前声明
        self_reference_pair right_mode = { "right_mode", &down_mode };
        down_mode = { "down_mode", &left_mode };
        left_mode = { "left_mode", &up_mode };
        up_mode = { "up_mode", &right_mode };
        // 开始循环
        int current_n = 1;
        self_reference_pair current_mode = right_mode; // 一开始向右
        std::pair<int, int> location = { 0, 0 };
        while (current_n <= m * n) {
            if (current_mode == right_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= m * n) {
                    // if (current_n == m * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    // std::cout << "is_valid: " << is_valid << std::endl;
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        // std::cout << location.first << ":" << location.second << std::endl;
                        location.first += 1;
                        location.second -= 1;
                        // std::cout << location.first << ":" << location.second << std::endl;
                        if (current_n == m * n) {
                            // std::cout << "call1" << std::endl;
                            res_vec.push_back(matrix[location.first][location.second]);
                            return res_vec;
                        }
                        break;
                    }
                    // res_vec[location.first][location.second] = current_n;
                    res_vec.push_back(matrix[location.first][location.second]);
                    check_vec[location.first][location.second] = true;
                    // if (current_n == m * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first;
                    location.second++;
                }
            } else if (current_mode == down_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= m * n) {
                    // if (current_n == m * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first -= 1;
                        location.second -= 1;
                        if (current_n == m * n) {
                            // std::cout << "call2" << std::endl;
                            res_vec.push_back(matrix[location.first][location.second]);
                            return res_vec;
                        }
                        break;
                    }
                    res_vec.push_back(matrix[location.first][location.second]);
                    check_vec[location.first][location.second] = true;
                    // if (current_n == m * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first++;
                    location.second;
                }
            } else if (current_mode == left_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                // std::cout << location.first << ":" << location.second << std::endl;
                while (current_n <= m * n) {
                    // if (current_n == m * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    // std::cout << "here" << std::endl;
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first -= 1;
                        location.second += 1;
                        if (current_n == m * n) {
                            // std::cout << "call3" << std::endl;
                            res_vec.push_back(matrix[location.first][location.second]);
                            return res_vec;
                        }
                        break;
                    }
                    res_vec.push_back(matrix[location.first][location.second]);
                    check_vec[location.first][location.second] = true;
                    // if (current_n == m * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first;
                    location.second--;
                }
            } else if (current_mode == up_mode) {
                // std::cout << "mode: " << current_mode.first << std::endl;
                while (current_n <= m * n) {
                    // if (current_n == m * n) {
                    //     res_vec[location.first][location.second] = current_n;
                    //     return res_vec;
                    // }
                    bool is_valid = is_location_valid(check_vec, location);
                    if (is_valid == false) {
                        // 需要转变mode
                        current_mode = *(current_mode.second); // 没有填写数字,所以 current_n 不需要 ++
                        location.first++;
                        location.second++;
                        if (current_n == m * n) {
                            // std::cout << "call4" << std::endl;
                            res_vec.push_back(matrix[location.first][location.second]);
                            return res_vec;
                        }
                        break;
                    }
                    res_vec.push_back(matrix[location.first][location.second]);
                    check_vec[location.first][location.second] = true;
                    // if (current_n == m * n)
                    //     return res_vec;
                    current_n += 1;
                    location.first--;
                    location.second;
                }
            } else {
                assert(false);
            }
        }
        return res_vec;
    }
};

区间和 (前缀和思想)

本题为代码随想录后续扩充题目,卡哥还没有视频讲解,顺便练习一下ACM输入输出模式(笔试面试必备)

https://kamacoder.com/problempage.php?pid=1070

输入描述:

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。

输出描述:

输出每个指定区间内元素的总和。

输入示例:

5
1
2
3
4
5
0 1
1 3

输出示例

3
9

数据范围:

0 < n <= 100000

思路:这里引入一个非常重要的算法,前缀和。

如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。

所以前缀和是很重要的。

#include <iostream>
#include <vector>
#include <string>
#include <assert.h>

void test() {
    int n = 0;
    std::cin >> n; // 数组的大小
    assert(n != 0);
    std::vector<int> nums;
    std::vector<int> prefix;
    for (int i = 0; i < n; ++i) {
        int x = 0;
        std::cin >> x; // 输入的数字
        nums.push_back(x);
        if(i == 0) {
            prefix.push_back(x);
        } else {
            prefix.push_back(x + prefix[i-1]);
        }
    }
    int l, r;
    while(std::cin >> l >> r) {
        int sum;
        if(l == 0) sum = prefix[r];
        else sum = prefix[r] - prefix[l - 1];
        std::cout << sum << std::endl;
    }
}

int main() {
    test();
    return 0;
}

开发商购买土地 (前缀和)

https://kamacoder.com/problempage.php?pid=1044

【题目描述】

在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。

为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

【输入描述】

第一行输入两个正整数,代表 n 和 m。

接下来的 n 行,每行输出 m 个正整数。

输出描述

请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

【输入示例】

3 3 1 2 3 2 1 3 1 2 3

【输出示例】

0

【提示信息】

如果将区域按照如下方式划分:

1 2 3 2 1 3 1 2 3

两个子区域内土地总价值之间的最小差距可以达到 0。

【数据范围】:

1 <= n, m <= 100; n 和 m 不同时为 1。

思路:前缀和

其实就是只能横着切一刀,数着切一刀就行。

这里有需要注意的地方,见代码的注释。



#include <vector>
#include <iostream>
#include <string>
#include <climits>
#include <algorithm>
#include <math.h>


void test() {
    // 初始化表
    int n, m;
    std::cin >> n >> m;
    int sum = 0;
    auto vec = std::vector<std::vector<int>>(n, std::vector<int>(m, 0));
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) {
            std::cin >> vec[i][j];
            sum += vec[i][j];
        }
    }
    // 统计横向
    auto row = std::vector<int>(n, 0);
    for (int i = 0; i < m; ++i)
        for(int j = 0; j < n; ++j)
            row[i] += vec[i][j];
    // 统计纵向
    auto col = std::vector<int>(m, 0);
    for (int j = 0; j < m; ++j)
        for (int i = 0; i < n; ++i)
            col[j] += vec[i][j];
    // sum ready
    int result = INT_MAX;
    int row_cut = 0;
    for(int i = 0; i < n; ++i) {
        // 遍历i种横向切割的方法
        row_cut += row[i];
        // tips:
        // 这里的理解卡了一下,
        // 当 i = 0 的时候,就是把第一行分出去,第一行的总和就是 row_cut
        // 所以后面几行的总和是 sum - row_cut
        // 所以差值是 sum - row_cut - row_cut
        // row[0] 是第一行的和
        // row[1] 是第二行的和,不是第一行和第二行的总和,所以 row, col 不是 prefix 数组,要注意
        // 因此这里是 row_cut += row[i] 而不是 row_cut = row[i]
        result = std::min(result, abs(sum - row_cut - row_cut));
    }
    int col_cut = 0;
    for(int j = 0; j < m; ++j) {
        col_cut += col[j];
        result = std::min(result, abs(sum - col_cut - col_cut));
    }
    std::cout << result << std::endl;
}

int main() {
    test();
    return 0;
}

总结

数组到这里就结束了。

https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html