LintCode 解题记录 17.10.5 递归

本文针对LintCode平台上的多个经典算法题目进行了详细解答,包括平衡二叉树的判断、最大路径和的计算、有序链表转换为平衡二叉搜索树等。通过对这些题目的解析,读者可以了解到如何利用递归、回溯等技巧解决二叉树和链表的相关问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LintCode Balanced Binary Tree

题目描述

给定一棵树,判断其是不是平衡二叉树(Avl树)

思路

平衡二叉树的定义是对于一个二叉查找树来说,其每一个节点的平衡因子(左右子树高度差的绝对值)小于2。
所以思路就很明朗,对于每一个节点,先判断该节点是否满足平衡条件,如何不满足则直接返回false,如果满足就递归地判断其左子树和右子树。

代码
    bool isBalanced(TreeNode * root) {
        // write your code here
        if (root == NULL) return true;
        int leftheight = getHeight(root->left);
        int rightheight = getHeight(root->right);
        if (abs(leftheight - rightheight) > 1) return false;
        return isBalanced(root->left) && isBalanced(root->right);
    }

    //求树高的两行 代码
    int getHeight(TreeNode *root) {
        if (root == NULL) return 0;
        return max(getHeight(root->left),getHeight(root->right)) + 1;
    }

LintCode Binary Tree Maximum Path Sum

题目描述

给定一个二叉树,求这个二叉树的最大路径和。路径的起点和终点都可以选取二叉树的任何一个节点。

思路

对于某一个节点来说,经过这个节点的最大路径和一定是从这个节点的左子树选一条最大的路,和从右子树同样选择一条最大的路,然后遍历所有的节点,维护这个最大值即可。

代码
    int maxPathSum(TreeNode * root) {
        // write your code here
        if (root == NULL) return INT_MIN;
        int lm = getBigger(root->left);
        int rm = getBigger(root->right);
        int r1 = (lm > 0 ? lm : 0) + root->val + (rm > 0 ? rm : 0);
        int r2 =  max(maxPathSum(root->left), maxPathSum(root->right));
        return max(r1, r2);
    }

    int getBigger(TreeNode *root) {
        if (root == NULL) return 0;
        return max(getBigger(root->left), getBigger(root->right) ) + root->val;
    }

//version 2 感觉这个代码写得更好一点
    int maxPathSum(TreeNode * root) {
        // write your code here
        int res = INT_MIN;
        helper(root, res);
        return res;
    }

    int helper(TreeNode *root, int &res) {
        if (root == NULL) return 0;
        int lm = helper(root->left, res);
        int rm = helper(root->right, res);
        int curr = (lm > 0 ? lm : 0) + root->val + (rm > 0 ? rm : 0);
        if (curr > res) res = curr;
        return root->val + max(lm, max(rm, 0));
    }

LintCode Convert Sorted List to Balanced BST

题目描述

给定一个排序的链表,将其转换成平衡二叉树。

思路

刚看到这道题时,想都没想就翻开自己笔记本开始复习平衡二叉树的Rotate操作了,后来提交发现超时。才发现自己没有用到链表是有序的这个条件。既然是有序的,就把这个链表一分两半,左边用来递归的建立左子树,右边用来建立右子树,中间的节点作为根节点即可。

注意

我自己做的时候是直接遍历一遍链表把结果放在数组里然后再来递归建树的,可以说是很简单粗暴的,也AC了。这里说一下其余两种方法。一是不用数组,从链表的头结点开始,找到中间节点,然后将其一分为2递归建树。只需要让一个指针一次走两步,另一个指针一次走一步,最后即可再快指针到达末尾的时候慢指针就到达了中间的节点。还有一种思路是先递归构建左子树,同时不断移动链表的头指针,使得该指针一直指向对应当前子树的根节点。后两种的代码可以见这个博客:
http://blog.youkuaiyun.com/worldwindjp/article/details/39722643

代码
    TreeNode * sortedListToBST(ListNode * head) {
        // write your code here
        if (head == NULL) return NULL;
        vector<int> num;
        while (head) {
            num.push_back(head->val);
            head = head->next;
        }
        TreeNode *root = NULL;
        helper(num, root, 0, num.size()-1);
        return root;
    }

    void helper(vector<int> num, TreeNode *&root, int left, int right) {
        if (left > right) return;
        int mid = (left + right) >> 1;
        root = new TreeNode(num[mid]);
        helper(num, root->left, left, mid-1);
        helper(num, root->right, mid+1, right);
    }

LintCode Gray Code

题目描述

给定一个数n,代表总共的比特位数。要求每次只改变一位,得到一个数组,这个数组包含从0到2^n的所有整数。实际上就是 格雷码。

思路

暴力搜索。对一个数的第i位取反可以用(num ^ (1 << i))来实现。对一个数跳到下一个数需要对这个数的所有位都尝试一遍,一共有2^n个数,所以复杂度为O(n2^n)。

挑战

O(2^n)复杂度。通过列举前面的例子可以发现,n位的格雷码有两部分,一部分是n-1位的格雷码前面加上0,然后接上1<

代码
    vector<int> grayCode(int n) {
        // write your code here
        vector<int> res;
        if (n == 0) {
            res.push_back(0);
            return res;
        }
        vector<int> tmp = grayCode(n-1);
        res = tmp;
        int addNumber = 1 << (n-1);
        for (int i = tmp.size()-1; i >= 0; i--) {
            res.push_back(addNumber + tmp[i]);
        }
        return res;
    }

LintCode Flatten List

题目描述

给定一个list,其中的每一个元素都是一个list或者是一个integer。要求把这个list转成全部都是integer的操作。

思路

递归思路。遍历链表,如果是整数,则将其压到结果中,如果不是整数,就递归操作。

代码
    vector<int> flatten(vector<NestedInteger> &nestedList) {
        // Write your code here
        vector<int> res;
        for (auto ls: nestedList) {
            if (ls.isInteger()) {
                res.push_back(ls.getInteger());
            } else {
                vector<NestedInteger> sublist = ls.getList();
                vector<int> tmp = flatten(sublist);
                for (auto t : tmp) {
                    res.push_back(t);
                }
            }
        }
        return res;
    }
挑战

不用递归解决。

思路

不用递归的话就是当遍历到的元素是链表而不是整数时,就把这个list内的全部元素重新压到原有的链表中。遍历完成之后再从头遍历一遍。直到全部都是整数就退出循环。

代码
    vector<int> flatten(vector<NestedInteger> &nestedList) {
        // Write your code here
        vector<int> res;
        bool isFlat = true;
        vector<NestedInteger> nList = nestedList;
        while (isFlat) {
            isFlat = false;
            vector<NestedInteger> newls;
            for (auto ls: nList) {
                if (ls.isInteger()) {
                    newls.push_back(ls);
                } else {
                    vector<NestedInteger> sublist = ls.getList();
                    for (auto ele : sublist) {
                        newls.push_back(ele);
                    }
                    isFlat = true;
                }
            }
            nList = newls;
        }
        for (auto ele : nList) {
            res.push_back(ele.getInteger());
        }
        return res;
    }

LintCode N-Queens

题目描述

经典的N-皇后问题

思路

个人觉得就是DFS搜索问题。确定一个皇后的位置,然后跳至下一个位置再接着进行判断,直到放了N个皇后都符合题意,得到的就是想要的答案。

注意

由于一行只能放一个皇后,所以这道题可以按行的递增顺序来考虑,刚开始做的时候我直接按照每一个位置都来考虑,代码丑陋不说,最后也直接超时了。

代码
    vector<vector<string>> solveNQueens(int n) {
        // write your code here
        vector<vector<string>> res;
        vector<int> pos(n, -1); //pos[i] = j代表第i行的第j列放了一个Q,初始化为-1
        dfs(res, pos, 0, n, 0);

        return res;
    }

    void dfs(vector<vector<string>> &res, vector<int> &pos, int cnt, int n, int row) {
        if (cnt == n) {
            vector<string> tmp;
            for (int i = 0; i < n; i++) {
                string line = "";
                for (int j = 0; j < n; j++) {
                    if (j == pos[i]) line += 'Q';
                    else line += '.';
                }
                tmp.push_back(line);
            }
            res.push_back(tmp);
            return;
        }
        for (int j = 0; j < n; j++) {
            if (!isValid(pos, row, j)) continue;
                pos[row] = j;
                dfs(res, pos, cnt+1, n, row+1);
                pos[row] = -1; 
            }
    }

    bool isValid(vector<int> pos, int row, int col) {
        for (int i = 0; i < row; i++) {
            if (col == pos[i] || abs(row-i) == abs(pos[i]-col)) return false;
        }
        return true;
    }

LintCode N-Queue II

题目描述

N-Queue是让返回最后的结果,此题就更简单,只让返回最后的结果的个数。

代码
    int totalNQueens(int n) {
        // write your code here
        int res = 0;
        vector<int> pos(n, -1);
        dfs(pos, res, 0, 0);
        return res;
    }

    void dfs(vector<int> pos, int &res, int cnt, int row) {
        if (cnt == pos.size()) {
            res++;
            return;
        }

        for (int j = 0; j < pos.size(); j++) {
            if (!isValid(pos, row, j)) continue;
            pos[row] = j;
            dfs(pos, res, cnt+1, row+1);
            pos[row] = -1;
        }
    }

    bool isValid(vector<int> pos, int row, int col) {
        for (int i = 0; i < row; i++) {
            if (pos[i] == col || abs(row-i) == abs(pos[i] - col)) return false;
        }
        return true;
    }

LintCode Permutations

题目描述

给定一个数组,返回所有的全排列。

思路

递归加回溯的思路。每次选一个数,将其标记置为1表示访问过,然后跳进递归选择下一个数。

代码
    vector<vector<int>> permute(vector<int> &nums) {
        // write your code here
        vector<vector<int>> res;
        vector<int> tmp;
        vector<int> vis(nums.size(), 0);
        dfs(res, tmp, vis, nums, 0);
        return res;
    }

    void dfs(vector<vector<int>> &res, vector<int> tmp, vector<int> vis, 
        vector<int> nums, int cnt) {
        if (cnt == nums.size()) {
            res.push_back(tmp);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (vis[i] == 1) continue;
            vis[i] = 1;
            tmp.push_back(nums[i]);
            dfs(res, tmp, vis, nums, cnt+1);
            vis[i] = 0;
            tmp.pop_back();
        }
    }
挑战

不使用递归完成。

思路

注意到这样一个递推关系:假设已知了n-1位的全排列,那么我只需要对于n-1位的每一个排列,将第n个元素插入到不同的位置上,那么我就得到了n位的全排列。

代码
    vector<vector<int>> permute(vector<int> &nums) {
        // write your code here
        vector<vector<int>> res;
        if (nums.size() == 0) return vector<vector<int>> (1, vector<int>());

        vector<int> first{nums[0]};
        res.push_back(first);

        for (int i = 1; i < nums.size(); i++) {
            vector<vector<int>> newRes;
            for (int j = 0; j < res.size(); j++) {
                vector<int> curr = res[j];
                for (int k = 0; k < curr.size()+1; k++) {
                    vector<int> temp = curr;
                    temp.insert(temp.begin()+k, nums[i]);
                    newRes.push_back(temp);
                }
            }
            res = newRes;
        }
        return res;
    }

LintCode Premutations II

题目描述

上一道题的变种,给定的数组里面可能有重复的数字。仍然要求是用递归和非递归的方式来解决。

思路

简单粗暴:对于C++来说,set有去重的作用,所以用set来存储最后的结果,然后把set里的结果再存到vector里作为返回值。或者对于每一个排列都添加一个标志,出现一次标志就置为1,并将其压入最后的结果中。
其实仔细想想还会有更优的做法。什么情况下会有重复呢?我们先把原数组排序,这样重复的元素就会放在一起。我们只需要对重复元素的第一个元素进行递归即可。那么我们的判断条件就是如果当前元素等于前一个元素且前一个元素没有被访问过(实际上代表前一个元素已经进行过递归了,递归结束回溯置为0),那么就跳过该数。

代码
    vector<vector<int>> permuteUnique(vector<int> &nums) {
        // write your code here
        vector<vector<int>> res;
        vector<int> tmp;
        vector<int> vis(nums.size(), 0);
        sort(nums.begin(), nums.end());
        dfs(res, tmp, vis, nums, 0);
        return res;
    }

    void dfs(vector<vector<int>> &res, vector<int> tmp, vector<int> vis, 
        vector<int> nums, int cnt) {
        if (cnt == nums.size()) {
            res.push_back(tmp);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (i > 0 && nums[i] == nums[i-1] && vis[i-1] == 0) continue;
            if (vis[i] == 1) continue;
            vis[i] = 1;
            tmp.push_back(nums[i]);
            dfs(res, tmp, vis, nums, cnt+1);
            vis[i] = 0;
            tmp.pop_back();
        }
    }

LintCode Print Numbers By Recursion

题目描述

通过递归打印从1到n位最大数之间的所有数。
要求递归的深度最大为n。

思路

最大深度为n的话显然递归就是按照位数进行的,假设现在结果里存有1~99的所有数,那么当递归到第三层也就是三位数的时候,如何获得100~999的所有数呢?显然不难发现,对于100,200,300…,900,每一个都分别加上1~99,就得到了全部的三位数。所以第i位的递归需要进行9*(10^(i-1))次的计算,那么总共就需要9*(10^0+10^1+..+10^n-1)次计算。
搞不懂为什么直接的for循环会得到超时的结果。

代码

一顿操作猛如虎,一看战绩0-5.看了网上的题解,提交到LintCode仍然是n=6的时候超时,就很尴尬。

//这是九章上的答案,不过我提交好像也超时了。
    vector<int> numbersByRecursion(int n) {
        // write your code here
        vector<int> result;
        num(n, 0, result);
        return result;
    }
    void num(int n, int ans, vector<int> &result){
        if(n == 0){
            if(ans > 0){
                result.push_back(ans);
            }
            return;
        }
        for(int i = 0; i <= 9; i ++){
            num(n - 1, ans * 10 + i, result);
        }
    }

LintCode Subsets

题目描述

给定一个不含有重复元素的集合,返回其所有的子集。

思路

递归+回溯。每搜索一个元素,将其加入到集合中,每一个集合都是原有集合的子集,所以都把结果压入最终的答案中。同时计数器加一,当计数器达到总个数时返回。注意首先要把空集加入到最终集合中。

代码
    vector<vector<int>> subsets(vector<int> &nums) {
        // write your code here
        vector<vector<int>> res;
        int total = pow(2, nums.size());
        vector<int> tmp{};
        res.push_back(tmp);
        int cnt = 1;
        sort(nums.begin(), nums.end());
        dfs(res, tmp, nums, cnt, total, 0);
        return res;
    }

    void dfs(vector<vector<int>> &res, vector<int> tmp, vector<int> nums, int &cnt, int total, int idx) {
        if (cnt == total) {
            return;
        }
        for (int i = idx; i < nums.size(); i++) {
            tmp.push_back(nums[i]);
            res.push_back(tmp);
            cnt++;
            dfs(res, tmp, nums, cnt, total, i+1);
            tmp.pop_back();
        }
    }
挑战

不使用递归而使用迭代。

思路

这道题其实非递归的思路更简单一点,当然是看网上的大牛写的。如何找到这个全部的子集呢?假设集合[1,2,3],那么可以一位一位的遍历,首先由空集开始[],遍历到1,得到[]和[1];接着遍历到2,将2插入现有的集合中,得到两个新的子集[2],[1,2],那么此时一共就有得到4个子集[],[1],[2],[1,2]。按照这个思路一直遍历集合所有元素。

代码
    vector<vector<int>> subsets(vector<int> &nums) {
        // write your code here
        vector<vector<int>> res;
        res.push_back(vector<int>());//首先将空集存到最终的结果中
        int len = nums.size();
        sort(nums.begin(), nums.end());
        for (int i = 0; i < len; i++) {
            vector<vector<int>> tmp = res;
            for (auto ele : tmp) {
                ele.push_back(nums[i]);
                res.push_back(ele);
            }
        }
        return res;
    }

LintCode Subsets II

题目描述

假设前一道题的集合中有重复的元素,返回所有的子集合。

思路

最简单的思路莫过于加一个hash来判断某一子集合是否出现过,但这明显需要消耗空间资源。
首先来看非递归时如何处理重复问题。如果此刻遍历的元素等于之前的那个元素,那么我就只需要对上一次遍历时产生的新的子集进行遍历,而之前产生的子集合则不需要遍历,那么就可以避免重复的问题。
至于递归时,只需要在从递归函数中退出来之后进行回溯处理的时候,如果下一元素等于当前元素,那么就跳过该元素即可。

代码
    vector<vector<int>> subsetsWithDup(vector<int> &nums) {
        // write your code here
        if (nums.size() == 0 ) return {};
        vector<vector<int>> res;
        res.push_back(vector<int>());
        sort(nums.begin(), nums.end());
        int last = 0; //last指针指向下一次遍历的起始位置,如果当前元素不等于前一元素就置为0
        for (int i = 0; i < nums.size(); i++) {
            if (!(i > 0 && nums[i] == nums[i-1])) {
                last = 0;
            }
            vector<vector<int>> tmp = res;
            for (int j = tmp.size()-1; j >= last; j--) {
                tmp[j].push_back(nums[i]);
                res.push_back(tmp[j]);
            }
            last = tmp.size();
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值