搜索与回溯算法(困难)
Q1 序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
主要思路:利用队列实现层序遍历,注意节点间的划分和空结点的处理。思路比较好理解,但是写起来比较麻烦。
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if(!root)
return "[]";
string ret="[";
queue<TreeNode*> st;
st.push(root);
while(!st.empty())
{
TreeNode* cur=st.front();
st.pop();
if(cur==NULL)
{
ret+="null";
}
else
{
ret+=to_string(cur->val);
st.push(cur->left);
st.push(cur->right);
}
if(!st.empty())
ret+=",";
}
ret+="]";
return ret;
}
void splitt(string &s,const string &c,vector<string> &v)
{
string::size_type pos1, pos2; //size_type 为string类的配套类型,默认为unsigned类型
pos2 = s.find(c);
pos1 = 0;
while(string::npos != pos2) //npos可以表示string的结束位置,string::type_size 类型。
{
v.push_back(s.substr(pos1, pos2-pos1)); //分隔符之间的内容
pos1 = pos2 + c.size(); //下一次分割的起始位置
pos2 = s.find(c, pos1);
}
if(pos1 != s.length())
v.push_back(s.substr(pos1)); //剩余字串
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data=="[]")
return NULL;
data.erase(data.begin());
data.erase(--data.end());
//将字符串章节点信息提取
vector<string> vals;
splitt(data,",",vals);
int n = vals.size();
TreeNode* root = new TreeNode(atoi(vals[0].c_str()));
queue<TreeNode*> myQueue;
myQueue.push(root);
int i = 1;
while(!myQueue.empty()&& i<n) {
TreeNode* node = myQueue.front();
myQueue.pop();
//左子树
if(vals[i] != "null" && i<n) {
node->left = new TreeNode(atoi(vals[i].c_str()));
myQueue.push(node->left);
}
//右子树
i++;
if(vals[i] != "null" && i<n) {
node->right = new TreeNode(atoi(vals[i].c_str()));
myQueue.push(node->right);
}
i++;
}
return root;
}
};
Q2 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
思路:一道很典型的回溯+剪枝的题目,标记已走位置的方法有很多,选择容易理解的方法。
class Solution {
public:
vector<string> permutation(string s) {
len = s.length();
for(int i=0;i<len;i++) flag[i] = 0;
DFS(s,0);
return res;
}
private:
vector<string> res ;
short flag[8];
int len;
string tmp;
void DFS(string &s,int pos)
{
if(pos == len) res.push_back(tmp);
for(int i=0;i<len;i++)
{
int f=1;
for(int j=0;j<i;j++)
if(s[i] == s[j] && flag[j]==0) f=0;;
if(!flag[i]&&f)
{ flag[i] = 1;
tmp += s[i];
DFS(s,pos+1);
flag[i] = 0;
tmp = tmp.substr(0,tmp.length() - 1);
}else continue;
}
}
};
思路二:
用交换来改变字符位置,用集合来保存之前是否探索过相同字符的在这个位置的情况
class Solution {
public:
vector<string> permutation(string s) {
dfs(s, 0);
return res;
}
private:
vector<string> res;
void dfs(string s, int x) {
if(x == s.size() - 1) {
res.push_back(s); // 添加排列方案
return;
}
set<int> st;
for(int i = x; i < s.size(); i++) {
if(st.find(s[i]) != st.end()) continue; // 重复,因此剪枝
st.insert(s[i]);
swap(s[i], s[x]); // 交换,将 s[i] 固定在第 x 位
dfs(s, x + 1); // 开启固定第 x + 1 位字符
swap(s[i], s[x]); // 恢复交换
}
}
};