【算法题】牛客研发最爱考[71 - 80]

括号生成(dfs)

class Solution {
    
    
public:

    vector<string> ans;

    vector<string> generateParenthesis(int n) {
    
    
        /*
            括号序列的性质:(重要,常用!)
                1. 任何前缀里 '(' 的数量 >= ')'的数量
                2. 左右括号数量相等
        */
        dfs(n,0,0,"");
        return ans;
    }

    void dfs(int n,int lc,int rc,string seq)
    {
    
    
        if(lc == n && rc == n) ans.push_back(seq);
        else
        {
    
    
            if(lc < n) dfs(n,lc + 1,rc , seq + '(');
            if(rc < n && lc > rc) dfs(n,lc,rc + 1,seq + ')');
        }
    }
};

最长公共子序列(DP)(求具体方案)

记录方案,记录转移状态,这里记录最前面,也就是第一个可以转移的

class Solution {
    
    
public:
    /**
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
    string LCS(string s1, string s2) {
    
    
        // write code here
        int n = s1.size(), m = s2.size();
        vector<vector<int>> f(n + 1,vector<int>(m + 1));
        
        for(int i = 1;i <= n;i ++ )
            for(int j = 1;j <= m;j ++ )
            {
    
    
                f[i][j] = max(f[i-1][j],f[i][j - 1]);
                if(s1[i - 1] == s2[j - 1]) f[i][j] = max(f[i][j], f[i-1][j-1] + 1);
            }
       
        if(f[n][m] == 0) return "-1";
        // 最长公共子序列记录方案
        string res;
        int i = n,j = m;
        while(f[i][j] > 0){
    
     // 记录方案,记录转移状态,这里记录最前面,也就是第一个可以转移的
            while(f[i - 1][j] == f[i][j]) i --; // 12C4B6
            while(f[i][j - 1] == f[i][j]) j --;
            res += s1[i - 1]; // i从下标1开始,所以要-1
            i --,j -- ;
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

丢棋子问题(动态规划)

题解

法一:爆搜(超时)

class Solution {
    
    
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回最差情况下扔棋子的最小次数
     * @param n int整型 楼层数
     * @param k int整型 棋子数
     * @return int整型
     */
    int solve(int n, int k) {
    
    
        // write code here
        return dfs(n,k);
    }
    
    int dfs(int n,int k)
    {
    
    
        if(n == 0) return 0;
        if(k == 1) return n;
        
        int minv = INT_MAX;
        for(int i = 1;i <= n;i ++)
        {
    
    
            minv = min(minv,max(dfs(n - i,k),dfs(i - 1,k - 1)));
        }
        return minv + 1;
    }
};

法二:暴搜改动态规划,时间复杂度 O ( n ∗ n ∗ K ) O(n * n * K) O(nnK)

class Solution {
    
    
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回最差情况下扔棋子的最小次数
     * @param n int整型 楼层数
     * @param k int整型 棋子数
     * @return int整型
     */
    int solve(int n, int k) {
    
    
        // write code here
        if(n == 0 || k == 0) return 0;
        if(k == 1) return n;
        
        vector<vector<int>> f(n + 1,vector<int>(k + 1));
        for(int i = 1;i <= n;i ++ ) f[i][1] = i;
        
        for(int i = 1;i <= n;i ++ )
            for(int j = 2;j <= k;j ++ )
            {
    
    
                int minv = INT_MAX;
                for(int k = 1;k <= i ;k ++)
                      minv = min(minv,max(f[k - 1][j - 1],f[i - k][j]));
                f[i][j] = minv + 1;
            }
        
        return f[n][k];
    }
};

法三:打表,找规律
考虑n个棋子扔1.2.3.。。k次能够解决多少层问题,打表,找规律。

class Solution {
    
    
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 返回最差情况下扔棋子的最小次数
     * @param n int整型 楼层数
     * @param k int整型 棋子数
     * @return int整型
     */
    int solve(int n, int k) {
    
    
        // write code here
        if(n == 0 || k == 0) return 0;
        if(k == 1) return n;
        
        int logk = log2(n) + 1;
        if(k >= logk) return logk;
        
        vector<vector<int>> f(n + 1,vector<int>(k + 1,0)); // 棋子数,楼层数
        for(int i = 1;i <= k;i ++ ) f[1][i] = i;
        for(int i = 2;i <= n;i ++ )
             for(int j = 0;j <= k;j ++ )
             {
    
    
                 if(j == 0){
    
    
                     f[i][j] = 0;
                     continue;
                 }
                 f[i][j] = f[i - 1][j - 1] + f[i- 1][j] + 1;
                 if(f[i][j] >= n) return i;
             }
        
        return 0;
    }
};

合并区间(排序)

先对区间排序,最后的时候记得把最后一段区间加入

#include <algorithm>
/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
bool cmp(Interval &a,Interval &b)
{
    
    
    return a.start < b.start;
}

class Solution {
    
    
public:
    
    
    
    vector<Interval> merge(vector<Interval> &intervals) {
    
    
        vector<Interval> ans;
        int n = intervals.size();
        sort(intervals.begin(),intervals.end(),cmp);
        
        int st = -2e9,ed = -2e9;
        for(int i = 0;i < n;i ++ )
        {
    
    
            if(ed < intervals[i].start)
            {
    
    
                if(st!=-2e9) ans.push_back({
    
    st,ed});
                st = intervals[i].start,ed = intervals[i].end;
            }else ed = max(ed,intervals[i].end);
        }
        if(st != -2e9) ans.push_back({
    
    st,ed}); // 把最后一段区间加入
        return ans;
    }
};

回文数字(数学)

class Solution {
    
    
public:
    /**
     * 
     * @param x int整型 
     * @return bool布尔型
     */
    bool isPalindrome(int x) {
    
    
        // write code here
        // 翻转一半
        if(x < 0 || (x % 10 == 0 && x != 0)) return false;
        
        int res = 0;
        while(x > res)
        {
    
    
            res = res * 10 + x % 10;
            x /= 10;
        }
        return res == x || res / 10 == x;
    }
};

顺时针旋转矩阵(数学)

题解

class Solution {
    
    
public:
    vector<vector<int> > rotateMatrix(vector<vector<int> > mat, int n) {
    
    
        // write code here
        for(int i = 0;i < n;i ++ )
            for(int j = 0;j < i;j ++ )
                 swap(mat[i][j],mat[j][i]);
        
        for(int i = 0;i < n;i ++ )
            for(int j = 0,k = n - 1;j < k;j ++,k -- )
                swap(mat[i][j],mat[i][k]);
        return mat;
    }
};

判断一棵二叉树是否是二叉搜索树和完全二叉树(递归)

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
    
    
public:
    /**
     * 
     * @param root TreeNode类 the root
     * @return bool布尔型vector
     */
    vector<bool> judgeIt(TreeNode* root) {
    
    
        // write code here
        return {
    
    dfs1(root,INT_MIN,INT_MAX),dfs2(root)};
    }
    
    bool dfs1(TreeNode *root,long long minv,long long maxv) // 判断是否是二叉搜索树
    {
    
    
        if(!root) return true;
        int v = root->val;
        if(v < minv || v > maxv) return false;
        return dfs1(root->left,minv,v - 1ll) && dfs1(root->right,v + 1ll,maxv);
    }
    
    bool dfs2(TreeNode *root) // 判断是否是完全二叉树
    {
    
    
        if(!root) return true;
        if(root->left && root->right == NULL) return dfs2(root->left);
        if(root->left == NULL && root->right) return false;
        return dfs2(root->left) && dfs2(root->right);
    }
};

判断是否是二叉搜索树的迭代版本

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    
    
public:
    bool isValidBST(TreeNode* root) {
    
    
        /*
            迭代:二叉搜索树前序遍历是递增数组
        */
        stack<TreeNode*> stk;
        long long inorder = INT_MIN - 1ll;
        
        // 迭代二叉树模板
        while(root || !stk.empty())
        {
    
    
            while(root != NULL){
    
    
                stk.push(root);
                root = root->left;
            }
            root = stk.top();
            stk.pop();
            if(root->val <= inorder) return false;
            inorder = root->val;

            root = root->right; // 转向右边
        }
        return true;
    }
};

重排链表(链表)

方法二:寻找链表中点 + 链表逆序 + 合并链表

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
    
    
public:
    void reorderList(ListNode* head) {
    
    
        if(!head) return ;
        
        auto fast = head,slow = head;
        while(fast->next != NULL && fast->next->next != NULL) // 这样写,偶数找第一个中点
        {
    
                                           // 规定前半段比后半段多1
            fast = fast->next->next;
            slow = slow->next;
        }

        //cout << slow->val;  1. 看中点是否正确
        auto p = reverse(slow->next);
        slow->next = NULL;
        
        /* 2. 看反转链表是否正确
        while(head) cout<<head->val <<' ',head=head->next;
        cout << endl;
        while(p) cout << p->val <<' ', p = p->next;
        */

        // 3.合并链表
        merge(head,p);
    }

    ListNode* reverse(ListNode* head)
    {
    
    
        ListNode* pre = NULL,*cur = head;
        while(cur){
    
    
            auto next = cur->next;
            cur->next = pre;
            pre = cur,cur = next;
        }
        return pre;
    }

    void merge(ListNode* l1,ListNode* l2)
    {
    
    
        while(l1 && l2)
        {
    
    
            auto l1_next = l1->next, l2_next = l2->next;

            l1->next = l2;
            l1 = l1_next;

            l2->next = l1;
            l2 = l2_next;
        }
    }
};

二叉树中是否有从根节点到叶子节点的节点值之和等于 sum 的路径(递归)

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
    
    
public:
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
    bool hasPathSum(TreeNode* root, int sum) {
    
    
        // write code here
        return dfs(root,sum);
    }
    
    bool dfs(TreeNode* root,int sum)
    {
    
    
        if(!root) return false;
        sum -= root->val;
        if(!root->left && !root->right && sum == 0) return true;
        return dfs(root->left,sum) || dfs(root->right,sum);
    }
    
};

有重复数字的所有排列(递归)

去重技巧

class Solution {
    
    
public:
    
    vector<vector<int> > ans;
    vector<int> path;
    vector<bool> st;
    
    vector<vector<int> > permuteUnique(vector<int> &num) {
    
    
        sort(num.begin(),num.end());
        st = vector<bool>(num.size());
        dfs(num,0);
        return ans;
    }
    
    void dfs(vector<int>& num,int u)
    {
    
    
        if(u == num.size())
        {
    
    
            ans.push_back(path);
            return;
        }
        
        for(int i = 0;i < num.size();i ++ )
        {
    
    
            if(!st[i])
            {
    
    
                st[i] = true;
                path.push_back(num[i]);
                dfs(num,u + 1);
                path.pop_back();
                st[i] = false;
                
                while(i+ 1 < num.size() &&  num[i + 1] == num[i]) i ++ ; // 去重
            }
            
        }
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_43154149/article/details/114282231
80