给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

image.png

分析过程

假设有一颗树

  1. 1
  2. / \
  3. 2 3
  4. / \ / \
  5. 4 5 6 7

左子树和右子树需要先变化为

  1. 1
  2. / \
  3. 2 5
  4. \ \
  5. 3 6
  6. \ \
  7. 4 7

最后根节点进行变化

  1. 1
  2. \
  3. 2
  4. \
  5. 3
  6. \
  7. 4
  8. \
  9. 5
  10. \
  11. 6
  12. \
  13. 7

根节点做了什么?

  1. /**
  2. * 实现节点的拉平
  3. */
  4. private void nodeNeedTodo(TreeNode node) {
  5. // 保存初始左子树
  6. TreeNode initLeft = node.left;
  7. // 保存初始右子树
  8. TreeNode initRight = node.right;
  9. // 移除左子树
  10. node.left = null;
  11. // 将原左子树变成右子树
  12. node.right = initLeft;
  13. // 遍历获取节点新右子树最末端的右子节点
  14. TreeNode temp = node;
  15. while (temp.right != null) {
  16. temp = temp.right;
  17. }
  18. // 将初始右子树变成新右子树的右子树
  19. temp.right = initRight;
  20. }

先操作左子树,然后操作右子树,最后操作根节点 ,和后序遍历二叉树是很类似的

完整算法

  1. public void flatten(TreeNode node) {
  2. if (node == null) {
  3. return;
  4. }
  5. // 先让左子树拉平
  6. flatten(node.left);
  7. // 后让右子树拉平
  8. flatten(node.right);
  9. // 最后操作根节点拉平
  10. nodeNeedTodo(node);
  11. }
  12. /**
  13. * 实现节点的拉平
  14. */
  15. private void nodeNeedTodo(TreeNode node) {
  16. // 保存初始左子树
  17. TreeNode initLeft = node.left;
  18. // 保存初始右子树
  19. TreeNode initRight = node.right;
  20. // 移除左子树
  21. node.left = null;
  22. // 将原左子树变成右子树
  23. node.right = initLeft;
  24. // 遍历获取节点新右子树最末端的右子节点
  25. TreeNode temp = node;
  26. while (temp.right != null) {
  27. temp = temp.right;
  28. }
  29. // 将初始右子树变成新右子树的右子树
  30. temp.right = initRight;
  31. }