Bootstrap

前端面试必修--面试算法题(附带字节跳动真题pdf)

面试算法题

目录

简单

53. 最大子数组和 - 力扣(LeetCode)

动态规划思想

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxSum = nums[0];  // 初始化最大子数组和为数组的第一个元素
        int currentSum = nums[0];  // 初始化当前子数组和为数组的第一个元素

        for (int i = 1; i < nums.size(); i++) {
            // 如果currentSum加上当前元素比当前元素还小,就重新开始找新的子数组
            currentSum = max(currentSum + nums[i], nums[i]);
            // 更新全局最大子数组和
            maxSum = max(maxSum, currentSum);
        }

        return maxSum;
    }
};

415. 字符串相加 - 力扣(LeetCode)

class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.size() - 1;
        int j = num2.size() - 1;
        int carry = 0;
        string result = "";

        while (i >= 0 || j >= 0 || carry) {
            int sum = carry;
            if (i >= 0) {
                sum += num1[i] - '0'; // 将字符转换为整数
                i--;
            }
            if (j >= 0) {
                sum += num2[j] - '0'; // 将字符转换为整数
                j--;
            }
            result = char(sum % 10 + '0') + result; // 计算当前位的结果并添加到结果字符串前
            carry = sum / 10; // 计算进位
        }

        return result;
    }
};

206. 反转链表 - 力扣(LeetCode)

双指针解法

https://leetcode.cn/problems/reverse-linked-list/solutions/99711/fan-zhuan-lian-biao-shuang-zhi-zhen-di-gui-yao-mo-

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* cur = NULL, *pre = head;
        while (pre != NULL) {
            ListNode* t = pre->next;
            pre->next = cur;
            cur = pre;
            pre = t;
        }
        return cur;
    }
};

1. 两数之和 - 力扣(LeetCode)

双指针进行维护

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int idx = nums.size();
        vector<pair<int, int>> v;
        for (int i = 0; i < idx; i++) { // 修正为包含全部元素
            v.push_back({nums[i], i});
        }
        sort(v.begin(), v.end());

        int i = 0, j = idx - 1; // 初始化 j
        while (i < j) { // 确保 i 和 j 不相遇
            int sum = v[i].first + v[j].first;
            if (sum == target) {
                return {v[i].second, v[j].second}; // 找到合适的对,返回其索引
            } else if (sum > target) {
                j--;
            } else {
                i++;
            }
        }
        return {}; // 如果没有找到,返回空数组
    }
};

572. 另一棵树的子树 - 力扣(LeetCode)

 //isSubtree()函数用与不断向下搜索(左下和右下)
 //judge()函数用于在搜索的时候进行判断
class Solution {
public:
    bool judge(TreeNode* root, TreeNode* subRoot)
    {
        if(subRoot==NULL&&root==NULL) return true;
        if(!root&&subRoot||root&&!subRoot||root->val!=subRoot->val) return false;
        return judge(root->left,subRoot->left)&&judge(root->right,subRoot->right);
    }
    bool isSubtree(TreeNode* root, TreeNode* subRoot) {
        if(root==NULL) return false;//只对root进行扩展,所以只判断root
        return judge(root,subRoot) || isSubtree(root->left,subRoot) ||isSubtree(root->right,subRoot);
    }
};

1410. HTML 实体解析器 - 力扣(LeetCode)

class Solution {
public:
    string entityParser(string text) {
        int l1=0, l2=0, l3=0, l4=0, l5=0, l6=0;
        // Replace &quot; with "
        while (text.find("&quot;", l1) != string::npos) {
            int idx = text.find("&quot;", l1);
            text.replace(idx, 6, "\"");
            l1 = idx + 1;
        }
        // Replace &apos; with '
        while (text.find("&apos;", l2) != string::npos) {
            int idx = text.find("&apos;", l2);
            text.replace(idx, 6, "'");
            l2 = idx + 1;
        }
        // Replace &gt; with >
        while (text.find("&gt;", l4) != string::npos) {
            int idx = text.find("&gt;", l4);
            text.replace(idx, 4, ">");
            l4 = idx + 1;
        }
        // Replace &lt; with <
        while (text.find("&lt;", l5) != string::npos) {
            int idx = text.find("&lt;", l5);
            text.replace(idx, 4, "<");
            l5 = idx + 1;
        }
        // Replace &frasl; with /
        while (text.find("&frasl;", l6) != string::npos) {
            int idx = text.find("&frasl;", l6);
            text.replace(idx, 7, "/");
            l6 = idx + 1;
        }
        // Finally, replace &amp; with & to avoid interference with other entities
        while (text.find("&amp;", l3) != string::npos) {
            int idx = text.find("&amp;", l3);
            text.replace(idx, 5, "&");
            l3 = idx + 1;
        }
        return text;
    }
};

69. x 的平方根 - 力扣(LeetCode)

二分秒了

class Solution {
public:
    bool check(long long  mid,long long x)
    {
        if(mid*mid<=x) return true;
        return false;
    }
    long long search(long long l,long long r,long long x)
    {
        while(l<r)
        {
            long long mid=(l+r+1)>>1;
            if(check(mid,x)) l=mid;
            else r=mid-1;
        }
        return l;
    }
    long long mySqrt(long long x) {
       return search(0,x,x);
    }
};

26. 删除有序数组中的重复项 - 力扣(LeetCode)

非常基础的一道题

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        unordered_map<int,int> mp;
        vector<int> v;
        for(auto &ele : nums)
        {
            if(mp[ele]) continue;
            else 
            {
                mp[ele]++;
                v.push_back(ele);
            }
        }
        nums=v;
        return v.size();
    }
};

141. 环形链表 - 力扣(LeetCode)

非常简单的map映射就可以解决

class Solution {
public:
    bool hasCycle(ListNode *head) {
        unordered_map<ListNode*,int> mp;
        while(head!=NULL)
        {
            mp[head]++;
            if(mp[head]>1) return true;
            head=head->next;
        }
        return false;
    }
};

LCR 140. 训练计划 II - 力扣(LeetCode)

class Solution {
public:
    ListNode* trainingPlan(ListNode* head, int cnt) {
        ListNode* p=head;
        if(p->next==NULL) return p;
        vector<int> v;
        while(p!=NULL)
        {
            v.push_back(p->val);
            p=p->next;
        }

        ListNode* l=new ListNode(v[v.size()-cnt]);
        ListNode* current=l;//目的是使l一直为头节点
        for(int i=v.size()-cnt+1;i<v.size();i++)
        {
            current->next=new ListNode(v[i]);
            current=current->next;
        }
        return l;
    }
};

338. 比特位计数 - 力扣(LeetCode)

class Solution {
public:
    int lowbit(int x)
    {
            return x & -x;
    }
    vector<int> countBits(int n) {
        vector<int> v;
        for(int i=0;i<=n;i++)
        {
            int temp=i;
            int res=0;
            while(temp)
            {
                temp-=lowbit(temp);
                res++;
            }
            v.push_back(res);
        }
        return v;
    }
};

101. 对称二叉树 - 力扣(LeetCode)

很简单的一个递归用法,虽然我没想到

class Solution {
public:
    bool check(TreeNode* l,TreeNode* r)
    {
        if(!l&&!r) return true;
        if(!l||!r) return false;
        return l->val==r->val && check(l->left,r->right) && check(l->right,r->left);
    }
    bool isSymmetric(TreeNode* root) {
        return check(root,root);
    }
};

110. 平衡二叉树 - 力扣(LeetCode)

平衡二叉树也叫AVL树,它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和左子树的高度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

涉及到求解二叉树深度

int depth(TreeNode* node)
    {
        if(!node) return true;
        return max(depth(node->left),depth(node->right))+1;
    }

下面为递归代码,不过我觉得这个代码有点冗余了,每个子节点都要判断一遍

class Solution {
public:
    int depth(TreeNode* node)
    {
        if(!node) return true;
        return max(depth(node->left),depth(node->right))+1;
    }
    bool isBalanced(TreeNode* root) {
        if(!root) return true;
        int d=abs(depth(root->left)-depth(root->right));
        return (d<=1) && (isBalanced(root->left)) && (isBalanced(root->right));
    }
};

155. 最小栈 - 力扣(LeetCode)

class MinStack {
public:
    stack<long long> st;
    MinStack() {
    }
    
    void push(int val) {
        st.push(val);
    }
    
    void pop() {
        st.pop();
    }
    
    int top() {
        return st.top(); 
    }
    
    int getMin() {
        long long mn=1e10;
        stack<long long> ts;
        ts=st;
        while(!ts.empty())
        {
            long long tp=ts.top();
            mn=min(mn,tp);
            ts.pop();
        }
        return mn;
    }
};

27. 移除元素 - 力扣(LeetCode)

增序放置不等于val的元素,k即作为增量下标又作为数量统计

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int k = 0;
        for (int i = 0; i < nums.size(); i++) {
           if(nums[i]!=val)
           {
                nums[k]=nums[i];
                k++;
           }
        }
        return k;
    }
};

中等

3. 无重复字符的最长子串 - 力扣(LeetCode)

本来想用双指针的,结果双指针都不需要,挺简单的

class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        if(!s.size()) return 0;
        unordered_map<char,int> mp;
        unordered_map<char,int> idx;
        int ans=0;
        int mx=-1e7;
        for(int i=0;i<s.size();i++)
        {
            mp[s[i]]++;
            if(mp[s[i]]==1)
            {
                idx[s[i]]=i;
                ans++;
                mx=max(mx,ans);
            }
            else 
            {
                i=idx[s[i]];
                ans=0;
                unordered_map<char,int>().swap(mp);
                unordered_map<char,int>().swap(idx);
            }
        }
        return mx;
    }
};

146. LRU 缓存 - 力扣(LeetCode)

很不错的一道题,让我学会了以下用法:

unordered_map<int,list<pair<int,int>>::iterator> cache
item.splice(item.begin(),item,cache[key]);//(移动位置,容器,移动的元素)
item.emplace_front(key,value);//链表头插

class LRUCache {
private:
     int cap;
    list<pair<int,int>> item;
    unordered_map<int,list<pair<int,int>>::iterator> cache;//cache始终指向list尾部 
public:
    LRUCache(int capacity) {
        cap=capacity;
    }
    
    int get(int key) {
        if(cache.find(key)==cache.end()) return -1;
        item.splice(item.begin(),item,cache[key]);
        return cache[key]->second;//把链表返回过去
    }
    
    void put(int key, int value) {
        if(cache.find(key)!=cache.end())
        {
            cache[key]->second=value;
            item.splice(item.begin(),item,cache[key]);
            return;
        }
        if(item.size()==cap)
        {
            auto last=item.back();
            cache.erase(last.first);
            item.pop_back();
        }
        item.emplace_front(key,value);
        cache[key]=item.begin();
    }
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

236. 二叉树的最近公共祖先 - 力扣(LeetCode)

非常巧妙的一道题

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL || root==p || root==q) return root;
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        if(left==NULL) return right;
        if(right==NULL) return left;
        return root;
    }
};

72. 编辑距离 - 力扣(LeetCode)

很奇妙的一道动态规划的题

dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]

状态转移方程为:dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;

class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1,0));
        for(int i=0;i<=word1.size();i++) dp[i][0]=i;
        for(int j=0;j<=word2.size();j++) dp[0][j]=j;
        for(int i=1;i<=word1.size();i++)
        {
            for(int j=1;j<=word2.size();j++)
            {
                if(word1[i-1]==word2[j-1]) dp[i][j]=dp[i-1][j-1];
                else
                {
                    dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
                }
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

15. 三数之和 - 力扣(LeetCode)

双指针+转化

nums[i] + nums[j] + nums[k] == 0 可以转化为 nums[j] + nums[k]==-nums[i]

因为下标i,j,k互补相等且i<j<k,及在下标(i-idx]范围内查找。

及for循环遍历i,维护j、k两个指针

set<vector<int>> st;
st.insert({nums[i],nums[l],nums[r]});
vector<vector<int>> ans(st.begin(),st.end());

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        set<vector<int>> st;
        sort(nums.begin(),nums.end());
        int idx=nums.size();
        for(int i=0;i<idx;i++)
        {
            if(i&&nums[i]==nums[i-1]) continue;
            int l=i+1,r=idx-1;
            while(l<r)
            {
                if(nums[l]+nums[r]>-nums[i]) r--;
                else if(nums[l]+nums[r]<-nums[i]) l++;
                else
                {
                    st.insert({nums[i],nums[l],nums[r]});
                    l++,r--;//要注意插入后要更新状态
                }
            }
        }
        vector<vector<int>> ans(st.begin(),st.end());
        return ans;
    }
};

1143. 最长公共子序列 - 力扣(LeetCode)

动态规划经典题

数组范围是0~n-1;

动态规划存储上一步的状态所以是1~n;

因为比较的是上一步的状态所以是i-1和j-1;

状态转移方程1:dp[i][j] = dp[i-1][j-1] + 1;

状态转移方程2:dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int dp[1010][1010]; // 初始化全部元素为0
        for(int i = 1; i <= text1.size(); i++) {
            for(int j = 1; j <= text2.size(); j++) {
                if( text1[i-1] == text2[j-1] ) {
                     dp[i][j] = dp[i-1][j-1] + 1;  // 如果当前字符匹配,则增加长度
                } else {
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]); // 否则,取最大值
                }
            }
        }
        return dp[text1.size()][text2.size()]; // 返回最终结果
    }
};


LCR 143. 子结构判断 - 力扣(LeetCode)

class Solution {
public:
    bool judge(TreeNode* A, TreeNode* B)
    {
        if(B==NULL) return true;
        if((A==NULL&&B!=NULL)||A->val!=B->val) return false;
        return judge(A->left,B->left)&&judge(A->right,B->right);//本身||左字数查找||右子树查找
    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A==NULL||B==NULL) return false;
        return judge(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);//当前节点||同时左查||同时右查
    }
};

54. 螺旋矩阵 - 力扣(LeetCode)

class Solution {
private:
    int st[20][20];
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.empty()) return {}; // 处理空矩阵的情况
        vector<int> v;
        int ti=matrix.size();
        int tj=matrix[0].size();
        int x=0,y=0;
        st[0][0]=1;
        v.push_back(matrix[0][0]);
        while(v.size()<ti*tj)
        {
            while(y!=tj-1&&!st[x][y+1])
            {
                y++;
                v.push_back(matrix[x][y]);
                st[x][y]=1;
            }
            while(x!=ti-1&&!st[x+1][y])
            {
                x++;
                v.push_back(matrix[x][y]);
                st[x][y]=1;
            }
            while(y!=0&&!st[x][y-1])
            {
                y--;
                v.push_back(matrix[x][y]);
                st[x][y]=1;
            }
            while(x!=0&&!st[x-1][y])//x!=0的判断是为了防止数组越界
            {
                x--;
                v.push_back(matrix[x][y]);
                st[x][y]=1;
            }
        }
        return v;
    }
};

56. 合并区间 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end());
        int st=-2e9,ed=-2e9;
        vector<vector<int>> res;
        for(auto &ele : intervals)
        {

            if(ed<ele[0])
            {
                if(st!=-2e9) res.push_back({st,ed});
                st=ele[0],ed=ele[1];
            }
            else ed=max(ed,ele[1]);
        }
        if(st!=-2e9) res.push_back({st,ed});
        return res;
    }
};

46. 全排列 - 力扣(LeetCode)

class Solution {
private:
    vector<vector<int>> v;
    vector<int> num;
    int vis[10];
    int a[10];
    int r;
    int n;
public:
    void dfs(int x)
    {
        if(x==r+1)
        {
            vector<int> nt;
            for(int i=1;i<=r;i++) nt.push_back(num[a[i]-1]);
            v.push_back(nt);
            return;
        }
        for(int i=1;i<=n;i++)
        {
            if(!vis[i])
            {
                a[x]=i;
                vis[i]=1;
                dfs(x+1);
                vis[i]=0;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        num=nums;
        r=n=nums.size();
        a[0]=0;
        dfs(1);
        return v;
    }
};

105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

class Solution {
private:
    vector<int> preorder;
    unordered_map<int,int> mp;
    TreeNode* check(int root,int left,int right)
    {
        if(left>right) return NULL;
        TreeNode* node=new TreeNode(preorder[root]);
        int i=mp[preorder[root]];//对前序遍历数组进行操作
        node->left = check(root+1,left,i-1);
        node->right=check(root+i+1-left,i+1,right);
        return node;
    }
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        this->preorder=preorder;
        for(int i=0;i<inorder.size();i++)
        {
            mp[inorder[i]]=i;//记录下中序遍历数组中元素的坐标
        }
        return check(0,0,inorder.size()-1);
    }
};

103. 二叉树的锯齿形层序遍历 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        if(!root) return {};
        vector<vector<int>> ans;
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty())
        {
            vector<int> v;
            for(int i=que.size();i>=1;i--)
            {
                auto top=que.front();
                que.pop();
                v.push_back(top->val);//存储该节点的值,每次循环只存储当前节点的值
                if(top->left) que.push(top->left);
                if(top->right) que.push(top->right);
            }
            if(ans.size()%2) reverse(v.begin(),v.end());
            ans.push_back(v);
        }
        return ans;
    }
};

279. 完全平方数 - 力扣(LeetCode)

记模板就行

状态转移方程:mx = min(mx, f[i - j * j]);

class Solution {
public:
    int numSquares(int n) {
        vector<int> f(n + 1);
        for (int i = 1; i <= n; i++) {
            int mn = INT_MAX;
            for (int j = 1; j * j <= i; j++) {
                mn = min(mn, f[i - j * j]);
            }
             f[i] = mn + 1; 
        }
        return f[n];
    }
};

240. 搜索二维矩阵 II - 力扣(LeetCode)

从右上角搜最合适

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) 
    {
        if (matrix.empty() || matrix[0].empty()) return false;
        int m = matrix.size(), n = matrix[0].size();
        int row = 0, col = 0; 
        while (row < m && col >= 0) 
        {
            if (matrix[row][col] == target)  return true;
            else if (matrix[row][col] > target) col--;
            else row++;
        }
        return false;
    }
};

牛牛的字符串解码问题_牛客题霸_牛客网 (nowcoder.com)

用栈解决嵌套问题

class Solution {
public:
    string decodeString(string s) {
        int num;
        stack<string> st;
        stack<int> nums;
        string ts;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]>='1'&&s[i]<='9')
            {
                num=s[i]-'0';
            }
            else if(s[i]=='[')
            {
                st.push(ts);
                nums.push(num);
                ts="";
                num=0;
            }
            else if(s[i]==']')
            {
                string tp=ts;//保存中间编码部分
                int ans=nums.top();
                nums.pop();
                ts=st.top();
                st.pop();
                for(int i=1;i<=ans;i++) ts+=tp;
            }
            else
            {
                ts+=s[i];
            }
        }
        return ts;
    }
};

牛奶供应问题_牛客题霸_牛客网 (nowcoder.com)

重复:不断取出最小的,加上下一元素后再压入

最后取出最大的

#include <functional>
#include <vector>
class Solution {
public:
    int animalTaskScheduler(vector<int>& taskDurations, int capacity) {
        priority_queue<int,vector<int>,greater<int>> que;
        for(auto &ele : taskDurations)
        {
            if(que.size()<capacity)
            {
                que.push(ele);
            }
            else 
            {
                int num=que.top();
                que.pop();
                num+=ele;
                que.push(num);
            }
        }
        int res;
        while(!que.empty())
        {
            res=que.top();
            que.pop();
        }
        return res;
    }
};
;