树的遍历及相关题目

来源:互联网 发布:aes解密算法 编辑:程序博客网 时间:2024/04/30 10:00

树的子结构

注意子结构和子树的区别
输入两颗二叉树A,B,判断B是不是A的子结构。

/**public class TreeNode {    int val = 0;    TreeNode left = null;    TreeNode right = null;    public TreeNode(int val) {        this.val = val;    }}*/public class Solution {    public class Solution {    //子结构表示B可以放入到A中重合    public boolean HasSubtree(TreeNode root1,TreeNode root2) {        //注意root2开始为空时返回false;        if(root1 != null && root2 != null) {            return isEqual(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);        }        return false;    }    public boolean isEqual(TreeNode t1, TreeNode t2) {        if(t2 == null)            return true;        if(t1 == null)            return false;        if(t1.val != t2.val)            return false;        return isEqual(t1.left, t2.left) && isEqual(t1.right, t2.right);    }}}

1.树的定义

  • 树是n(n >= 0 )个节点的有限集。当n等于0时称为空树。在任意一颗非空树中:1)有且仅有一个特定的称为根的结点 2)当 n > 1 时,其余结点可分为m(m > 0) 个互不相交的有限集T1,T2, …. Tm,其中每一个集合本身又是一颗树,并且称为根的子树。
  • 森林是m(m >= 0)个互不相交的树的集合。

1.1 树的存储结构

三种表示法

  • 双亲表示法
//在每个结点中,设置一个指示器指示其双亲结点在链表中的位置#define MAX_TREE_SIZE 100;typedef int ElementType;typedef struct TreeNode {    ElementType data;    int parent; //指向双亲的位置} TreeNode;typedef struct {    TreeNode nodes[MAX_TREE_SIZE];    int r,n; // 根位置和结点数}Tree;
  • 孩子表示法
//把每个结点的孩子结点排列起来,以单链表做存储结构#define MAX_TREE_SIZE 100;typedef int ElementType;typedef struct TreeNode {    int child;    struct TreeNode* next;} *ChildPtr;typedef struct {    ElementType data;    ChildPtr firstChild;}TBox;typedef struct {    TBox nodes[MAX_TREE_SIZE];    int r, n; //根和结点的个数}Tree;
  • 孩子兄弟表示法
//任意一颗树,它的结点的第一个孩子如果存在就是唯一的//它的有兄弟如果存在也是唯一的typedef struct TreeNode {    ElementType data;    struct TreeNode* firstChild;    struct TreeNode* rightBrother;}TreeNode, *Tree;

2 二叉树的定义

二叉树是n个结点的有限集,该集合或者为空集,或者由一个根节点和两颗互不相交的分别称为左子树和右子树的二叉树组成。

二叉树的存储

  • 顺序存储
    二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标。
  • 链式存储
typedef struct BinaryTreeNode {    ElementType data;    struct BinaryTreeNode* lChild;    struct BinaryTreeNode* rChild;}BinaryTreeNode, *BinaryTree; 

二叉树的遍历

1. 前序遍历

前序遍历:
首先访问根结点,再访问左子树,最后访问右子树

class BinaryTreeNode {    int val = 0;    BinaryTreeNode left = null;    BinaryTreeNode right = null;    public BinaryTreeNode(int val) {        this.val = val;    }}//递归实现public void preOrder(BinaryTreeNode root) {    if(root != null) {        System.out.println(root.val);        preOrder(root.left);        preOrder(root.right);    }}//非递归实现public void nonRecursivePreOrder(BinaryTreeNode root) {    Stack<BinaryTreeNode> stack = new Stack<>();    BinaryTreeNode current = root;    while(current != null || (!stack.isEmpty())) {        while(current != null) {            System.out.println(current.val);            stack.push(current);            current = current.left;        }        if(!stack.isEmpty()) {            current = stack.pop();            current = current.right;        }    }}

2. 后序遍历

后序遍历:
首先访问左子树,然后访问右子树,最后访问根结点

class BinaryTreeNode {    int val = 0;    BinaryTreeNode left = null;    BinaryTreeNode right = null;    public BinaryTreeNode(int val) {        this.val = val;    }}//递归实现public void postOrder(BinaryTreeNode root) {    if(root != null) {        postOrder(root.left);        postOrder(root.right);        System.out.println(root.val);    }}//非递归实现 比较复杂/*对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还未访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。*/public void nonRecursivePostOrder(BinaryTreeNode root) {    Stack<BinaryTreeNode> stack = new Stack<>();    BinaryTreeNode cur = root;    while(cur != null || (!stack.isEmpty())) {        while(cur != null) {            cur.isFirst = true;            stack.push(cur);            cur = cur.left;        }        if(!cur.isEmpty()) {            cur = stack.pop();            if(cur.isFirst) {                cur.isFirst = false;                stack.push(cur);                cur = cur.right;            }else {                System.out.println(cur.val);                cur = null; //注意            }        }    }}//第二种方法/*要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。*/public void nonRecursivePostOrder2(BinaryTreeNode root) {    if(root == null)        return ;    Stack<BinaryTreeNode> stack = new Stack<>();    BinaryTreeNode cur = null;    BinaryTreeNode pre = null;    stack.push(root);    while(!stack.isEmpty()) {        cur = stack.peek();        if((cur.left == null && cur.right == null) || (pre != null && (pre == cur.left || pre == cur.right)) {            System.out.println(cur.val);            stack.pop();            pre = cur;        }else {            if(cur.right != null)                stack.push(cur.right);            if(cur.left != null)                stack.push(cur.left);        }    }}

3. 中序遍历

中序遍历:
首先访问左子树,然后访问根结点,最后访问右子树

class BinaryTreeNode {    int val = 0;    BinaryTreeNode left = null;    BinaryTreeNode right = null;    public BinaryTreeNode(int val) {        this.val = val;    }}//递归实现public void inOrder(BinaryTreeNode root) {    if(root != null) {        inOrder(root.left);        Sysout.out.println(root.val);        inOrder(root.right);    }}//非递归实现public void nonRecursiveInOrder(BinaryTreeNode root) {    Stack<BinaryTreeNode> stack = new Stack<>();    BinaryTreeNode cur = root;    while(cur != null || (!stack.isEmpty())) {        while(cur != null) {            stack.push(cur);            cur = cur.left;        }        if(!stack.isEmpty()) {            cur = stack.pop();            System.out.println(cur.val);            cur = cur.right;        }    }}

4. 层次遍历

class BinaryTreeNode {    int val = 0;    BinaryTreeNode left = null;    BinaryTreeNode right = null;    public BinaryTreeNode(int val) {        this.val = val;    }}public void LevelOrder(BinaryTreeNode root) {    if(root == null)        return ;    Queue<BinaryTreeNode> queue = new Queue<>();    BinaryTreeNode cur = null;    queue.add(root);    while(!queue.isEmpty()) {        cur = queue.poll();        System.out.println(cur.vale);        if(cur.left != null)            queue.add(cur.left);        if(cur.right != null)            queue.add(cur.right);    }}

线索二叉树

指向前驱和后继的指针称为线索,其相应的二叉树称为线索二叉树
线索化的实质就是将二叉链表中的空指针改为指向前驱或后继的线索,线索化的过程就是在遍历过程中修改空指针

//存储结构typedef enum {Child, Thread} PointerTag;typedef struct BinaryTreeNode {    ElementType data;    struct BinaryTreeNode* left;    struct BinaryTreeNode* right;    PointerTag leftTag;    // 用于标识是左孩子还是前驱     PointerTag rightTag;   //用于标识是右孩子还是后继}BinaryTreeNode, *BinaryTreee;//二叉树的线索化BinaryTree pre = null;public void inThreading(BinaryTree root) {    if(root != null) {        inThreading(root.left);        if(root.left == null) {            root.left = pre;            root.leftTag = Thread;        }        if(pre != null && pre.right == null) {            pre.right = root;            pre.rightTag = Thread;        }        pre = root;        inThreading(root.right);    }}//线索二叉树的遍历public void inOrderTraverThread(BinaryTree head) {    BinaryTree p;    p = head.left;    while(p != head) {        while(p.leftTag == Link)            p = p.left; //找到左子树的第一个访问结点        System.out.println(p.val);        while(p.rightTaf == Thread && p.right != head) {            p = p.right;            System.out.println(p.val);        }        p = p.right; //进入右子树    }}
0 0
原创粉丝点击