46. 二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
如果是则返回true,否则返回false。
假设输入的数组的任意两个数字都互不相同。
样例
输入:[4, 8, 6, 12, 16, 14, 10]
输出:true
class Solution {
public:
vector<int> seq;
bool verifySequenceOfBST(vector<int> sequence) {
seq = sequence;
return dfs(0, seq.size() - 1);
}
bool dfs(int l, int r)
{
if(l >= r) return true;
int root = seq[r];
int k = l;
while(k < r && seq[k] < root) k ++;
for(int i = k; i < r; i ++)
if(seq[i] < root)
return false;
return dfs(l, k - 1) && dfs(k, r - 1);
}
};
47. 二叉树中和为某一值的路径
输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
样例
给出二叉树如下所示,并给出num=22。
5
/ \
4 6
/ / \
12 13 6
/ \ / \
9 1 5 1
输出:[[5,4,12,1],[5,6,6,5]]
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> findPath(TreeNode* root, int sum) {
dfs(root, sum);
return ans;
}
void dfs(TreeNode *root, int sum)
{
if(!root) return;
path.push_back(root->val);
sum -= root->val;
if(!root->left && !root->right && !sum) ans.push_back(path);
dfs(root->left, sum);
dfs(root->right, sum);
path.pop_back();
}
};
48. 复杂链表的复刻
请实现一个函数可以复制一个复杂链表。
在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。
注意:
函数结束后原链表要与输入时保持一致。
/**
* Definition for singly-linked list with a random pointer.
* struct ListNode {
* int val;
* ListNode *next, *random;
* ListNode(int x) : val(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
ListNode *copyRandomList(ListNode *head) {
//先复制一遍,接在原节点后面
for(auto p = head; p;)
{
auto np = new ListNode(p->val);
auto next = p->next;
p->next = np;
np->next = next;
p = next;
}
for(auto p = head; p; p = p->next->next)
{
if(p->random) p->next->random = p->random->next;
}
auto dummy = new ListNode(-1);
auto cur = dummy;
for(auto p = head; p; p = p->next)
{
cur->next = p->next;
cur = cur->next;
p->next = p->next->next;
}
return dummy->next;
}
};
49. 二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。
要求不能创建任何新的结点,只能调整树中结点指针的指向。
注意:
需要返回双向链表最左侧的节点。
例如,输入下图中左边的二叉搜索树,则输出右边的排序双向链表。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* convert(TreeNode* root) {
if(!root) return NULL;
auto sides = dfs(root);
return sides.first; //返回pair first 即左子树最左节点 second 左子树最右节点
}
pair<TreeNode*, TreeNode*> dfs(TreeNode *root)
{
if(!root->left && !root->right) return {root, root};//只剩一个叶子节点
if(root->left && root->right)
{
auto lsides = dfs(root->left), rsides = dfs(root->right);
lsides.second->right = root, root->left = lsides.second;
rsides.first->left = root, root->right = rsides.first;
return {lsides.first, rsides.second};
}
if(root->left)
{
auto lsides = dfs(root->left);
lsides.second->right = root, root->left = lsides.second;
return {lsides.first, root};
}
if(root->right)
{
auto rsides = dfs(root->right);
rsides.first->left = root, root->right = rsides.first;
return {root, rsides.second};
}
}
};
50. 序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树。
您需要确保二叉树可以序列化为字符串,并且可以将此字符串反序列化为原始树结构。
样例
你可以序列化如下的二叉树
8
/ \
12 2
/ \
6 4
为:"[8, 12, 2, null, null, 6, 4, null, null, null, null]"
注意:
以上的格式是AcWing序列化二叉树的方式,你不必一定按照此格式,所以可以设计出一些新的构造方式。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
dfs_s(root, res);
//cout << res << endl;
return res;
}
void dfs_s(TreeNode *root, string &res)
{
if(!root)
{
res += "null ";
return;
}
res += to_string(root->val) + ' ';
dfs_s(root->left, res);
dfs_s(root->right, res);
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
int u = 0;
return dfs_d(data, u);
}
TreeNode* dfs_d(string data, int &u)
{
if(u == data.size()) return NULL;
int k = u;
while(data[k] != ' ') k ++;
if(data[u] == 'n')
{
u = k + 1;
return NULL;
}
int val = 0, flag = 0; //增加对负数的判断
for(int i = u; i < k; i++)
{
if(data[i] == '-') //负号做个标记
{
flag = 1;
continue;
}
val = val * 10 + data[i] - '0';
}
u = k + 1;
if(flag) val = -val;
auto root = new TreeNode(val);
root->left = dfs_d(data, u);
root->right = dfs_d(data, u);
return root;
}
};
51. 数字排列
输入一组数字(可能包含重复数字),输出其所有的排列方式。
样例
输入:[1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {
public:
vector<vector<int>> ans;
vector<int> path;
vector<vector<int>> permutation(vector<int>& nums) {
path.resize(nums.size());
sort(nums.begin(), nums.end());
dfs(nums, 0, 0, 0);
return ans;
}
void dfs(vector<int> &nums, int u, int start, int state)
{
if(u == nums.size())
{
ans.push_back(path);
return;
}
if(!u || nums[u] != nums[u - 1]) start = 0;
for(int i = start; i < nums.size(); i ++)
{
if(!(state >> i & 1)) //如果第 i 位不是1
{
path[i] = nums[u];
dfs(nums, u + 1, i + 1, state + (1 << i));
}
}
}
};
52. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
假设数组非空,并且一定存在满足条件的数字。
思考题:
假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
样例
输入:[1,2,1,1,3]
输出:1
class Solution {
public:
int moreThanHalfNum_Solution(vector<int>& nums) {
int cnt = 0, val = -1;
for(auto x : nums)
{
if(!cnt) val = x, cnt = 1;
else
{
if(x == val) cnt ++;
else cnt --;
}
}
return val;
}
};
53. 最小的k个数
输入n个整数,找出其中最小的k个数。
注意:
数据保证k一定小于等于输入数组的长度;
输出数组内元素请按从小到大顺序排序;
样例
输入:[1,2,3,4,5,6,7,8] , k=4
输出:[1,2,3,4]
class Solution {
public:
vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
priority_queue<int> heap; //维护一个大根堆
for(auto x : input)
{
heap.push(x);
if(heap.size() > k) heap.pop();
}
vector<int> res;
while(heap.size()) res.push_back(heap.top()), heap.pop();
reverse(res.begin(), res.end());
return res;
}
};
54. 数据流中的中位数
如何得到一个数据流中的中位数?
如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
样例
输入:1, 2, 3, 4
输出:1,1.5,2,2.5
解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。
class Solution {
public:
priority_queue<int> max_heap; //大根堆
priority_queue<int, vector<int>, greater<int>> min_heap; //小根堆
void insert(int num){
max_heap.push(num);
if(min_heap.size() && max_heap.top() > min_heap.top())
{
auto maxv = max_heap.top(), minv = min_heap.top();
max_heap.pop(), min_heap.pop();
max_heap.push(minv), min_heap.push(maxv);
}
if(max_heap.size() > min_heap.size() + 1)
{
min_heap.push(max_heap.top());
max_heap.pop();
}
}
double getMedian(){
if(max_heap.size() + min_heap.size() & 1) return max_heap.top();
return (max_heap.top() + min_heap.top()) / 2.0;
}
};
55. 连续子数组的最大和
输入一个 非空 整型数组,数组里的数可能为正,也可能为负。
数组中一个或连续的多个整数组成一个子数组。
求所有子数组的和的最大值。
要求时间复杂度为O(n)。
样例
输入:[1, -2, 3, 10, -4, 7, 2, -5]
输出:18
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int sum = 0, res = INT_MIN;
for(auto x : nums)
{
if(sum > 0) sum += x;
else sum = x;
res = max(res, sum);
}
return res;
}
};
56. 从1到n整数中1出现的次数
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12这些整数中包含“1”的数字有1,10,11和12,其中“1”一共出现了5次。
样例
输入: 12
输出: 5
class Solution {
public:
int numberOf1Between1AndN_Solution(int n) {
if(!n) return 0;
vector<int> number;//取出每一位数字
while(n) number.push_back(n % 10), n /= 10;
int res = 0;
for(int i = number.size() - 1; i >= 0; i --) //从最高位开始枚举
{
auto left = 0, right = 0, t = 1; //取出左边数字和右边数字
for(int j = number.size() - 1; j > i; j --) left = left * 10 + number[j];
for(int j = i - 1; j >= 0; j --) right = right * 10 + number[j], t *= 10;
res += left * t;
if(number[i] == 1) res += right + 1;
else if(number[i] > 1) res += t;
}
return res;
}
};