深度优先(DFS)

1 dfs简介

        深度优先其实就是回溯算法,也就是暴力穷举,所以效率并不高。深度优先常常使用递归来进行编码,套路如下:做出选择;递归;撤销选择;只需要注意递归的退出点就可以了。

2 力扣题目

2.1 岛屿的最大面积

695. 岛屿的最大面积

class Solution {
public:
    int maxAreaOfIsland(vector>& grid) {
        int maxArea = 0;
        for (int i = 0; i < grid.size(); ++i)
        {
            for (int j = 0; j < grid[0].size(); ++j)
            {
                if (grid[i][j])
                {
                    maxArea = std::max(maxArea, helper(grid, i, j));
                }                
            }
        }

        return maxArea;
    }

    int helper(vector>& grid, int r, int c)
    {
        int area = 0;
        if (r >=0 && r < grid.size() && c >=0 && c < grid[0].size() && grid[r][c])
        {
            area += 1;
            grid[r][c] = 0;
            if ((c-1) >=0 && grid[r][c-1])
            {
                area += helper(grid, r, c-1);                
            }

            if ((c+1) < grid[0].size() && grid[r][c+1])
            {
                area += helper(grid, r, c+1);
            }     

            if ((r-1) >=0 && grid[r-1][c])
            {
                area += helper(grid, r-1, c);
            }   

            if ((r+1) < grid.size() && grid[r+1][c])
            {
                area += helper(grid, r+1, c);
            }                           
        }

        return area;
    }
};

2.2 不含重复数字的全排列

46. 全排列

class Solution {
public:
    vector> permute(vector& nums) {
        vector path;
        vector> result;
        dfs(nums,path,result);
        return result;
    }

    void dfs(vector& nums, vector& path, vector>& result)
    {
        if (nums.size() == path.size())
        {
            result.emplace_back(path);
            return;
        }

        for (int n : nums)
        {
            if (std::find(path.begin(), path.end(), n) != path.end())
            {
                continue;
            }

            path.push_back(n);
            dfs(nums,path,result);
            path.pop_back();
        }
    }
};

47. 全排列 II

class Solution {
public:
    vector> permuteUnique(vector& nums) {
        vector path, index;
        vector> result;
        dfs(nums,index,path,result);
        std::sort(result.begin(),result.end());
        result.resize(std::distance(result.begin(),std::unique(result.begin(),result.end())));
        return result;
    }

    void dfs(vector& nums, vector& index, vector& path, vector>& result)
    {
        if (nums.size() == path.size())
        {
            result.emplace_back(path);
            return;
        }

        for (int i=0;i

2.3 二叉树中和为某一值的路径

剑指 Offer 34. 二叉树中和为某一值的路径

/**
 * 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:
    vector> pathSum(TreeNode* root, int target) {
        vector> r;
        vector path;
        backTrack(r,path,root,target);
        return r;
    }

    void backTrack(vector>& r, vector& path, TreeNode* root, int target)
    {
        if (root)
        {           
            path.push_back(root->val); 
            if (root->val == target && !root->left && !root->right)
            {
                r.push_back(path);
            }

            backTrack(r,path,root->left,target-root->val);
            backTrack(r,path,root->right,target-root->val);
            path.pop_back();
        }
    }
};

2.4 二叉树的最近公共祖先

剑指 Offer 68 - II. 二叉树的最近公共祖先

/**
 * 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 {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector path_p = getPath(root, p);
        vector path_q = getPath(root, q);
        TreeNode* ancestor;
        for (int i = 0; i < path_p.size() && i < path_q.size(); ++i) 
        {
            if (path_p[i] == path_q[i]) 
            {
                ancestor = path_p[i];
            }
            else 
            {
                break;
            }
        }
        return ancestor;        
    }

    vector getPath(TreeNode* root, TreeNode* target) {
        vector path;
        dfs(root, target, path);
        return path;
    }

    bool dfs(TreeNode* root, TreeNode* target, vector& path)
    {
        bool find = false;
        do 
        {
            if (root)
            {
                path.push_back(root);
                if (root == target)
                {
                    find = true;
                    break;
                }

                if (dfs(root->left, target, path) || dfs(root->right, target, path))
                {
                    find = true;
                    break;
                }

                path.pop_back();
            }
        } while(0);

        return find;
    }
};

2.5 二叉搜索树转换成双向链表

剑指 Offer 36. 二叉搜索树与双向链表

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    Node* treeToDoublyList(Node* root) {
        if (!root)
        {
            return root;
        }

        Node *pre = NULL, *head = NULL;
        backTrack(root, pre, head);
        head->left = pre;
        pre->right = head;

        return head;
    }

    void backTrack(Node* cur, Node*& pre, Node*& head)
    {
        if (!cur)
        {
            return;
        }

        backTrack(cur->left, pre, head);
        if (!pre)
        {
            head = cur;
        }
        else
        {
            pre->right = cur;
            cur->left = pre;
        }

        pre = cur;
        backTrack(cur->right, pre, head);
    }
};

2.6 矩阵中的路径

剑指 Offer 12. 矩阵中的路径

class Solution {
public:
	bool exist(vector>& board, string word) {
		bool find = false;
        int row = board.size(), col = board[0].size();
		vector> visited(row, vector(col, false));
		do
		{
			if (board.empty())
			{
				if (word.empty())
				{
					find = true;
				}
				break;
			}

            for (int r = 0; r < row; ++r)
            {
                for (int c = 0; c < col; ++c)
                {
                    if (backTrack(board, word, visited, 0, r, c))
                    {
                        find = true;
                        return find;
                    }
                }
            }
		} while (0);

		return find;
	}

	bool backTrack(vector>& board, const string& word, vector>& visited, int pos, int r, int c)
	{
		bool find = false;
		do
		{
			if (pos == word.size())
			{
				find = true;
				break;
			}

			if (word.empty())
			{
				break;
			}

			int row = board.size(), col = board[0].size();
            char ch = word[pos];
            if (r >= 0 && r < row && c >= 0 && c < col)
            {
                if (visited[r][c])
                {
                    break;
                }

                if (ch != board[r][c])
                {
                    break;
                }

                visited[r][c] = true;
                if (backTrack(board, word, visited, pos+1, r-1, c)
                    || backTrack(board, word, visited, pos+1, r+1, c)
                    || backTrack(board, word, visited, pos+1, r, c-1)
                    || backTrack(board, word, visited, pos+1, r, c+1))
                {
                    find = true;
                    break;
                }
                visited[r][c] = false;
            }
		} while (0);

		return find;
	}
};

2.7 字符串的排列

剑指 Offer 38. 字符串的排列

class Solution {
public:
    //方法一
    // vector rec;
    // vector vis;

    // void backtrack(const string& s, int i, int n, string& perm) {
    //     if (i == n) {
    //         rec.push_back(perm);
    //         return;
    //     }
    //     for (int j = 0; j < n; j++) {
    //         if (vis[j] || (j > 0 && !vis[j - 1] && s[j - 1] == s[j])) {
    //             continue;
    //         }
    //         vis[j] = true;
    //         perm.push_back(s[j]);
    //         backtrack(s, i + 1, n, perm);
    //         perm.pop_back();
    //         vis[j] = false;
    //     }
    // }

    // vector permutation(string s) {
    //     int n = s.size();
    //     vis.resize(n);
    //     sort(s.begin(), s.end());
    //     string perm;
    //     backtrack(s, 0, n, perm);
    //     return rec;
    // }


    //方法二
    unordered_set rec;
    void helper(string& s, int beginIndex, int endIndex)
    {
        if (beginIndex == endIndex)
        {
            rec.insert(s);
            return;
        }

        for (int i=beginIndex;i<=endIndex;++i)
        {
            char c = s[i];
            s[i] = s[beginIndex];
            s[beginIndex] = c;

            helper(s, beginIndex+1, endIndex);
            
            c = s[i];
            s[i] = s[beginIndex];
            s[beginIndex] = c;            
        }
    }

    vector permutation(string s) {
        int n = s.size();
        helper(s, 0, n-1);
        return vector(rec.begin(), rec.end());
    }    
};

2.8 被围绕的区域

130. 被围绕的区域

class Solution {
public:
    /* 方法一
    先用 for 循环遍历棋盘的四边,用 DFS 算法把那些与边界相连的O换成一个特殊字符,比如#;
    然后再遍历整个棋盘,把剩下的O换成X,把#恢复成O。这样就能完成题目的要求,时间复杂度 O(MN)。 
    */
    void solve(vector>& board) {
        int n = board.size(), m = board[0].size();
        vector> visited(n, vector(m,false));
        for (int c=0;c>& board, vector>& visited, int r, int c)
    {
        if (r < 0 || r >= board.size() || c < 0 || c >= board[0].size())
            return;

        if (visited[r][c])
            return;

        visited[r][c] = true;
        if (board[r][c] == 'O')
        {
            board[r][c] = '#';
            dfs(board, visited, r-1, c);
            dfs(board, visited, r+1, c);
            dfs(board, visited, r, c-1);
            dfs(board, visited, r, c+1);
        }
        visited[r][c] = false;
    }
};

//方法二:使用UnionFind

class Solution {
public:
    /* 方法一
    先用 for 循环遍历棋盘的四边,用 DFS 算法把那些与边界相连的O换成一个特殊字符,比如#;
    然后再遍历整个棋盘,把剩下的O换成X,把#恢复成O。这样就能完成题目的要求,时间复杂度 O(MN)。 
    */
    // void solve(vector>& board) {
    //     int n = board.size(), m = board[0].size();
    //     vector> visited(n, vector(m,false));
    //     for (int c=0;c>& board, vector>& visited, int r, int c)
    // {
    //     if (r < 0 || r >= board.size() || c < 0 || c >= board[0].size())
    //         return;

    //     if (visited[r][c])
    //         return;

    //     visited[r][c] = true;
    //     if (board[r][c] == 'O')
    //     {
    //         board[r][c] = '#';
    //         dfs(board, visited, r-1, c);
    //         dfs(board, visited, r+1, c);
    //         dfs(board, visited, r, c-1);
    //         dfs(board, visited, r, c+1);
    //     }
    //     visited[r][c] = false;
    // }

    //方法二:使用UnionFind
    class UF 
    {
    public:
        UF(int n) 
        {
            count = n;
            parent = new int[n];
            size = new int[n];
            for (int i = 0; i < n; i++) 
            {
                parent[i] = i;
                size[i] = 1;
            }
        }

        ~UF()
        {
            delete[] parent;
            delete[] size;
        }

        void Union(int p, int q) 
        {
            int rootP = Find(p);
            int rootQ = Find(q);
            if (rootP == rootQ)
                return;

            // 小树接到大树下面,较平衡
            if (size[rootP] > size[rootQ]) 
            {
                parent[rootQ] = rootP;
                size[rootP] += size[rootQ];
            }
            else 
            {
                parent[rootP] = rootQ;
                size[rootQ] += size[rootP];
            }

            count--;
        }

        bool Connected(int p, int q) 
        {
            int rootP = Find(p);
            int rootQ = Find(q);

            return rootP == rootQ;
        }

        int Find(int x) 
        {
            while (parent[x] != x) 
            {
                // 进行路径压缩
                parent[x] = parent[parent[x]];
                x = parent[x];
            }

            return x;
        }

    private:
        // 连通分量个数
        int count;
        // 存储一棵树
        int* parent;
        // 记录树的“重量”
        int* size;
    };

    void solve(vector>& board) {
        int m = board.size(), n = board[0].size();
        // 给 dummy 留一个额外位置
        UF uf(m * n + 1);
        int dummy = m * n;
        // 将首列和末列的 O 与 dummy 连通
        for (int i = 0; i < m; i++) 
        {
            if (board[i][0] == 'O')
                uf.Union(i * n, dummy);
            if (board[i][n - 1] == 'O')
                uf.Union(i * n + n - 1, dummy);
        }
        // 将首行和末行的 O 与 dummy 连通
        for (int j = 0; j < n; j++) 
        {
            if (board[0][j] == 'O')
                uf.Union(j, dummy);
            if (board[m - 1][j] == 'O')
                uf.Union(n * (m - 1) + j, dummy);
        }
        // 方向数组 d 是上下左右搜索的常用手法
        int d[4][2] = {{1,0}, {0,1}, {0,-1}, {-1,0}};
        for (int i = 1; i < m - 1; ++i)
        {
            for (int j = 1; j < n - 1; ++j) 
            {
                if (board[i][j] == 'O')
                {
                    // 将此 O 与上下左右的 O 连通
                    for (int k = 0; k < 4; ++k) 
                    {
                        int x = i + d[k][0];
                        int y = j + d[k][1];
                        if (board[x][y] == 'O')
                            uf.Union(x * n + y, i * n + j);
                    }                     
                }                
            }           
        }
        // 所有不和 dummy 连通的 O,都要被替换
        for (int i = 1; i < m - 1; ++i) 
            for (int j = 1; j < n - 1; ++j) 
                if (!uf.Connected(dummy, i * n + j))
                    board[i][j] = 'X';
    }
};

2.9 所有可能的路径

797. 所有可能的路径

剑指 Offer II 110. 所有路径

class Solution {
public:
    vector> allPathsSourceTarget(vector>& graph) {
        vector path;
        vector> res;
        dfs(graph, 0, path, res);
        return res;
    }

    void dfs(vector>& graph, int index, vector& path, vector>& res)
    {     
        path.push_back(index);   
        if (index == graph.size()-1)
        {
            res.emplace_back(path);
            path.pop_back();
            return;
        }        
        
        for (int i : graph[index])
            dfs(graph, i, path, res);
        
        path.pop_back();
    }
};

2.10 二叉树剪枝

814. 二叉树剪枝

剑指 Offer II 047. 二叉树剪枝

/**
 * 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:
    TreeNode* pruneTree(TreeNode* root) {
        if (!root)
        {
            return root;
        }

        if (root->val == 0)
        {
            root->left = pruneTree(root->left);
            root->right = pruneTree(root->right);
            if (!root->left && !root->right)
            {
                return NULL;
            }
            else
            {
                return root;
            }
        }
        else
        {
            root->left = pruneTree(root->left);
            root->right = pruneTree(root->right);
            return root;
        }        
    }
};

2.11 除法求值

399. 除法求值

剑指 Offer II 111. 计算除法

class Solution {
public:
    vector calcEquation(vector>& equations, vector& values, vector>& queries) {
        vector r;
        //转换成图: 一维是起始索引,二维是pair{终止节点,权重}
        unordered_map> graph;
        buildGraph(equations, values, graph);

        for (auto& v : queries)
        {
            string first = v[0], second = v[1];
            if (graph.count(first))
            {
                if (graph[first].count(second))
                {
                    r.push_back(graph[first][second]);
                }
                else
                {
                    if (first == second)
                    {
                        r.push_back(1.0);
                    }
                    else
                    {
                        double res = -1.0;
                        unordered_map visited;
                        visited[first] = true;
                        for (auto& item : graph[first])
                        {
                            res = dfs(graph[first][item.first], item.first, second, graph, visited);
                            if (-1.0 != res)
                            {
                                break;
                            }
                        }
                        visited[first] = false;
                        r.push_back(res);
                        if (-1.0 != res)
                        {
                            graph[first].insert({second, res});
                            graph[second].insert({first, 1.0 / res});
                        }
                    }                    
                }
            }
            else
            {
                r.push_back(-1.0);
            }
        }

        return r;
    }

    void buildGraph(vector>& equations, vector& values, unordered_map>& graph)
    {
        int n = equations.size();
        for (int i=0;i>& graph, unordered_map& visited)
    {
        double res = -1.0;
        do
        {
            if (visited[first])
                break;

            if (!graph.count(first) || !graph.count(second))
                break;
            
            if (first.compare(second) == 0)
            {
                res = 1.0;
                break;
            }
            else
            {
                if (graph[first].count(second))
                {
                    res = base * graph[first][second];
                }
                else
                {
                    visited[first] = true;
                    for (auto& item : graph[first])
                    {
                        res = dfs(graph[first][item.first], item.first, second, graph, visited);
                        if (-1.0 != res)
                        {
                            res *= base;                            
                            break;
                        }
                    }
                    visited[first] = false;
                }
            }
        }while(0);
        
        return res;
    }
};

2.12 求根节点到叶子结点数字之和

129. 求根节点到叶节点数字之和

剑指 Offer II 049. 从根节点到叶节点的路径数字之和

/**
 * 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:
    int sumNumbers(TreeNode* root) {
        vector v;
        dfs(0, root, v);
        return accumulate(v.begin(), v.end(), 0);
    }

    void dfs(int base, TreeNode* cur, vector& v)
    {
        if (!cur)
        {
            v.push_back(base);
        }

        if (!cur->left && !cur->right)
        {
            v.push_back(base*10 + cur->val);
        }
        else
        {
            if (cur->left)
            {
                dfs(base*10 + cur->val, cur->left, v);
            }

            if (cur->right)
            {
                dfs(base*10 + cur->val, cur->right, v);
            }            
        }
    }
};

2.13 向下的路径节点之和

剑指 Offer II 050. 向下的路径节点之和

437. 路径总和 III

/**
 * 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:
    int pathSum(TreeNode* root, int targetSum) {
        if (!root)
            return 0;  

        return dfs(root, targetSum) + pathSum(root->left, targetSum) + pathSum(root->right, targetSum);
    }

    int dfs(TreeNode* root, int targetSum)
    {
        if (!root)
            return 0;

        if (targetSum == root->val)
        {
            return 1 + dfs(root->left, targetSum - root->val) + dfs(root->right, targetSum - root->val);
        }

        return dfs(root->left, targetSum - root->val) + dfs(root->right, targetSum - root->val);
    }
};

2.14

2.15

2.16

你可能感兴趣的:(算法学习,深度优先,算法)