这道题的难点就在于如何避免反复挪动数字。
我很自然而然地就用空间换时间了,重新令了个空数组来存结果,在内存消耗上惨不忍睹。
实际上更巧妙的办法是倒着来。
一般我们会考虑从小到大去找、去存,实际上也可以从大到小去找、去存。这样就不会覆盖存储。
然后我写成了这个丑样子:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int j;
if(n == 0)
return ;
if(m == 0)
{
for(j=0 ; j<n ; j++)
nums1[j] = nums2[j];
return ;
}
int cur1 = nums1[m-1],cur2;
int i = m-1;
for(j=n-1 ; j>=0 ; j--)
{
cur2 = nums2[j];
while(i>=0 && cur1 > cur2)
{
nums1[i+j+1] = nums1[i];
i--;
if(i<0)
break;
cur1 = nums1[i];
}
nums1[i+j+1] = cur2;
}
}
};
再围观一下大佬的题解:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int len1 = m - 1;
int len2 = n - 1;
int len = m + n - 1;
while(len1 >= 0 && len2 >= 0) {
// 注意--符号在后面,表示先进行计算再减1,这种缩写缩短了代码
nums1[len--] = nums1[len1] > nums2[len2] ? nums1[len1--] : nums2[len2--];
}
// 表示将nums2数组从下标0位置开始,拷贝到nums1数组中,从下标0位置开始,长度为len2+1
System.arraycopy(nums2, 0, nums1, 0, len2 + 1);
}
}
其实意思是一样的,只不过我这种更丑。
这里学到了两个新的函数:System.arraycopy() ; swap()
我的想法挺简单的,既然比较树是否相同,那遍历一遍不就好了?
于是我写了个深度优先遍历。
/**
* 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:
bool isSameTree(TreeNode* p, TreeNode* q) {
//处理有空树的情况
if(p == NULL && q == NULL)
return true;
if((p == NULL && q!=NULL)||(p != NULL && q==NULL))
return false;
stack<TreeNode*> ss1;
stack<TreeNode*> ss2;
if(p->val != q->val)
return false;
//将树根压入栈
ss1.push(p);
ss2.push(q);
p = p->left;
q = q->left;
//在栈不空的时候深度优先遍历树
while(!ss1.empty() || !ss2.empty())
{
while(p == NULL || q == NULL)
{
if((p == NULL && q!=NULL)||(p != NULL && q==NULL))
return false;
if((ss1.empty() && !ss2.empty()) || (!ss1.empty() && ss2.empty()))
return false;
if(ss1.empty() && ss2.empty())
return true;
p = ss1.top();
ss1.pop();
q = ss2.top();
ss2.pop();
p = p->right;
q = q->right;
}
if(p->val != q->val)
return false;
ss1.push(p);
ss2.push(q);
p = p->left;
q = q->left;
}
if(ss1.empty() && ss2.empty())
return true;
return false;
}
};
显然算法课还是有点用的,起码我还记得深度优先用栈,广度优先用队列,这样写也是双百的……然后我发现看题解,这道题更简洁的写法是用递归。
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(p == NULL || q == NULL)
{
if(p == NULL && q == NULL)
return true;
return false;
}
if(p->val == q->val)
return (isSameTree(p->left,q->left)&&isSameTree(p->right,q->right));
return false;
}
};
这样写瞬间变好看了有木有。
有了100题做铺垫,这道题就很好做了。我们只需要将left子树与left子树比较 变成 left子树与right子树的比较。
class Solution {
public:
bool check(TreeNode* p,TreeNode* q)
{
if(!p && !q)
return true;
if(!p || !q)
return false;
if(p->val == q->val)
return (check(p->left,q->right)&&check(p->right,q->left));
return false;
}
bool isSymmetric(TreeNode* root) {
if(root == NULL)
return true;
return check(root->left,root->right);
}
};
很愉快。
当然官方也给了迭代的算法,即广度遍历,只不过注意因为要检查镜像对称,所以压入的时候也是一左一右地往队列里压。
其实这道题很好写,但是不熟悉C++的一些语法,所以卡了蛮久。
第一,vector
这样格式的二维数组,可以通过push_back向里面添加元素。要么一行一行的添,要么一个一个地添:res.push_back({一个一维vector数组});
,或者res[0].push_back(1);
这里可以参考一下这个:二维vector的性质、向二维vector添加元素的方法
第二,要理解广度优先遍历和深度优先遍历的区别。
除了广度优先遍历用队列,深度优先遍历用栈之外,还有一些微妙的区别。
广度优先是弹一个走,就压入它的孩子;而深度优先是压无可压,才弹出。
第三,C++的一个库,reverse,可以对容器等进行反转。
参考:C++ reverse函数的用法 - yuanch2019 - 博客园
第四,很巧妙的一点,当广度遍历完一层时,此时队列中保留的恰好是下一层的所有元素。
于是最后我们得到了这样一个代码:
/**
* 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>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> myq;
vector<vector<int>> res;
TreeNode* cur;
if(root == NULL)
return res;
else
myq.push(root);
while(!myq.empty())
{
int length = myq.size();
vector<int> curlevel;
for(int i=0; i<length ; i++)
{
cur = myq.front();
myq.pop();
if(cur != NULL)
{
curlevel.push_back(cur->val);
myq.push(cur->left);
myq.push(cur->right);
}
}
if(curlevel.size() > 0)
res.push_back(curlevel);
}
reverse(res.begin(),res.end());
return res;
}
};
这道题官方答案写得很清晰了。
法一:递归
/**
* 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* sortedArrayToBST(vector<int>& nums) {
int size = nums.size();
if(size == 0)
return NULL;
TreeNode* cur = new TreeNode;
cur->val = nums[size/2];
vector<int>::iterator i1 = nums.begin(), i2 = nums.begin() + size/2;
vector<int> leftr(i1,i2);
cur->left = sortedArrayToBST(leftr);
vector<int>::iterator i3 = nums.end(), i4 = nums.begin() + size/2 + 1;
vector<int> rightr(i4,i3);
cur->right = sortedArrayToBST(rightr);
return cur;
}
};
我这里直接把递归写在了原函数里,用了迭代器来截断数组。也可以像官方一样再声明一个含坐标的函数,方便使用。
class Solution {
public:
int height(TreeNode* root)
{
if(root == NULL)
return -1;
else
return 1 + max(height(root->left),height(root->right));
}
bool isBalanced(TreeNode* root) {
if(root == NULL)
return true;
else if(abs(height(root->left)-height(root->right))>1)
return false;
else
return (isBalanced(root->left)&&isBalanced(root->right));
}
};
我想到了要递归检查是否平衡,没想到还要递归检查左右子树的高度,真就老千层饼了。
有上一题的铺垫,很容易就跳坑里了。
一定要搞清楚,什么是深度!什么是高度!这俩是不一样的。
而题目说明里也写清楚了,最小深度是从根节点到最近叶子节点的最短路径上的节点数量,这个节点是包含了根节点本身的。
当树为空,深度为0。其实这是为了返回一个int值人为设定的,我觉得理解为不存在更好。
如果一棵树只有树根,它的深度是1.一棵树的左子树为空,那么就要看它右侧的深度,我们可以理解为它左侧的深度不存在,而不是为0.
在被上一题坑过之后,不自觉地开始加上很多限制条件,最后被自己骚死。
当然,也需要好好理解题意:
当树为空,无论sum是多少,结果都是false,可以理解为它的值为Null。
如果只有树根,它的值不为Null。虽然没有叶子节点,但也是可以进行判断的。
比较方便的是,我们采用||来判断结果,所以假如左子树为空,也不影响结果的判断,只要右边存在就能找到。
111和112可以比对着多看看。
我可能递归上瘾,第一反应就是递归:
class Solution {
public:
vector<vector<int>> result;
vector<int> eachrow(int numRows)
{
if(numRows == 1)
{
vector<int> cur;
cur.push_back(1);
result.push_back(cur);
return cur;
}
vector<int> cur;
vector<int> last = eachrow(numRows-1);
cur.push_back(1);
for(int i = 1; i<numRows-1 ; i++)
{
cur.push_back(last[i-1]+last[i]);
}
cur.push_back(1);
result.push_back(cur);
return cur;
}
vector<vector<int>> generate(int numRows) {
vector<int> cur;
if(numRows == 0)
{
return result;
}
eachrow(numRows);
return result;
}
};
递归的耗时不是很理想,我去看了一下大家的解法,用动态规划的比较多。
d[i][j] =
1 if j = 0 or j = i;
d[i-1][j-1] + d[i-1][j] else
就是这个思路,写出来大概长这样:
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> result(numRows);
if(numRows == 0)
return result;
int i,j;
for(i = 0; i<numRows ; i++)
{
result[i] = vector<int>(i+1,0); //一定要记得声明数组大小
result[i][0] = 1;
result[i][i] = 1;
}
for(i = 2 ; i<numRows ; i++)
{
for(j=1 ; j<i ; j++)
{
result[i][j] = result[i-1][j-1] + result[i-1][j];
}
}
return result;
}
};