树的各种遍历写法总结(C#版)
兄弟们,我总结了各种各样树的遍历。方便大家去快速的查找,而且我发现C sharp的内容在网络上真的相较于JAVA和C++来说确实要少好多一些。咱也得出点力呀
树的遍历非常重的确需要的牢记。以下内容整理自《代码随想录》。参加算法训练营的一个小总结。因为在写各种数的便利的时候,发现树的便利花样繁多。有各种各样的方法。卡哥已经按方法不同前中后续讲完了。此处便是将前序遍历,中序遍历,后续遍历把他们的方法列出来,加以总结。方便在有需要的时候查阅。Morris遍历是在leeetcode抄的不一定对(我都能发现力扣有错别字,有错完全可能,运行不了也正常)。代码稍加修改可能就可以运行了。
前序、中序、后序的递归写法
树的遍历,最常用的还得是递归的遍历方法。好写也好记。最开始对于递归看起来可能有些吃力,但是只要看明白的原理就非常简单了。
这里有个理解的小技巧。这个理解方式有点类似于俄罗斯套娃的全部打开。我们可以把俄罗斯套娃看作一个大的套娃外壳和里面一系列套娃外壳所组成的一个小套娃。而我们可以把最外层套娃外壳拿去,里面的套娃也可以看做一个大的套娃外壳。和一系列套娃外壳所组成的小套娃。反复把外壳拿去。最终所有的套娃会被我们拆成一排也就完成了套娃的“遍历”。
同样树可以看作一个大套娃,里边的两枝看做两个小套娃。不同的遍历顺序就相当于把最大的那个套娃外壳放在两个小套娃的左边,中间还是右边。以前序为例。
第一步,将大套娃拆出来放在左边两个小套娃依次排开。
下一步,将未拆完的套娃。拆一层将拆出来的壳,放在所拆位置的左边,两个小小套娃第一个放在刚才拆的位置,第二个放在所拆位置右边。
下N步,重复操作上一步直至所有套娃都只有一层。遍历结束
下面是递归写法代码
public class Solution
{
//此函数包含了前中后序稍加修改即可
public IList<int> Traversal(TreeNode root)
{
IList<int> result = new IList<int>();
preorder(root, result);
return result;
void preorder(TreeNode root, IList<int> result)
{
if (root == null)
{
return;
}
result.Add(root.val);
preorder(root.left, result);
preorder(root.right, result);
}
void inorder(TreeNode root, IList<int> result)
{
if (root == null)
{
return;
}
inorder(root.left, result);
result.Add(root.val);
inorder(root.right, result);
}
void postorder(TreeNode root, IList<int> result)
{
if (root == null)
{
return;
}
postorder(root.left, result);
postorder(root.right, result);
result.Add(root.val);
}
}
}
前序、中序、后序的迭代写法(前中后写法不统一)
很多递归的程序都可经过一定的修改为非递归的写法,以下是树的非递归的写法——树的迭代遍历。下面的三种写法前中,后续都不太一致。
前序遍历
public class Solution
{
public IList<int> PreorderTraversal(TreeNode root)
{
IList<int> traversal = new List<int>();
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = root;
while (stack.Count > 0 || node != null)
{
while (node != null)
{
traversal.Add(node.val);
stack.Push(node);
node = node.left;
}
node = stack.Pop().right;
}
return traversal;
}
}
中序遍历
public class Solution
{
public IList<int> InorderTraversal(TreeNode root)
{
IList<int> result = new List<int>();
if (root == null)
{
return result;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = root;
while (node != null || stack.Count() != 0)
{
if (node != null)
{
stack.Push(node);
node = node.left;
}
else
{
node = stack.Pop();
result.Add(node.val);
node = node.right;
}
}
return result;
}
}
后序遍历
public class Solution {
public IList<int> PostorderTraversal(TreeNode root) {
List<int> result = new();
if (root == null) return result;
Stack<TreeNode> stack = new();
stack.Push(root);
TreeNode tempNode;
while(stack.Count() != 0) {
tempNode = stack.Pop();
result.Add(tempNode.val);
if (tempNode.left != null) stack.Push(tempNode.left);
if (tempNode.right != null) stack.Push(tempNode.right);
}
result.Reverse();
return result;
}
}
前序、中序、后序的迭代写法(前中后写法统一)
以下是树的非递归的写法——树的迭代遍历。前面的三种写法前中后规律不太一致。使用了在需要遍历的地方。用空指针做标记的方式。完成了前,中,后续三种写法的统一。
前序遍历
class Solution
{
public IList<int> PreorderTraversal(TreeNode root)
{
IList<int> result = new List<int>();
if (root == null)
{
return result;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.Push(root);
while (stack.Count() != 0)
{
TreeNode node = stack.Peek();
if (node != null)
{
stack.Pop();
if (node.right != null)
{
stack.Push(node.right);
}
if (node.left != null)
{
stack.Push(node.left);
}
stack.Push(node);
stack.Push(null);
}
else
{
stack.Pop();
node = stack.Pop();
result.Add(node.val);
}
}
return result;
}
}
中序遍历
class Solution
{
public IList<int> InorderTraversal(TreeNode root)
{
IList<int> result = new List<int>();
if (root == null)
{
return result;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.Push(root);
while (stack.Count() != 0)
{
TreeNode node = stack.Peek();
if (node != null)
{
stack.Pop();
if (node.right != null)
{
stack.Push(node.right);
}
stack.Push(node);
stack.Push(null);
if (node.left != null)
{
stack.Push(node.left);
}
}
else
{
stack.Pop();
node = stack.Pop();
result.Add(node.val);
}
}
return result;
}
}
后序遍历
class Solution
{
public IList<int> PostorderTraversal(TreeNode root)
{
IList<int> result = new List<int>();
if (root == null)
{
return result;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.Push(root);
while (stack.Count() != 0)
{
TreeNode node = stack.Peek();
if (node != null)
{
stack.Pop();
stack.Push(node);
stack.Push(null);
if (node.right != null)
{
stack.Push(node.right);
}
if (node.left != null)
{
stack.Push(node.left);
}
}
else
{
stack.Pop();
node = stack.Pop();
result.Add(node.val);
}
}
return result;
}
}
前序、中序、后序的迭代写法(Morris 遍历)
Morris 遍历是一种只占用常数空间来实现前序遍历。
这种方法由 J. H. Morris 在 1979 年的论文「Traversing Binary Trees Simply and Cheaply」中首次提出,因此被称为 Morris 遍历。Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。其前序遍历规则总结如下:
新建临时节点,令该节点为 root;
如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点;如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点:如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。重复步骤 2 和步骤 3,直到遍历结束。这样我们利用 Morris 遍历的方法,前序遍历该二叉树,即可实现线性时间与常数空间的遍历。
前序遍历
class Solution
{
public IList<int> PreorderTraversal(TreeNode root)
{
IList<int> result = new List<int>();
if (root == null)
{
return result;
}
TreeNode curr = root;
TreeNode currLeft = null;
while (curr != null)
{
currLeft = curr.left;
if (currLeft != null)
{
while (currLeft.right != null && currLeft.right != curr)
{
currLeft = currLeft.right;
}
if (currLeft.right == null)
{
result.Add(curr.val);
currLeft.right = curr;
curr = curr.left;
continue;
}
else
{
currLeft.right = null;
}
}
else
{
result.Add(curr.val);
}
curr = curr.right;
}
return result;
}
}
中序遍历
//中序 Morris
class Solution {
public IList<int> InorderTraversal(TreeNode root) {
IList<int> list = new List<int>();
if (root == null) {
return list;
}
TreeNode p1 = root;
TreeNode p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
if (p2.right == null) {
p2.right = p1;
p1 = p1.left;
continue;
} else {
p2.right = null;
}
}
list.Add(p1.val);
p1 = p1.right;
}
return list;
}
}
后序遍历
class Solution {
IList<int> list = new List<int>();
public IList<int> PostorderTraversal(TreeNode root) {
if (root == null) {
return list;
}
TreeNode p1 = root;
TreeNode p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
if (p2.right == null) {
p2.right = p1;
p1 = p1.left;
continue;
} else {
p2.right = null;
PostMorris(p1.left);
}
}
p1 = p1.right;
}
//以根节点为起点的链表
PostMorris(root);
return list;
}
public void PostMorris(TreeNode root) {
//翻转链表
TreeNode reverseNode = ReverseList(root);
//从后往前遍历
TreeNode cur = reverseNode;
while (cur != null) {
list.Add(cur.val);
cur = cur.right;
}
//翻转回来
ReverseList(reverseNode);
}
public TreeNode ReverseList(TreeNode head) {
TreeNode cur = head;
TreeNode pre = null;
while (cur != null) {
TreeNode next = cur.right;
cur.right = pre;
pre = cur;
cur = next;
}
return pre;
}
}