首先是原地算法的定义:
算法原地工作的含义是指不需要任何额外的辅助,算法所需要的辅助空间不随着问题的规模而变化,是一个确定的值。
通过观察示例可以知道,我们可以猜想,大方向是前序遍历。
第一种思路:dfs。设置一个全局的指针(这种做法有点脱离原地算法,因为多开辟了一个指针变量)核心思想是拿到一个根节点以后,将其左右子树断开。然后将作为全局变量的右孩子指向刚断开左孩子和右孩子的根节点(flat->right=root)。形式上同前序遍历的递归形式。
//尝试使用前序遍历
class Solution {
public:
TreeNode* flat; //这里设置flat可以帮忙辅助式地在原root的基础上进行增删。
void dfs(TreeNode* root)
{
if(root==NULL)return;
TreeNode* l=root->left;
TreeNode* r=root->right;
root->left=NULL;//不要忘记给左右节点置空
root->right=NULL;
flat->right=root;
flat=root;//及时更新flat的值
dfs(l);
dfs(r);
}
void flatten(TreeNode* root) {
if(root==NULL) return;
flat=root;
TreeNode* l=root->left;
TreeNode* r=root->right;
flat->left=NULL;
flat->right=NULL;
dfs(l);//可以想象为什么是左右子树都要dfs一下
dfs(r);
}
};
第二种方法是迭代的方法,我觉得不错的原因是用到了考研上的迭代寻找左子树的最右节点等一系列相似操作。
详见这篇博文的解法二 https://blog.youkuaiyun.com/thousa_ho/article/details/77961918
其中关键的的是找左子树的最右节点:
代码为while (p.right != null) { p = p.right };