面试150——第三周

目录

生命游戏

赎金信

同构字符串

负二进制转换

单词规律

有效的字母异位词

字母异位词分组

[两数之和 梦的开始](#两数之和 梦的开始)

快乐数

最长连续序列

汇总区间

合并区间

插入区间

有效的括号

简化路径


​​​​​​​生命游戏

思路1:开一个相同的二维数组储存答案

cpp 复制代码
class Solution
{
public:
    void gameOfLife(vector<vector<int>> &board)
    {
        vector<vector<int>> ret = board;
        int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
        int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
        int n = board.size(), m = board[0].size();
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                int count = 0;
                for (int k = 0; k < 8; k++)
                {
                    int x = i + dx[k], y = j + dy[k];
                    if (x >= 0 && x < n && y >= 0 && y < m)
                    {
                        if (board[x][y] == 1)
                            count++;
                    }
                }
                if ((board[i][j] == 1 && (count == 2 || count == 3)) || (board[i][j] == 0 && count == 3))
                    ret[i][j] = 1;
                else
                    ret[i][j] = 0;
            }
        }
        board = ret;
    }
};

思路2:使用(从右往左)第二个 bit 进行标记,就不用额外开辟空间

cpp 复制代码
class Solution
{
public:
    void gameOfLife(vector<vector<int>> &board)
    {
        int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
        int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
        int n = board.size(), m = board[0].size();
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                int count = 0;
                for (int k = 0; k < 8; k++)
                {
                    int x = i + dx[k], y = j + dy[k];
                    if (x >= 0 && x < n && y >= 0 && y < m)
                    {
                        // if((board[x][y]&1)==1) count++;
                        count += board[x][y] & 1;
                    }
                }
                if (((board[i][j] & 1) == 1 && (count == 2 || count == 3)) || ((board[i][j] & 1) == 0 && count == 3))
                {
                    board[i][j] |= 2;
                }
            }
        }
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < m; j++)
            {
                board[i][j] >>= 1;
                // if((board[i][j]&2)==2) board[i][j]=1;
                // else board[i][j]=0;
            }
        }
    }
};

赎金信

解法:哈希

统计 ransomNote 中的每个种类字符个数是否大于 magazine

cpp 复制代码
class Solution
{
public:
    bool canConstruct(string ransomNote, string magazine)
    {
        int hashr[26] = {0}, hashm[26] = {0};
        for (auto &ch : ransomNote)
            hashr[ch - 'a']++;
        for (auto &ch : magazine)
            hashm[ch - 'a']++;
        for (int i = 0; i < 26; i++)
        {
            if (hashr[i] > hashm[i])
                return false;
        }
        return true;
    }
};

同构字符串

思路1:各自映射

当以 s 为参照物时,看看 t 中的字符映射到 s字符时是否是 1对1 的关系;反过来(s 映射到 t )也是如此,两中情况都成立才说明是同字符串

cpp 复制代码
class Solution {
public:
    bool IsMapping(const string& s1, const string& s2)
    {
        unordered_map<char,char> hash;
        for(int i=0;i<s1.size();i++)
        {
            char ch1 = s1[i], ch2 = s2[i];
            if(!hash.contains(ch1))
            {
                // 建立映射
                hash[ch1] = ch2;
            }
            else
            {
                // 是不是之前建立的映射
                if(hash[ch1] != ch2)
                    return false;
            }
        }
        return true;
    }
    bool isIsomorphic(string s, string t) {
        // 映射对应关系都互换
        return IsMapping(s,t) && IsMapping(t,s);
    }
};

思路2:翻译

把字符串的映射情况统一"翻译"成数字,使用 count 为1进行记录,比如:abb 和 egg:a -> e 第一次出来,该位置为1,count++...最后 b -> g 出现过了所有还是2;翻译结果 122 和 122,比较翻译结果是否一致即可

注:收集结果要以一个特殊字符比如" "分隔开,不然即可出现

"abcdefghijkaa" -> 123456789101111

"abcdefghijaak" -> 123456789101111

cpp 复制代码
class Solution
{
public:
    string IsMapping(const string &s)
    {
        unordered_map<char, int> hash;
        string ret;
        int count = 1;
        for (auto &ch : s)
        {
            if (!hash.contains(ch))
            {
                hash[ch] = count;
                count++;
            }
            // 可能第一个和第十一个字符映射重复
            // "abcdefghijkaa" "abcdefghijaak"
            ret += to_string(hash[ch]) + " ";
        }
        return ret;
    }
    bool isIsomorphic(string s, string t)
    {
        // 翻译出来是否相等
        cout << IsMapping(s) << ' ' << IsMapping(t) << endl;
        return IsMapping(s) == IsMapping(t);
    }
};

优化:使用两个哈希表来优化,遍历一遍,如何字符串保存的结果不同直接返回 false

cpp 复制代码
class Solution
{
public:
    bool isIsomorphic(string s, string t)
    {
        unordered_map<char, int> hashs, hasht;
        for (int i = 0; i < s.size(); i++)
        {
            char s1 = s[i], t1 = t[i];
            // 使用下标来记录
            if (!hashs.contains(s1))
                hashs[s1] = i + 1;
            if (!hasht.contains(t1))
                hasht[t1] = i + 1;
            if (hashs[s1] != hasht[t1])
            {
                return false;
            }
        }
        return true;
    }
};

负二进制转换

解法:模拟

二进制:使用 %2 除2的思路,对于负二进制,我们同样可以用这个做法。但是由于是负数,我们在取余的时候,可能会出现负数。但是二进制是没有负数的,因此我们要将余数为负数修正为余数为正数,即 -1 修正为 1,对应的商就应该增加 1进行抵消

cpp 复制代码
class Solution
{
public:
    string baseNeg2(int n)
    {
        if (n == 0)
            return "0";
        string ret;
        int mod = 0;
        while (n)
        {
            mod = n % (-2);
            n /= (-2);
            // 取模为-1时特殊判断
            if (mod == -1)
            {
                mod += 2;
                n++;
            }
            // 倒着存
            ret = to_string(mod) + ret;
        }
        return ret;
    }
};

十进制转任意进制

cpp 复制代码
class Solution
{
public:
    string decToBit(int n, int bit)
    {
        string s = to_string(n), ret;
        while (s != "0")
        {
            string tmp;
            // 字符串除法
            int mod = 0, sum = 0;
            for (int i = 0; i < s.size(); i++)
            {
                sum = mod * 10 + (s[i] - '0');
                tmp += to_string(sum / bit);
                mod = sum % bit;
            }
            // 每次取低位,如果是大于10进制的要使用哈希表对应字符,比如16进制的10->'A'
            ret = to_string(mod) + ret;
            // 去前导0,比如: 145 -> 072 -> 72
            while (tmp.size() > 1 && tmp.front() == '0')
                tmp.erase(tmp.begin());
            s = tmp;
        }
        return ret;
    }
};

单词规律

上题的加强版,处理字符变成字符串即可,使用思路2的翻译法来完成

cpp 复制代码
class Solution
{
public:
    bool wordPattern(string pattern, string s)
    {
        unordered_map<string, int> hashs;
        stringstream ss(s);
        string tmp, ret1;
        int count = 1;
        while (ss >> tmp)
        {
            if (!hashs.contains(tmp))
            {
                hashs[tmp] = count;
                count++;
            }
            ret1 += to_string(hashs[tmp]);
        }
        unordered_map<char, int> hashp;
        string ret2;
        count = 1;
        for (auto &ch : pattern)
        {
            if (!hashp.contains(ch))
            {
                hashp[ch] = count;
                count++;
            }
            ret2 += to_string(hashp[ch]);
        }
        return ret1 == ret2;
    }
};

有效的字母异位词

解法:哈希

统计字符个数是否相等

cpp 复制代码
class Solution
{
public:
    bool isAnagram(string s, string t)
    {
        int hash1[26] = {0}, hash2[26] = {0};
        for (auto &ch : s)
            hash1[ch - 'a']++;
        for (auto &ch : t)
            hash2[ch - 'a']++;
        for (int i = 0; i < 26; i++)
        {
            if (hash1[i] != hash2[i])
                return false;
        }
        return true;
    }
};

字母异位词分组

解法:哈希表

用哈希表分组,把排序后的字符串当作哈希表的 key,排序前的字符串加到对应的列表中(哈希表的 value)

cpp 复制代码
class Solution
{
public:
    vector<vector<string>> groupAnagrams(vector<string> &strs)
    {
        unordered_map<string, vector<string>> hash;
        for (auto &str : strs)
        {
            string tmp = str;
            sort(str.begin(), str.end());
            hash[str].push_back(tmp);
        }
        vector<vector<string>> ret;
        for (auto &[a, b] : hash)
            ret.push_back(b);
        return ret;
    }
};

两数之和 梦的开始

cpp 复制代码
class Solution
{
public:
    vector<int> twoSum(vector<int> &nums, int target)
    {
        unordered_map<int, int> hash;
        vector<int> ret(2);
        for (int i = 0; i < nums.size(); i++)
        {
            if (hash.contains(target - nums[i]))
            {
                ret[0] = i, ret[1] = hash[target - nums[i]];
            }
            hash.insert({nums[i], i});
        }
        return ret;
    }
};

快乐数

解法1:哈希表

使用哈希表储存每一个计算结果

为1就是快乐数

计算结果不为1并且与前面的计算结果重复了就返回false

cpp 复制代码
class Solution
{
public:
    bool isHappy(int n)
    {
        unordered_set<int> hash;
        while (true)
        {
            int tmp = n, sum = 0;
            while (tmp)
            {
                sum += pow(tmp % 10, 2);
                tmp /= 10;
            }
            cout << sum << endl;
            if (sum == 1)
                return true;
            if (hash.contains(sum))
                break;
            hash.insert(sum);
            n = sum;
        }
        return false;
    }
};

解法2:快慢指针

无论是快乐数还是不是,在某个时间段都能相等,在相等时判断是不是等于1即可,此时就不用开空间储存计算结果了

cpp 复制代码
class Solution
{
public:
    int happyVal(int n)
    {
        int sum = 0;
        while (n)
        {
            sum += pow(n % 10, 2);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n)
    {
        int slow = n, fast = n;
        while (true)
        {
            slow = happyVal(slow);
            fast = happyVal(fast);
            fast = happyVal(fast);
            if (slow == fast)
            {
                if (slow == 1)
                    return true;
                else
                    break;
            }
        }
        return false;
    }
};

最长连续序列

解法:哈希

题目要求时间复杂度 O(N),先使用哈希表进hash行储存

主要思路:对每个数x进行遍历,看看x+1,x+2...有多少个,取最大值

优化1:不使用 nums 进行遍历,而使用 hash 进行遍历,因为里面是去重后的结果

优化2:遇到 x-1 在 hash 了,直接进行下一个数的遍历

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> hash;
        for(auto& num:nums) hash.insert(num);
        int ret = 0;
        // 哈希表进行遍历
        for(auto& x:hash)
        {
            // 前面遍历过了
            if(hash.contains(x-1)) continue;
            int y = x + 1;
            while(hash.contains(y))
                y++;
            ret = max(ret, y - x);
        }
        return ret;
    }
};

汇总区间

解法:模拟

两次简单循环找连续自然数区间

cpp 复制代码
class Solution
{
public:
    vector<string> summaryRanges(vector<int> &nums)
    {
        vector<string> ret;
        int n = nums.size();
        for (int i = 0; i < n;)
        {
            string s = to_string(nums[i]);
            int j = i;
            while (j + 1 < n && (long long)nums[j + 1] - nums[j] == 1)
                j++;
            if (nums[i] != nums[j])
                s += "->" + to_string(nums[j]);
            ret.push_back(s);
            i = j + 1;
        }
        return ret;
    }
};

合并区间

解法:模拟

先按照左端点(第一个数)排序,保障接下来模拟区间不会断开;

每次按照第i组数的第二个数与第i+1组数的第一个数进行比较,大于等于往下找,因为不知道谁比较大,往下找之前要先取二者最大值

cpp 复制代码
class Solution
{
public:
    vector<vector<int>> merge(vector<vector<int>> &intervals)
    {
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> ret;
        int n = intervals.size();
        for (int i = 0; i < n;)
        {
            vector<int> v(2);
            int j = i, max_val = intervals[j][1];
            while (j + 1 < n && max_val >= intervals[j + 1][0])
            {
                max_val = max(max_val, intervals[j + 1][1]);
                j++;
            }
            v[0] = intervals[i][0], v[1] = max_val;
            ret.push_back(v);
            i = j + 1;
        }
        return ret;
    }
};

插入区间

分为不重叠和重叠的情况进行判断

cpp 复制代码
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        if(intervals.empty()) return {newInterval};
        vector<vector<int>> ret;
        int n=intervals.size(),i=0;
        while(i<n && intervals[i][1] < newInterval[0]) ret.push_back(intervals[i++]);
        // {[1,2]} [3,4] 的话如果 min_val=INT_MAX max_val=INT_MIN 就错了
        int j=i,min_val=newInterval[0],max_val=newInterval[1];
        while(i<n && intervals[i][0] <= newInterval[1]) 
        {

            min_val = min(min_val,min(intervals[i][0],newInterval[0]));
            max_val = max(max_val,max(intervals[i][1],newInterval[1]));
            i++;
        }
        ret.push_back({min_val,max_val});
        for(int k=i;k<n;k++) ret.push_back(intervals[k]);
        return ret;
    }
};

有效的括号

解法:栈

使用栈进行模拟,分情况进行判断

cpp 复制代码
class Solution
{
public:
    bool isValid(string s)
    {
        stack<char> st;
        for (auto &ch : s)
        {
            if (st.empty() || ch == '(' || ch == '[' || ch == '{')
                st.push(ch);
            else if (st.size() > 0)
            {
                if (ch == ')' && st.top() == '(')
                    st.pop();
                else if (ch == ']' && st.top() == '[')
                    st.pop();
                else if (ch == '}' && st.top() == '{')
                    st.pop();
                else
                    return false;
            }
            else
                return false;
        }
        return st.size() == 0;
    }
};

简化路径

解法:栈模拟

先把 path 用 / 分割,得到一个字符串列表

遍历字符串列表的同时,用栈维护遍历过的字符串:

  • 如果当前字符串是空串或者 .,什么也不做(跳过)。
  • 如果当前字符串不是 ..,那么把字符串入栈。
  • 否则弹出栈顶字符串(前提是栈不为空),模拟返回上一级目录。

使用 "/" 将字符串列表的字符串进行拼接

cpp 复制代码
class Solution
{
public:
    string simplifyPath(string path)
    {
        auto split_view = path | std::views::split('/');
        // C++23 直接转换
        auto dirs = std::ranges::to<std::vector<std::string>>(split_view);
        stack<string> st;
        for (auto &dir : dirs)
        {
            if (dir == "" || dir == ".")
                continue;
            else if (dir != "..")
                st.push(dir);
            else
            {
                if (!st.empty())
                    st.pop();
            }
        }
        string ret;
        while (!st.empty())
        {
            ret = st.top() + '/' + ret;
            st.pop();
        }
        while (ret.size() && ret.back() == '/')
            ret.pop_back();
        ret = '/' + ret;
        return ret;
    }
};

class Solution
{
public:
    string simplifyPath(string path)
    {
        stringstream ss(path);
        string tmp;
        vector<string> dirs;
        while (getline(ss, tmp, '/'))
        {
            if (tmp == "" || tmp == ".")
                continue;
            else if (tmp != "..")
                dirs.push_back(tmp);
            else
            {
                if (dirs.size())
                    dirs.pop_back();
            }
        }
        string ret;
        for (auto &dir : dirs)
            ret = ret + '/' + dir;
        return ret.empty() ? "/" : ret;
    }
};

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

相关推荐
一车小面包15 小时前
Neo4j中的APOC
算法·neo4j
H_BB15 小时前
前缀和算法详解
数据结构·算法
聆风吟º15 小时前
【数据结构手札】时间复杂度详解:概念 | 大O渐进表示法 | 习题
数据结构·算法·时间复杂度·大o渐进表示法
zs宝来了15 小时前
Spring Boot启动流程源码深度解析:电商订单系统面试实战
java·spring boot·面试·源码分析·电商
有意义16 小时前
从重复计算到无效渲染:用对 useMemo 和 useCallback 提升 React 性能
react.js·面试·前端框架
山楂树の16 小时前
买卖股票的最佳时机(动态规划)
算法·动态规划
UrbanJazzerati17 小时前
掌握SOQL For Loops:高效处理大量Salesforce数据的艺术
后端·面试
小O的算法实验室17 小时前
2024年IEEE TMC SCI1区TOP,面向无人机辅助 MEC 系统的轨迹规划与任务卸载的双蚁群算法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进
无才顽石17 小时前
什么是数学
算法·数理象