一 先序遍历写入数组再生成二叉树
1 递归遍历
int num;
void makelink(struct TreeNode* root, struct TreeNode*** link)
{
if(!root)
return ;
num++;
*link = (struct TreeNode**)realloc(*link, sizeof(struct TreeNode*) * num);
(*link)[num - 1] = root;
makelink(root -> left, link);
makelink(root -> right, link);
}
void flatten(struct TreeNode* root) {
num = 0;
struct TreeNode** link = (struct TreeNode**)malloc(0);
makelink(root, &link);
for(int i = 1; i < num; i++)
{
struct TreeNode* pre = link[i - 1], *cur = link[i];
pre -> left = NULL;
pre -> right = cur;
}
free(link);
}
对二叉树进行先序遍历 将节点写入一个二叉树节点数组 最后将数组中的节点展开为右撇的二叉树
先将数组申请一个大小为零的地址 将数组地址传入递归函数以便对数组所指的地址进行修改
将num定义为全局变量便于在递归函数中进行数组下标的更新
因为我们不知道树究竟有多少个节点 所以再每先序遍历一个节点时 都对数组所指向的地址realloc
这样将遍历到的元素存入当前数组的最后一个位置 下标为 num - 1;
函数跑完之后我们就得到了先序遍历的节点顺序 但树中节点之间的指向还没有改变
这时候再用for循环遍历一遍数组中的元素并修改他们之间的指向即可
2 迭代遍历
void flatten(struct TreeNode* root) {
if(!root)
return;
struct TreeNode** sta = (struct TreeNode**)malloc(0);
struct TreeNode** val = (struct TreeNode**)malloc(0);
int num = 0, top = -1;
struct TreeNode* node = root;
while(node || top != -1)
{
while(node)
{
num++;
sta = (struct TreeNode**)realloc(sta, sizeof(struct TreeNode*) * num);
sta[++top] = node;
val = (struct TreeNode**)realloc(val, sizeof(struct TreeNode*) * num);
val[num - 1] = node;
node = node -> left;
}
if(top != -1)
node = sta[top--] -> right;
}
for(int i = 1; i < num; i++)
{
struct TreeNode* pre = val[i - 1], *cur = val[i];
pre -> left = NULL;
pre -> right = cur;
}
free(sta);
}
迭代遍历也是一样 只不过需要申请两个节点数组
一个用来实现栈的操作得到先序遍历的顺序
一个用来将节点按顺序存放起来
同样的 每遍历到一个节点都要用realloc额外申请一个节点的空间
二 先序遍历同时生成二叉树
一中必须先得到先序遍历的顺序在生成二叉树,因为如果同时生成二叉树的话就会改变原有二叉树的结构 这时再出栈得到的右子节点就是错误的
但如果我们改变栈存入的方式每次将左右子节点都存入栈 就可以再先序遍历的同时生成二叉树。
void flatten(struct TreeNode* root) {
if(!root)
return;
struct TreeNode** sta = (struct TreeNode**)malloc(sizeof(struct TreeNode*));
int top = 0, num = 1;
sta[top] = root;
struct TreeNode* pre = NULL, *cur = NULL;
while(top != -1)
{
cur = sta[top--];
if(pre)
{
pre -> left = NULL;
pre -> right = cur;
}
struct TreeNode* left = cur -> left, *right = cur -> right;
if(right)
{
num++;
sta = (struct TreeNode**)realloc(sta, sizeof(struct TreeNode*) * num);
sta[++top] = right;
}
if(left)
{
num++;
sta = (struct TreeNode**)realloc(sta, sizeof(struct TreeNode*) * num);
sta[++top] = left;
}
pre = cur;
}
free(sta);
}
上面的入栈方式是右子节点 左子节点
pre为上一层循环的节点 cur为本层循环节点
三 寻找前驱节点
上面两种方法都利用了数组 能不能省去数组直接对树进行操作呢
void flatten(struct TreeNode* root) {
while(root)
{
if(root -> left)
{
struct TreeNode* next = root -> left;
struct TreeNode* nnext = next;
while(nnext -> right)
nnext = nnext -> right;
nnext -> right = root -> right;
root -> left = NULL;
root -> right = next;
}
root = root -> right;
}
}
思路是先序遍历中 右子树都是跟在左子树最后一个节点后面的
我们先找到当前左子树中的最右侧元素 将右子树安在他的右子节点上
最后将右子节点换为左子节点将左子节点置空
循环遍历 root = root-> right