leetcode解题思路分析(十九)127 - 133题

  1. 单词接龙
    给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:
    每次转换只能改变一个字母。
    转换过程中的中间单词必须是字典中的单词。

双向BFS:使用两个set,分别从start和end两头开始BFS
每次选择较小的set开始BFS, 也就是将小的作为start,大的作为end
如果end中能找到start,就结束
否则,在访问set中加入访问记录,并加入到tmp中,作为子节点。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {

        unordered_set<string> dict(wordList.begin(), wordList.end());
        if (dict.find(endWord) == dict.end() ) return 0;
        // 初始化起始和终点
        unordered_set<string> beginSet, endSet, tmp, visited;
        beginSet.insert(beginWord);
        endSet.insert(endWord);
        int len = 1;

        while (!beginSet.empty() && !endSet.empty()){
            if (beginSet.size() > endSet.size()){
                tmp = beginSet;
                beginSet = endSet;
                endSet = tmp;
            }
            tmp.clear();
            for ( string word : beginSet){
                for (int i = 0; i < word.size(); i++){
                    char old = word[i];
                    for ( char c = 'a'; c <= 'z'; c++){
                        if ( old == c) continue;
                        word[i] = c;
                        if (endSet.find(word) != endSet.end()){
                            return len+1;
                        }
                        if (visited.find(word) == visited.end() && dict.find(word) != dict.end()){
                            tmp.insert(word);
                            visited.insert(word);
                        }
                    }
                    word[i] = old;
                }
            }
            beginSet = tmp;
            len++;
            

        }
        return 0;
    }
};

  1. 最长连续序列
    给定一个未排序的整数数组,找出最长连续序列的长度。
    要求算法的时间复杂度为 O(n)。

要求时间复杂度O(n),所以采用哈希表是很自然的选择。

class Solution {
public:
    int longestConsecutive(vector<int>& nums)
    {
        if(nums.size()<2)
            return nums.size();
        unordered_set<int> s(nums.begin(),nums.end());
        int res=1;
        for(int num : s)
        {
            // 剪枝:若 num - 1 存在于hashset之中,那么num位于该段连续子序列的中间位置,
            // 只有num位于这段子序列的左边界时,才能一次就统计到这段子序列的长度,避免多次统计该段子序列的子子序列
            if(s.count(num - 1) != 0)
                continue;
            int len=1;
            while(s.count(num+1)!=0)
            {
                len++;
                num++;
            }
            res = max(res,len);
        }
        return res;
    }
};
  1. 求根到叶子节点数字之和
    给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
    例如,从根到叶子节点路径 1->2->3 代表数字 123。
    计算从根到叶子节点生成的所有数字之和。

很简单的题目,DFS即可

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    int ret;
public:
    int sumNumbers(TreeNode* root) {
        if (root == NULL)
            return 0;
        getSum(root, 0);
        return ret;
    }

    void getSum(TreeNode *root, int sum)
    {                
        if (root == NULL)
            return;
        sum = sum * 10 + root->val;
        if (root->left == NULL && root->right == NULL)
        {          
            ret += sum;
            return;
        }

        getSum(root->left, sum);
        getSum(root->right, sum);
    }
};
  1. 被围绕的区域
    **方法:(DFS深度优先遍历) O(n2)
    逆向考虑问题,我们先统计出哪些区域不会被攻占,然后将其它区域都变成’X’即可。

1.开一个二维布尔数组,记录哪些区域被遍历过。
2.枚举所有边界上的’O’,从该位置做深度优先遍历,只遍历是’O’的位置,并将所有遍历到的位置都标记成true。
3.将所有未遍历到的位置变成’X’。
**

class Solution {
public:
    vector<vector<bool>> st;
    int n,m;

    void solve(vector<vector<char>>& board) {
        if(board.empty() || board[0].empty()) return ;
        n = board.size();
        m = board[0].size();
        
        //初始化二维标记数组为false
        for(int i=0;i<n;i++){
            vector<bool> tmp;
            for(int j=0;j<m;j++){
                tmp.push_back(false);
            }
            st.push_back(tmp);
        }

        //搜寻边界为O的区域,并标记为true
        for(int i=0;i<n;i++){
            if(board[i][0] =='O' )   dfs(board,i,0);
            if(board[i][m-1] =='O')  dfs(board,i,m-1);
        }
        for(int i=0;i<m;i++){
            if(board[0][i] == 'O')   dfs(board,0,i);
            if(board[n-1][i] == 'O') dfs(board,n-1,i);
        }

        //未标记的全为X
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!st[i][j]){
                    board[i][j] = 'X';
                }
            }
        }
    }

    void dfs(vector<vector<char>>& board,int x,int y){
        st[x][y] = true;//标记该点已经搜寻过
        int dx[4]={1,0,0,-1};
        int dy[4]={0,1,-1,0};
        
        for(int i=0;i<4;i++){
            int tx = x+dx[i];
            int ty = y+dy[i];
            //st[tx][ty] = true;
            if(tx>=0 && tx<board.size() && ty>=0 && ty<board[0].size() && !st[tx][ty] && board[tx][ty] == 'O'){
                dfs(board,tx,ty);
            }
        }
    }
};

  1. 分割回文串
    给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
    返回 s 所有可能的分割方案。

本题采用回溯法求解。其实还可以优化:采取一个dp实现存储回文状态,然后根据该状态来直接判断子串是否为回文串,即空间换时间

class Solution {
    vector<vector<string>> res;
    int size;
    bool check(const string&s, int i, int j) {
        if (j < i) return true;
        if (s[i++] == s[j--]) return check(s, i, j);
        else return false;
    }
    void backtrack(const string& s, int ps, vector<string>& temp) {
        if (ps >= size) {
            res.push_back(temp);
            return;
        }
        for (int i = ps; i < size; ++i) {
            if (check(s, ps, i)) {
                temp.push_back(s.substr(ps, i-ps+1));
                backtrack(s, i+1, temp);
                temp.pop_back();
            }
        }
    }
public:
    vector<vector<string>> partition(const string& s) {
        size = s.size();
        if (size == 0) return res;
        vector<string> temp;
        backtrack(s, 0, temp);
        return res;
    }
};

  1. 分割回文串2
    给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
    返回符合要求的最少分割次数。

分割次数最少,则其子串分割次数也是最少,因此可以想到用动态规划求解:如果这是回文串,那么到这里为止的划分次数可以为这个回文串前面所需的次数+1

class Solution 
{
public:
    int minCut(string s) 
    {
        int l = s.length();
        vector<int> list(l + 1); // list[i]代表前i个字符需要划几次,特别地,list[0]=-1
        for(int i = 0; i < l + 1; ++i)
        { // 初始化[-1, 0, 1, 2, 3...]
            list[i] = i - 1;
        }

        for(int i = 0; i < l; ++i)
        { // 以每个字符为中心找最长回文子串
            list[i + 1] = min(list[i + 1], list[i] + 1); // 初始化,最坏情况下就比左边的多划一次
            
            if(i == l - 1)
            { // 最后一个了没必要找了
                break;
            }

            // 先找偶数个的
            int start = i, end = i + 1;
            while(s[start] == s[end])
            {
                list[end + 1] = min(list[end + 1], list[start] + 1);
                if(end == l - 1 || start == 0)
                {
                    break;
                }
                --start, ++end;
            }

            // 再找奇数个的
            start = i - 1, end = i + 1;
            if(start < 0)
            {
                continue;
            }

            while(s[start] == s[end])
            {
                list[end+1] = min(list[end + 1], list[start] + 1);
                if(end == l-1 || start == 0)
                {
                    break;
                }
                --start, ++end;
            }

            // 如果整个串都是回文串,那么就中断
            if(list[l] == 0)
            {
                return 0;
            }
        }
        return list[l];
    }
};


  1. 克隆图
    给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

本题题目看起来比较复杂,实际其实就是一个DFS的递归拷贝而已,换一个概念为复制一棵树是不是就好理解了

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution 
{
public:
    Node* used[101];           //创建一个节点(指针)数组记录每个拷贝过的节点
    Node* cloneGraph(Node* node) 
    {
        if(!node)
            return node;   //如果是空指针,则返回空

        if(used[node->val])
            return used[node->val];  //该节点已经拷贝,直接返回该节点的指针即可
            
        Node* p = new Node(node->val);    //创建拷贝节点
        used[node->val] = p;             //递归会遍历每一个原有节点,然后将拷贝后的指针放入used
        vector<Node*> tp = node->neighbors;

        for(int i = 0; i < tp.size(); i++) //将该节点的邻接节点放入拷贝节点邻接数组
            p->neighbors.push_back(cloneGraph(tp[i]));//递归实现每一个节点的更新

        return p;           //返回拷贝后的节点
    }
};

发布了129 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/105160173