Leetcodeの問題解決のアイデアの分析(37)307-316

  1. エリアと検索配列は変更できます

非常に簡単な質問

class NumArray {
    
    
        vector<int> m_sum;
        vector<int> m_nums;
public:
    NumArray(vector<int>& nums) {
    
    
        m_nums = nums;
        m_sum = nums;

        if (nums.size() == 0) 
            return;

        for (int i = 1; i < nums.size(); i++)
        {
    
    
            m_sum[i] += m_sum[i - 1];
        }
    }
    
    void update(int i, int val) {
    
    
        int dif = val - m_nums[i];
        m_nums[i] = val;

        for (int j = i; j < m_sum.size(); j++)
        {
    
    
            m_sum[j] += dif;
        }
    }
    
    int sumRange(int i, int j) {
    
    
        if (i == 0)
            return m_sum[j];
        else
            return m_sum[j] - m_sum[i - 1];
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(i,val);
 * int param_2 = obj->sumRange(i,j);
 */
  1. 株を売買するのに最適な時期には、凍結期間が含まれます。
    整数配列が指定された場合、i番目の要素はi番目の日の株価を表します。
    最大利益を計算するアルゴリズムを設計します。次の制約の下で、できるだけ多くのトランザクションを完了することができます(株の購入と売却を複数回):
    同時に複数のトランザクションに参加することはできません(前の株を売却してから再度購入する必要があります)。
    在庫を販売した後、翌日に購入することはできません(つまり、凍結期間は1日です)。

古典的な動的プログラミングアプリケーション。まず、1日目の状態とi-1日目の状態の関係を書き留めてから、次元削減の最適化を実行します。

class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
        if (prices.empty()) {
    
    
            return 0;
        }

        int n = prices.size();
        int f0 = -prices[0];
        int f1 = 0;
        int f2 = 0;
        for (int i = 1; i < n; ++i) {
    
    
            int newf0 = max(f0, f2 - prices[i]);
            int newf1 = f0 + prices[i];
            int newf2 = max(f1, f2);
            f0 = newf0;
            f1 = newf1;
            f2 = newf2;
        }

        return max(f1, f2);
    }
};
  1. 最小高さツリー
    ツリーの特徴を持つ無向グラフの場合、ルートとして任意のノードを選択できます。したがって、グラフはツリーになる可能性があります。考えられるすべてのツリーの中で、高さが最小のツリーを最小高さツリーと呼びます。このようなグラフを前提として、すべての最小高さツリーを検索し、それらのルートノードを返す関数を記述します。

この質問の主なポイントは、最小の高さツリーがグラフのリーフをルートとして使用する場合、リーフへの他のポイントは少なくとも1 +リーフの親ノードであるということです。リーフノードは私たちのオプションではないことがわかります。すべての葉ノードから中央に近づき、最短距離との交点となる交点まで、レイヤーごとに再帰的にピーリングします。深度に応じて、奇数と偶数は1つの結果または2つの結果になります。

class Solution {
    
    
public:
vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
    
    
	if (n == 1)
		return {
    
     0 };
	else if (n == 2)
		return{
    
     0,1 };

	vector<int> indegree(n,0);//入度数组,并初始化
	vector<int> v;
	vector<vector<int>> graph(n,v);//图形表示,并初始化
	for (int i = 0; i < edges.size(); i++)//构造图与入度数组:无向图,两个点都要处理
	{
    
    
		graph[edges[i][0]].push_back(edges[i][1]);
		graph[edges[i][1]].push_back(edges[i][0]);
		indegree[edges[i][0]]++;
		indegree[edges[i][1]]++;
	}
	queue<int> myqueue;//装载入度为1的queue
	for (int i = 0; i < n; i++)
	{
    
    
		if (indegree[i] == 1)
			myqueue.push(i);
	}
	int cnt = myqueue.size();//!!令cnt等于myqueue.size(),一次性将入度为1的点全部删去。
	while (n>2)
	{
    
    
		n -= cnt;//一次性将入度为一的点全部删去!!不能一个一个删!
		while (cnt--)
		{
    
    
			int temp = myqueue.front();
			myqueue.pop();
			indegree[temp] = 0;
			//更新temp的邻接点:若temp临接点的入度为1,则将其放入queue中。
			for (int i = 0; i < graph[temp].size(); i++)
			{
    
    
				if (indegree[graph[temp][i]] != 0)
				{
    
    
					indegree[graph[temp][i]]--;
					if (indegree[graph[temp][i]] == 1)//放在这里做!只判断邻接点。
						myqueue.push(graph[temp][i]);
				}
				
			}
		}
		cnt = myqueue.size();
	}
	vector<int> result;
	while (!myqueue.empty())
	{
    
    
		result.push_back(myqueue.front());
		myqueue.pop();
	}
	return result;
}
};

  1. 風船を突き出す
    0からn-1までの番号が付けられたn個の風船があり、各風船には番号が付けられ、これらの番号は配列numsに格納されます。

ここで、すべてのバルーンをポップする必要があります。バルーンiをポップすると、nums [左] * nums [i] * nums [右]コインを獲得できます。ここで、左と右はiに隣接する2つのバルーンのシリアル番号を表します。バルーンiをバーストすると、左と右のバルーンが隣接するバルーンになることに注意してください。
取得できるコインの最大数を見つけます。

古典的な動的プログラミングの問題は、トップダウンまたはボトムアップの考え方で解決できます。コアアイデア:バルーンを突き出す場合は、最初に最小の内側のバルーンを突き、最後に端を突きます。バルーンを追加する場合は、大きい方から追加します

class Solution {
    
    
public:
    int maxCoins(vector<int>& nums) {
    
    
        int n = nums.size();
        vector<vector<int>> rec(n + 2, vector<int>(n + 2));
        vector<int> val(n + 2);
        val[0] = val[n + 1] = 1;
        for (int i = 1; i <= n; i++) {
    
    
            val[i] = nums[i - 1];
        }
        for (int i = n - 1; i >= 0; i--) {
    
    
            for (int j = i + 2; j <= n + 1; j++) {
    
    
                for (int k = i + 1; k < j; k++) {
    
    
                    int sum = val[i] * val[k] * val[j];
                    sum += rec[i][k] + rec[k][j];
                    rec[i][j] = max(rec[i][j], sum);
                }
            }
        }
        return rec[0][n + 1];
    }
};


  1. 超醜い数
    n番目の超醜い数を見つけるプログラムを書いてください。
    超醜い数とは、すべての素因数が素数の正の整数、つまり長さkの素数のリストであることを意味します。

1)キューの要素のソース:最上位要素*素数のリスト。新しく生成された要素をキューに入れます。ポップアップするn番目の要素は、必要な非常に醜い数です。ただし、ポップされた要素は必ずしもn番目に小さい素数ではないため、ポップする前にソートする必要があります。
2)n番目の素数の場合、キューは小さいものから大きいものへ順序付けて配置する必要があるため、小さいトップパイルを使用します。

class Solution {
    
    
public:
    int nthSuperUglyNumber(int n, vector<int> &primes)
    {
    
    
        priority_queue<long long, vector<long long>, greater<long long>> buff;
        unordered_set<long long> primeset;

        buff.push(1);
        primeset.insert(1);
        long long i = 1;
        int count = 0;
        while (count < n) {
    
    
            count++;
            i = buff.top();
            buff.pop();
            for (long long prime : primes) {
    
    
                long long next = i * prime;
                if (next<=INT32_MAX && !primeset.count(next)) {
    
    
                    buff.push(next);
                    primeset.insert(next);
                }
            }
        }

        return i;
    }
};

  1. 現在の要素よりも少ない右側の要素の数を計算します。
    整数の配列numsを指定すると、必要に応じて新しい配列の数を返します。配列countsには次の特性があります。counts[i]の値は、nums [i]よりも小さいnums [i]の右側の要素の数です。

この問題は、古典的な問題であるツリー配列によって解決されます。各バケットはいくつかの値を格納し、その後蓄積することができることが理解できます

class Solution {
    
    
private:
    int lowbit(int x){
    
    
        return x & (-x);
    }
    void update(int i, vector<int>&C){
    
    
        while (i < C.size()) {
    
    
            C[i]++;
            i += lowbit(i);
        }
    }
    void query(int i, int j, vector<int>&C, vector<int>&counts){
    
    
        while (i >= 1) {
    
    
            counts[j] += C[i];
            i -= lowbit(i);
        }
    }
public:    
    vector<int> countSmaller(vector<int>& nums) {
    
    
        vector<int>counts(nums.size(), 0);
        if (nums.size() < 1) {
    
    
            return counts;
        }
        
        vector<int>N(nums);
        // Sort and unique
        sort(N.begin(), N.end());
        int slow = 1;
        int fast = 1;
        while(fast< N.size()) {
    
    
            if (N[fast] != N[slow - 1]) {
    
    
                N[slow] = N[fast];
                slow++;
                fast++;
            }else{
    
    
                fast++;
            }
        }
        N.resize(slow);
        
        // key: number, value: i
        map<int, int>m;
        for (int j = 1; j < 1 + N.size(); ++j) {
    
    
            m[N[j - 1]] = j;
        }
        
        // traverse
        vector<int>C(N.size() + 1, 0); //  C[i] is necessary, but A[i] not
        int i;
        for (int j = nums.size() - 1; j >= 0; --j) {
    
    
            i = m[nums[j]];
            update(i, C);
            if (i != 1) {
    
    
                query(i - 1, j, C, counts);
            }else{
    
    
                counts[j] = 0;
            }
        }
        return counts;
    }
};

  1. 繰り返し文字を削除し
    、あなたに小文字のみを含む文字列を与えます。各文字は一度だけ表示されるように、文字列の繰り返し文字を削除してください。返される結果の辞書式順序が最小になるようにする必要があります(他の文字の相対位置を乱さないために必要です)。

結果の辞書式順序を最小限にしたい場合は、小さい要素をできるだけ前に配置する必要があります。stkスタックの最上部にある要素が現在トラバースされている要素より大きい場合、上記の原則に従って、条件が許せばこの状況は回避する必要があります。トピックの要件(重複除外)に従って、後でスタックの最上部に要素がある場合のみスタックの一番上の要素を削除するため(逆の順序を減らす)。後でスタックの一番上にそのような要素がない場合は、次の要素よりも大きい場合でも、ここにのみ保持できます。

class Solution {
    
    
public:
    string removeDuplicateLetters(string s) {
    
    
        string stk;
        size_t i = 0;
        for(size_t i = 0;i < s.size(); ++i)
        {
    
    
            if(stk.find(s[i]) != string::npos) continue;
            while(!stk.empty()&& stk.back() > s[i]&& 
                s.find(stk.back(), i) != string::npos)
                stk.pop_back();
            stk.push_back(s[i]);
        }
        return stk;
    }
};

おすすめ

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