数据结构算法--递归、分治、贪心、回溯和枚举详解

1. 递归

定义描述

解题策略

C++举例--翻转链表

2. 分治

定义描述

解题策略

3. 贪心

定义描述

解题策略

C++举例--最小生成树

4. 回溯

定义描述

解题策略

C++举例--深度优先搜索

5. 枚举

定义描述

解题策略

c++举例--计数质数

1. 递归

定义描述

循环直接或间接地调用小问题解决方案,直到解决同类大问题。

解题策略

  • 确定输入输出;
  • 确定递归条件;
  • 确定递归调用关系。

C++举例--翻转链表

定义:力扣(LeetCode)--翻转链表

代码:

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;
    }
};

分析:

  • 定义两个指针: pre 和 cur ;pre 在前 cur 在后。
  • 每次让 pre 的 next 指向 cur ,实现一次局部反转
  • 局部反转完成之后,pre 和 cur 同时往前移动一个位置
  • 循环上述过程,直至 pre 到达链表尾部

2. 分治

定义描述

顾名思义,分而治之;将复杂大问题分成两个或多个相似小问题,各个子问题相互独立,先求解小问题,最后求解大问题。

解题策略

  • 分解复杂大问题。各个子问题与大问题类似且相互独立;
  • 求解各个子问题;
  • 合并求解。将各个子问题的解合并为大问题的解。

3. 贪心

定义描述

大问题复杂度过高,则分解为各个子问题,每个子问题寻找最优解,合并为全局解,全局解不一定最优。

解题策略

  • 确定最优解;
  • 分解大问题,每个子问题都要满足最优解;
  • 合并所有子问题最优解为全局解,全局解不一定最优。

C++举例--最小生成树

定义:力扣(LeetCode)官网 - 连接所有点最小费用

代码:

class Djset {
public:
    vector<int> parent; // 记录节点的根
    vector<int> rank;   // 记录根节点的深度(用于优化)
    vector<int> size;   // 记录每个连通分量的节点个数
    vector<int> len;    // 记录每个连通分量里的所有边长度
    int num;            // 记录节点个数
    Djset(int n): parent(n), rank(n), len(n, 0), size(n, 1), num(n) {
        for (int i = 0; i < n; i++) {
            parent[i] = i;
        }
    }

    int find(int x) {
        // 压缩方式:直接指向根节点
        if (x != parent[x]) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    int merge(int x, int y, int length) {
        int rootx = find(x);
        int rooty = find(y);
        if (rootx != rooty) {
            if (rank[rootx] < rank[rooty]) {
                swap(rootx, rooty);
            }
            parent[rooty] = rootx;
            if (rank[rootx] == rank[rooty]) rank[rootx] += 1;
            // rooty的父节点设置为rootx,同时将rooty的节点数和边长度累加到rootx,
            size[rootx] += size[rooty];
            len[rootx] += len[rooty] + length;
            // 如果某个连通分量的节点数 包含了所有节点,直接返回边长度
            if (size[rootx] == num) return len[rootx];
        }
        return -1;
    }
};
struct Edge {
    int start; // 顶点1
    int end;   // 顶点2
    int len;   // 长度
};

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        int res = 0;
        int n = points.size();
        Djset ds(n);
        vector<Edge> edges;
        // 建立点-边式数据结构
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                Edge edge = {i, j, abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1])};
                edges.emplace_back(edge);
            }
        }
        // 按边长度排序
        sort(edges.begin(), edges.end(), [](const auto& a, const auto& b) {
            return a.len < b.len;
        });

        // 连通分量合并
        for (auto& e : edges) {
           res = ds.merge(e.start, e.end, e.len);
           if (res != -1) return res;
        }
        return 0;
    }
};

定义:

代码:

4. 回溯

定义描述

针对每一步搜索试探解决方案,如果不满足期望结果,则回溯回去重新选择。

解题策略

  • 确定解空间;
  • 确定探索方法;
  • 在解空间探索,不满足期望结果,则回溯回去重新选择解方法。

C++举例--深度优先搜索

定义:力扣(LeetCode)官网 - 岛屿数量

代码:

class Solution {
private:
    void dfs(vector<vector<char>>& grid, int r, int c) {
        int nr = grid.size();
        int nc = grid[0].size();

        grid[r][c] = '0';
        if (r - 1 >= 0 && grid[r-1][c] == '1') dfs(grid, r - 1, c);
        if (r + 1 < nr && grid[r+1][c] == '1') dfs(grid, r + 1, c);
        if (c - 1 >= 0 && grid[r][c-1] == '1') dfs(grid, r, c - 1);
        if (c + 1 < nc && grid[r][c+1] == '1') dfs(grid, r, c + 1);
    }

public:
    int numIslands(vector<vector<char>>& grid) {
        int nr = grid.size();
        if (!nr) return 0;
        int nc = grid[0].size();

        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    dfs(grid, r, c);
                }
            }
        }

        return num_islands;
    }
};

5. 枚举

定义描述

将问题所有可能的答案枚举,根据条件判断是否合适,合适则保留,否则丢弃。

解题策略

  • 确定枚举对象、枚举范围、判断条件;
  • 枚举可能的解,验证每个解是否合适。

c++举例--计数质数

定义:力扣(LeetCode)官网 - 计算质数

代码:

class Solution {
public:
    int countPrimes(int n) {
        if(n < 3)
            return 0;;
        //从3开始验算,所以初始值为1(2为质数)。
        int count = 1;
        for (int i = 3; i < n; i++){
            //当某个数为 2 的 n 次方时(n为自然数),其 & (n - 1) 所得值将等价于取余运算所得值
            //*如果 x = 2^n ,则 x & (n - 1) == x % n
            //if(i % 2 == 0)
            if ((i & 1) == 0)
                continue; ;
            bool sign = true;
            //用 j * j <= i 代替 j <= √i 会更好。
            //因为我们已经排除了所有偶数,所以每次循环加二将规避偶数会减少循环次数
            for (int j = 3; j * j <=i; j+=2){
                if (i % j == 0){
                    sign = false;
                    break;
                }
            }
            if (sign)
                count++; ;
        }
        return count;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值