9.力扣2018年常見編程題總結(動態規劃)

1.找到給定字符串(由小寫字符組成)中的最長子串 T , 要求 T 中的每一字符出現次數都不少於 k 。輸出 的長度。

輸入: s = "ababbc", k = 2

輸出: 5

最長子串爲 "ababb" ,其中 'a' 重複了 2 次, 'b' 重複了 3 次。

解:當x字符的次數小於k時則不能最終結果包括x,因此將x分爲左邊和右邊進行遞歸尋找

代碼:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
using namespace std;

class Solution {
public:
	int ans = 0;
	void process(string s, int k)
	{
		//vis用來統計每個字符出現的次數
		int vis[26] = { 0 };
		for (int i = 0; i < s.size(); ++i)
			vis[s[i] - 'a']++;

		//對x左邊的字符串進行判斷
		int l = 0, flag = 1;
		for (int i = 0; i < s.size(); ++i)
		{
			if (vis[s[i] - 'a'] && vis[s[i] - 'a'] < k)
			{
				if (i > l) process(s.substr(l, i - l), k);
				flag = 0;
				l = i + 1;
			}
		}
		if (!flag && s.size() > l) process(s.substr(l, s.size() - 1), k);
		if (flag) ans = max((int)s.size(), ans);
	}
	int longestSubstring(string s, int k) {
		process(s, k);
		return ans;
	}
};

int main()
{
	Solution s1;
	string s = "ababbc";
	cout << s1.longestSubstring(s,2);
	return 0;
}

2.給定一個非空二叉樹,返回其最大路徑和。

本題中,路徑被定義爲一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。

輸入: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

輸出: 42

解:給定一個非空節點,最終路徑經過這個節點有4種情況:1.只有該節點本身(左右子樹的路徑都是負數);2.該節點+左子樹路徑;3.該節點+右子樹路徑;4.該節點+左子樹路徑+右子樹路徑。其中1,2,3都可以作爲子樹路徑和向上延伸,而4則不行。

代碼:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
using namespace std;

struct TreeNode {
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
	int result = INT_MIN;
	int maxPathSum(TreeNode* root)
	{
		getPath(root);
		return result;//result存儲搜索過路徑的最大值
	}
	int getPath(TreeNode* node) {
		if (node == NULL) return 0;
		int left = getPath(node->left);	//向左不斷遞歸
		int right = getPath(node->right);	//向右不斷遞歸
		int tmp = max(max(left + node->val, right + node->val), node->val);//當前結點的最大路徑值
		result = max(result, max(tmp, left + right + node->val));//歷史所有結點的最大路徑值
		return tmp;
	}
};

int main()
{
	Solution s1;
	TreeNode *root = new TreeNode(2);
	root->left = new TreeNode(1);
	root->right = new TreeNode(3);
	cout << s1.maxPathSum(root);
	return 0;
}

3.

給定一個未排序的整數數組,找出最長連續序列的長度。

要求算法的時間複雜度爲 O(n)

示例:

輸入: [100, 4, 200, 1, 3, 2]

輸出: 4

解釋: 最長連續序列是 [1, 2, 3, 4]。它的長度爲 4。

解:先將數組插入到無序集合中,利用count方法來進行元素尋找。從第i個元素開始尋找,且保證i-1元素沒有找到,每找到一個元素,則curmax++

代碼:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
public:
	int longestConsecutive(vector<int>& nums) {
		unordered_set<int> dict;
		for (auto i : nums)
			dict.insert(i);//將元素插入到集合中
		int res = 0;
		int cur, curMax;
		for (auto i : nums) {
			if (dict.count(i - 1) == 0) {//從當前元素開始尋找,且前一個元素沒有找到
				cur = i;
				curMax = 1;//找到的連續個數
				while (dict.count(cur + 1)) {//找下一個
					cur++;
					curMax++;
				}
				res = max(res, curMax);//歷史最大值
			}
		}
		return res;
	}
};

int main()
{
	Solution s1;
	vector<int> a = { 100, 4, 200, 1, 3, 2 };
	cout << s1.longestConsecutive(a);
	return 0;
}

4.你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定一個代表每個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的情況下,能夠偷竊到的最高金額。

示例 1:

輸入: [1,2,3,1]

輸出: 4

解釋: 偷竊 1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3)。   偷竊到的最高金額 = 1 + 3 = 4 。

解:通過維護dp數組來存放當前累計的最大值。

代碼:

#include"pch.h"
#include <iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<unordered_set>
using namespace std;

class Solution {
public:
	int rob(vector<int>& nums) {
		int length = nums.size();
		if (length == 0) return 0;
		if (length == 1) return nums[0];
		if (length == 2)	return max(nums[0], nums[1]);
		int *dp = (int *)malloc(length * sizeof(int));
		dp[0] = nums[0]; dp[1] = nums[1]; dp[2] = dp[0] + nums[2];
		int k = max(dp[1],dp[2]);
		for (int i = 3; i < length; i++)
		{
			dp[i] = max(dp[i - 2] , dp[i-3])+nums[i];//動態方程
			k = max(k, dp[i]);
		}
		return k;
	}
};

int main()
{
	Solution s1;
	vector<int> a = { 2,1,1,2 };
	cout << s1.rob(a);
	return 0;
}

5.給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。

示例 1:

輸入: n = 12

輸出: 3

解釋: 12 = 4 + 4 + 4.

解:利用動態規劃進行求解,先把小於n的完全平方數進行統計,放置在dp表中,之後進行最小個數的統計。從1到n遍歷求解最小組成個數,再對每個數遍歷小於其的所有完全平方數,最小組成個數的狀態轉移方程爲:dp[i] = min(dp[i], dp[i - j * j] + 1)

代碼:

using namespace std;

class Solution {
public:
	int numSquares(int n) {
		vector<int> dp(n + 1, INT_MAX);
		for (int i = 1; i * i <= n; i++)
			dp[i * i] = 1;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j * j < i; j++)
				dp[i] = min(dp[i], dp[i - j * j] + 1);
		return dp[n];
	}
};

int main()
{
	Solution s1;
	cout << s1.numSquares(13);
	return 0;
}

6.給定一個無序的整數數組,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18]

輸出: 4

解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4

解:利用動態規劃維護一張dp表,記錄第i個數下遞增的總個數。

using namespace std;

class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {
		int length = nums.size();
		if (length == 0)	 return 0;
		int * dp = new int[length];
		for (int i = 0; i < length; i++)
			dp[i] = 1;
		for (int i = 0; i < length; i++)
		{
			int current = nums[i];
			for (int j = 0; j < i; j++)
			{
				if (current > nums[j])//遞增才進行記錄
					dp[i] = max(dp[j] + 1, dp[i]);//每次遍歷記錄找到的最大個數
			}
		}
		int res = dp[0];
		for (int i = 0; i < length; i++)
		{
			res = max(res, dp[i]);
		}
		return res;
	}
};

int main()
{
	Solution s1;
	vector<int> ss = { 10,9,2,5,3,7,101,18 };
	cout << s1.lengthOfLIS(ss);
	return 0;
}

7.給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1

示例 1:

輸入: coins = [1, 2, 5], amount = 11

輸出: 3

解釋: 11 = 5 + 5 + 1

示例 2:

輸入: coins = [2], amount = 3

輸出: -1

解:利用動態規劃從amount=0開始對硬幣進行尋找。

代碼:

using namespace std;

class Solution {
public:
	int coinChange(vector<int>& coins, int amount) {
		int* dp = new int[amount + 1]();
		for (int i = 1; i < amount + 1; i++)
			dp[i] = 1000;
		dp[0] = 0;
		for (int i = 0; i <= amount; i++)
		{
			for (int j = 0; j < coins.size(); j++)
			{
				if (i - coins[j] >= 0)
					if (dp[i] > dp[i - coins[j]] + 1)
						dp[i] = dp[i - coins[j]] + 1;
			}
		}
		if (dp[amount] >= 1000)
			return -1;
		else
			return dp[amount];
	}
};

int main()
{
	Solution s1;
	vector<int> ss = { 186,419,83,408};
	cout << s1.coinChange(ss, 6249);
	return 0;
}

8.給定一個整數矩陣,找出最長遞增路徑的長度。

對於每個單元格,你可以往上,下,左,右四個方向移動。 你不能在對角線方向上移動或移動到邊界外(即不允許環繞)。

示例 1:

輸入: nums = [ [9,9,4], [6,6,8], [2,1,1] ]

輸出: 4

解釋: 最長遞增路徑爲 [1, 2, 6, 9]

using namespace std;

class Solution {
	vector<vector<int>> pos{ {-1,0},{1,0},{0,-1},{0,1} };
public:
	int longestIncreasingPath(vector<vector<int>>& matrix) {
		if (matrix.empty() || matrix[0].empty()) return 0;
		//創建一個二維表
		vector<vector<int>> visited(matrix.size(), vector<int>(matrix[0].size(), 0));
		int longest = 0;//記錄最長路徑
		int current = 1;
		for (int i = 0; i < matrix.size(); i++)
		{
			for (int j = 0; j < matrix[0].size(); j++)
			{
				if (visited[i][j] == 0)
					dfs(matrix, visited, current, longest, i, j);//深度優先搜索
			}
		}
		return longest;
	}

	void dfs(vector<vector<int>>& matrix, vector<vector<int>>& visited, int &current, int &longest, int i, int j)
	{
		visited[i][j] = 1;
		int oldlen = current;
		int maxlen = current;
		for (int m = 0; m < 4; m++)
		{
			int x = i + pos[m][0];
			int y = j + pos[m][1];//向四個方向尋找
			if (x < 0 || y < 0 || x >= matrix.size() || y >= matrix[0].size() || matrix[x][y] <= matrix[i][j]) continue;
			else if (visited[x][y])
				maxlen = max(maxlen, oldlen + visited[x][y]);
			else
			{
				current++;
				dfs(matrix, visited, current, maxlen, x, y);
				maxlen = max(maxlen, current);
				current = oldlen;
			}
		}
		visited[i][j] = max(maxlen - oldlen + 1, visited[i][j]);
		longest = max(longest, maxlen);
	}
};

int main()
{
	Solution s1;
	vector<vector<int>> ss = { {9,9,4},{6,6,8 },{2,1,1 }};
	cout << s1.longestIncreasingPath(ss);
	return 0;
}

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章