文章目录
1. 序列化和反序列化二叉搜索树
序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。
设计一个算法来序列化和反序列化 二叉搜索树 。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。
编码的字符串应尽可能紧凑。
1.1 前序遍历
对于BST来说,只需要给出它的前序遍历,就能得到它的中序遍历,为什么呢?因为BST的中序遍历是有序的,所以对前序遍历结果排序就是中序遍历。
所以,我们需要求出先序或者后序中的一种,然后在解码的时候,进行递归求解左子树和右子树和右子树即可。
怎么递归求解呢?以先序为例:
如果有左儿子,那么序列中下一个节点的值小于当前结点的值,对下一个节点找左儿子,直到找不到为止,然后找右儿子,如果下一个节点的值大于当前结点的值,那么是右儿子,再对右儿子找左右儿子……
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "";
List<Integer> ls = new ArrayList<Integer>();
preOrder(root, ls);
String str = ls.toString();
return str.substring(1, str.length() - 1);
}
private void preOrder(TreeNode root, List<Integer> list) {
if (root == null) return;
list.add(root.val);
preOrder(root.left, list);
preOrder(root.right, list);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if (data.isEmpty()) return null;
String[] array = data.split(", ");
Queue<Integer> qu = new LinkedList<Integer>();
for (String tmp : array) {
qu.add(Integer.parseInt(tmp));
}
return construct(-1, 100001, qu);
}
private TreeNode construct(int low, int high, Queue<Integer> qu) {
if (qu.isEmpty() || qu.peek() < low || qu.peek() > high) {
return null;
}
int val = qu.peek();
qu.remove();
TreeNode root = new TreeNode(val);
root.left = construct(low, val, qu);
root.right = construct(val, high, qu);
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// String tree = ser.serialize(root);
// TreeNode ans = deser.deserialize(tree);
// return ans;
2. 单词拆分
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
2.1 动态规划
本次dp的状态是f[i],判断第0到第i个字符组成的子字符串可以被拆分成字典中所有的单词。
如何判断f[i]是true还是false呢?
从0到i-1,如果有f[j] == true && j ~ i 的子字符串能够在字典中找到对应的单词,说明f[i]为真,如果找不到这么一个f[j],那么就为假
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> dict;
for (auto tmp : wordDict) {
dict.emplace(tmp);
}
int n = s.size();
vector<bool> dp(n + 1);
dp[0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && (dict.find(s.substr(j, i - j))) != dict.end()) {
dp[i] = true;
break;
}
}
}
return dp[n];
}
};
3. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
3.1 本人的垃圾方法
根据图推知,如果i点比它左边的点都高,或者比它右边的点都高,那么它不会接到雨水;反之,他坑定会接到雨水……
所以,怎么求i点左边最高点,和i点右边最高点呢?
动态规划,做左边为例,dp[i]记为到第i点的最高点,那么递推式:
- d p [ i ] = m a x ( d p [ i − 1 ] , h e i g h t [ i ] ) dp[i] = max(dp[i - 1], height[i]) dp[i]=max(dp[i−1],height[i])
右边同样的道理,这样扫描两边,我们就有了判断某一点是否能接到雨水了……
那么如果会接到雨水,接多少?
接离他最近的左非接水点和右非接水点中的最小值……
怎么求左非接水点和右非接水点呢?
向左向右扫描……
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n <= 2) return 0;
vector<bool> canGetRain(n, false);
vector<int> leftMost(n);
vector<int> rightMost(n);
leftMost[0] = height[0];
rightMost[n - 1] = height[n - 1];
for (int i = 1; i < n - 1; i++) {
leftMost[i] = max(leftMost[i - 1], height[i]);
}
for (int i = n - 2; i >= 1; i--) {
rightMost[i] = max(rightMost[i + 1], height[i]);
}
for (int i = 1; i < n - 1; i++) {
canGetRain[i] = height[i] < min(leftMost[i], rightMost[i]) ? true : false;
}
int ans = 0;
for (int i = 1; i < n - 1; i++) {
if (canGetRain[i]) {
ans += getCount(i, canGetRain, height) - height[i];
}
}
return ans;
}
int getCount(int i, vector<bool> canGetRain, vector<int> height) {
int left, right;
for (int j = i - 1; j >= 0; j--) {
if (!canGetRain[j]) {
left = j;
break;
}
}
for (int j = i + 1; j < height.size(); j++) {
if (!canGetRain[j]) {
right = j;
break;
}
}
return min(height[left], height[right]);
}
};
3.2 优秀解法
再想一想,最近左非接水点,和最近右非接水点,其实就是左边的最大值,和右边的最大值……
所以接水量可以简单地写成一句话:
- W a t e r [ i ] = m i n ( l e f t M o s t [ i ] , r i g h t M o s t [ i ] ) − h e i g h t [ i ] Water[i] = min(leftMost[i], rightMost[i]) - height[i] Water[i]=min(leftMost[i],rightMost[i])−height[i]
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n == 0) {
return 0;
}
vector<int> leftMax(n);
leftMax[0] = height[0];
for (int i = 1; i < n; ++i) {
leftMax[i] = max(leftMax[i - 1], height[i]);
}
vector<int> rightMax(n);
rightMax[n - 1] = height[n - 1];
for (int i = n - 2; i >= 0; --i) {
rightMax[i] = max(rightMax[i + 1], height[i]);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans += min(leftMax[i], rightMax[i]) - height[i];
}
return ans;
}
};
总结
应该是第一次做出困难题,虽然性能只干掉了5%的人,但好歹是做出来了的……
今天这两道题的思路都不算特别好想,如果是第一次写,很难马上列出递推式吧,只能慢慢想,慢慢观察,算是积累经验吧……