一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
剑指 Offer 37. 序列化二叉树
思路
(前序遍历序列化) O(n)
- 序列化:对整个二叉树进行先序遍历的序列存起来,同时需要把每个结点的空节点使用
"#"进行标记,例如样例的顺序是1,2,#,#,3,4,#,#,5,#,#, - 反序列化:对整个字符串按照
","进行分割,把所有的元素按序存到链表中(链表元素的顺序是先序序列),按先序遍历的方式拿链表的元素,每次拿第一个元素作为根结点,并删除链表中的第一个元素,然后递归到左儿子做同样的操作,递归到右儿子做同样的操作。注意:若第一个元素是"#",表示该节点是null,直接返回null
时间复杂度分析: 每个节点仅遍历两次,故时间复杂度为 O(n)。
c++代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Codec {
public:
string path;
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
dfs_s(root);
return path;
}
void dfs_s(TreeNode* root){
if(!root) path += "#,";
else{
path += to_string(root->val) + ',';
dfs_s(root->left);
dfs_s(root->right);
}
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int idx = 0;
return dfs_d(data, idx);
}
TreeNode* dfs_d(string &data, int& idx){
if(data[idx] == '#') {
idx += 2;
return NULL;
}else{
int k = idx;
while(data[idx] != ',') idx++;
TreeNode* root = new TreeNode(stoi(data.substr(k, idx - k))); idx++;
root->left = dfs_d(data, idx);
root->right = dfs_d(data, idx);
return root;
}
}
};
// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));
剑指 Offer 38. 字符串的排列
思路
(全排列 + 去重) O(n * n!)
数字全排列的模板,对一个数组进行全排列,数组中不包含重复元素,比如 nusm = {1, 2, 3}时,我们输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
c++代码模板
void dfs(int u)
{
if(u == nums.size())
{
res.push_back(path);
return ;
}
for(int i = 0; i < nums.size(); i++)
{
if(!st[i])
{
path[u] = nums[i];
st[i] = true;
dfs(u + 1);
st[i] = false; ;
}
}
}
现在我们要对一个字符串进行全排列,但这个字符串中可能包含重复元素,比如s = aab,此时a1a2b和a2a1b就属于同一方案,而题目要求答案数组里面不能有重复元素,因此我们要考虑去重。
如何去重
1、先将所有字符从小到大排序,这样相同的字符会排在一起。
2、对于相同的字符,我们人为去定义一种相对顺序,即优先使用出现顺序在前的字符,其次使用相对顺序在后的字符,比如a1a2b,我们按a1,a2去使用。因此,如果我们先使用了a2,再a1,就要按下列代码减去剪枝。
if(i && s[i] == s[i - 1] && !st[i - 1]) continue;
时间复杂度分析: O(n*n!)
c++代码
class Solution {
public:
vector<string> res;
vector<bool> st;
vector<string> permutation(string s) {
sort(s.begin(), s.end()); // 排序一下
st = vector<bool>(s.size(), false);
dfs(s, 0, "");
return res;
}
void dfs(string s, int u, string str){
if(u == s.size()){
res.push_back(str);
}
for(int i = 0; i < s.size(); i++){
if(!st[i]){
if(i && s[i] == s[i - 1] && !st[i - 1]) continue; //定义一个使用的相对顺序
st[i] = true;
dfs(s, u + 1, str + s[i]);
st[i] = false;
}
}
}
};
剑指 Offer 39. 数组中出现次数超过一半的数字
思路
(投票算法) O(n)
当一个国家的总统候选人r的支持率大于50%的话,即使每个反对他的人都给他投一个反对票,抵消掉他的支持票,他的支持票也不会被完全消耗掉。因此,我们可以假定和r相同的数都是支持票,和r不同的数都是反对票。
维护两个变量:候选人和他的票数
- 1、候选人初始化为
r = 0,票数c初始化为0,遍历整个数组 - 2、当候选人的票数为
0时,更换候选人,并将票数重置为1 - 3、当候选人的值和当前元素相同时,票数加
1,否则减1 - 4、最后维护的候选人即是答案
时间复杂度分析: O(n) ,n是数组的大小。
空间复杂度分析: 仅使用了两个变量,故需要 O(1) 的额外空间。
c++代码
class Solution {
public:
int majorityElement(vector<int>& nums) {
int r = 0, c = 0;
for(int x : nums){
if(c == 0) r = x, c = 1;
else if(r == x) c++;
else c--;
}
return r;
}
};