Leetcodeの問題解決アイデア分析(33)235-241


  1. バイナリ検索ツリーの最も近い共通の祖先バイナリ検索ツリーを指定すると、ツリー内の指定された2つのノードの最も近い共通の祖先を見つけます。

非常に単純な問題、再帰的な解決策:二分探索ツリーの場合、左サブツリー<ルート<右サブツリーでは、2つのノードが両側または同じ側にあるかどうかを判断できます。両側にある場合、ルートは最も近い共通の祖先です。それ以外の場合は、検索を続行します

/**
 * 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. バイナリツリーの最も近い共通の祖先バイナリツリーが与えられた場合、ツリー内の指定された2つのノードの最も近い共通の祖先を見つけます。

バイナリサーチツリーの機能がないため、この質問は前の質問に比べて少し面倒です。したがって、再帰を行う場合は、下から開始する必要があり、特定のルートの左側のサブツリーと右側のサブツリーにノードがあることを確認すると、ルートノードが返されます。それ以外の場合、2つのノードの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;
    }
};

別のアプローチは、各ノードの親ノードを記録し、2つのノードの共通ルートノードを見つけることです

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の整数配列numを与えます。ここで、n> 1は、出力配列の出力を返します。ここで、output [i]は、nums [i]を除くnumsの残りの要素の積に等しくなります。

この問題の解決策は、除算を使用できないことを制限するので、考えを変えることができます。2つの配列を使用して、左側の要素の積と右側の要素の積を現在の位置に格納し、その2つを乗算します。さらに単純化することができます。出力配列を使用して最初に左の要素を格納し、次に動的に右の要素を取得して、割り当てを直接乗算します。

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番号を表示できます。スライディングウィンドウは、一度に1つの位置だけ右に移動します。
    スライディングウィンドウの最大値を返します。

キューを使用してウィンドウを保存します。変数のフロントエンド(つまり、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次元マトリックスの検索2
    mxnマトリックスマトリックスでターゲット値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. 演算式の優先順位を設計する
    数値と演算子を含む文字列を指定して、式に括弧を追加し、演算の優先順位を変更してさまざまな結果を見つけます。すべての可能な組み合わせの結果を与える必要があります。有効な演算子には、+、-、および*が含まれます。

この質問では、分割統治アルゴリズムを使用して、式を偶然で区切られた部分に分解し、さまざまなソリューションを順番に見つけることができます。別のアプローチは、それを動的プログラミングソリューションとして扱うことです。すべての数値用とすべての演算子用の2つのリストを使用します。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;
};


おすすめ

転載: blog.csdn.net/u013354486/article/details/107226044