leetcode解题思路分析(三十三)235—241题

  1. 二叉搜索树的最近公共祖先
    给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

很简单的一道题,递归求解:对于二叉搜索树,左子树<根<右子树,由此可以判断两个节点分别位于两边还是同一边。如果在两边则该根为最近公共祖先,否则继续找下去

/**
 * 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) {
        if (root->val == p->val || root->val == q->val)
        {
            return root;
        }
        if ((root->val > p->val && root->val < q->val)
        || (root->val < p->val && root->val > q->val))
        {
            return root;
        }

        if (root->val > p->val && root->val > q->val)
        {
            return lowestCommonAncestor(root->left, p, q);
        }
        else
        {
            return lowestCommonAncestor(root->right, p, q);
        }
    }
};
  1. 二叉树的最近公共祖先
    给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

本题相对于上题会稍微麻烦一些,因为没有二叉搜索树的特性。所以才去递归的时候需要从最底端开始,如果满足某根左边子树右边子树各有一个节点,则返回该根节点。否则说明两个节点必有一个是公共祖先。

class Solution {
public:
    TreeNode* ans;
    bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr) return false;
        bool lson = dfs(root->left, p, q);
        bool rson = dfs(root->right, p, q);
        if ((lson && rson) || ((root->val == p->val || root->val == q->val) && (lson || rson))) {
            ans = root;
        } 
        return lson || rson || (root->val == p->val || root->val == q->val);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        dfs(root, p, q);
        return ans;
    }
};

另一种做法是记录每一个节点的父节点,然后对两个节点来找寻共有的根节点

class Solution {
public:
    unordered_map<int, TreeNode*> fa;
    unordered_map<int, bool> vis;
    void dfs(TreeNode* root){
        if (root->left != nullptr) {
            fa[root->left->val] = root;
            dfs(root->left);
        }
        if (root->right != nullptr) {
            fa[root->right->val] = root;
            dfs(root->right);
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        fa[root->val] = nullptr;
        dfs(root);
        while (p != nullptr) {
            vis[p->val] = true;
            p = fa[p->val];
        }
        while (q != nullptr) {
            if (vis[q->val]) return q;
            q = fa[q->val];
        }
        return nullptr;
    }
};

  1. 删除链表中的节点
    请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。

本题比较有意思的地方在于单向链表不知道前面节点如何删除节点,做法是将当前节点赋值为下一个节点,删除下一个节点即可

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void deleteNode(ListNode* node) {

        if (node == NULL)
            return;

        if (node->next == NULL)
        {
            node = NULL;
            return;
        }

        ListNode *tmp = node->next;
        node->val = node->next->val;
        node->next = node->next->next;
        delete tmp;
        return;
    }
};
  1. 除自身以外数组的乘积
    给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

本题解法限制了不能用除法,因此可以转变思路:用两个数组分别存储当前位置的左边元素乘积和右边元素乘积,二者相乘即可。进一步可以简化:利用输出数组先存储左边元素,然后动态获取右边元素直接相乘赋值即可

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int length = nums.size();
        vector<int> answer(length);

        // answer[i] 表示索引 i 左侧所有元素的乘积
        // 因为索引为 '0' 的元素左侧没有元素, 所以 answer[0] = 1
        answer[0] = 1;
        for (int i = 1; i < length; i++) {
            answer[i] = nums[i - 1] * answer[i - 1];
        }

        // R 为右侧所有元素的乘积
        // 刚开始右边没有元素,所以 R = 1
        int R = 1;
        for (int i = length - 1; i >= 0; i--) {
            // 对于索引 i,左边的乘积为 answer[i],右边的乘积为 R
            answer[i] = answer[i] * R;
            // R 需要包含右边所有的乘积,所以计算下一个结果时需要将当前值乘到 R 上
            R *= nums[i];
        }
        return answer;
    }
};


  1. 滑动窗口最大值
    给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
    返回滑动窗口中的最大值。

使用队列保存窗口,变量的最前端(也就是 window.front())是此次遍历的最大值的下标,当我们遇到新的数时,将新的数和双项队列的末尾(也就是window.back())比较,如果末尾比新数小,则把末尾扔掉,直到该队列的末尾比新数大或者队列为空的时候才停止,做法有点像使用栈进行括号匹配。

class Solution {
public:
	vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if (k == 0) return {};
		vector<int> res;
		deque<size_t> window;
		/*Init K integers in the list*/
		for (size_t i = 0; i < k; i++) {
			while (!window.empty()  && nums[i] > nums[window.back()]) {
				window.pop_back();
			}
			window.push_back(i);
		}
		res.push_back(nums[window.front()]);
		/*End of initialization*/
		for (size_t i = k; i < nums.size(); i++) {
			if (!window.empty() && window.front() <= i - k) {
				window.pop_front();
			}
			while (!window.empty() && nums[i] > nums[window.back()]) {
				window.pop_back();
			}
			window.push_back(i);
			res.push_back(nums[window.front()]);
		}
		return res;
	}
};


  1. 搜索二维矩阵2
    编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
    每行的元素从左到右升序排列。
    每列的元素从上到下升序排列。

本题是典型的二分查找,但是由于已排序,除了二分查找外还可以通过对角线的原理来完成:从右上角或者左下角开始,按照大小规则移动行列,直至找到或者出界

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size() == 0||matrix[0].size() == 0)
        {
            return  false;
        }
        int  curx = matrix.size()-1,cury = 0;
        while(curx >= 0 &&cury < matrix[0].size())
        {
            if(target > matrix[curx][cury])
            {
                cury++;
            }
            else  if(target < matrix[curx][cury])
            {
                curx--;
            }
            else
            {
                return  true;
            }
        }
        return  false;
    }
};


  1. 为运算表达式设计优先级
    给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。

本题可以采用分治算法,将表达式拆解为由符合分割开的各个部分,然后按顺序求不同的解。另一种做法是视为动态规划求解:用两个 list,一个保存了所有数字,一个保存了所有运算符。dp[i][j]表示第 i 到第 j 个数字(从 0 开始计数)范围内的表达式的所有解。

class Solution {
public:
    vector<int> diffWaysToCompute(string input) {
        int index = 0;
        int num = 0;
        while(index < input.size() && isdigit(input[index]))
            num = num * 10 + input[index++] - '0';
        if(index == input.size()){
            hash[input] = {num};
            return {num};
        }
        vector<int> ans;
        for(int i = 0; i < input.size(); i++){
            if(isOp(input[i])){
                string s1 = input.substr(0,i);
                string s2 = input.substr(i);
                vector<int> result1, result2;
                if(!hash.count(s1))
                    result1 = diffWaysToCompute(input.substr(0,i));
                else
                    result1 = hash[s1];
                if(!hash.count(s2))
                    result2 = diffWaysToCompute(input.substr(i+1));
                else
                    result2 = hash[s2];
                for(int r1 : result1){
                    for(int r2 : result2){
                        ans.push_back(calculate(r1,input[i],r2));
                    }
                }
            }
        }
        hash[input] = ans;
        return ans;
    }

    bool isOp(const char& c){
        return c == '+' || c == '-' || c == '*';
    }

    int calculate(const int& num1, const char& op, const int& num2){
        if(op == '+')
            return num1 + num2;
        else if(op == '-')
            return num1 - num2;
        else
            return num1 * num2;
    }
private:
    unordered_map<string,vector<int>> hash;
};


你可能感兴趣的:(面试,数据结构,算法,链表)