左神基础班04

这篇博客详细介绍了二叉树的各种遍历方法,包括先序、中序和后序遍历的递归与非递归实现,并提供了打印二叉树的图形化方法。此外,还讲解了如何找到二叉树中节点的后继节点,以及二叉树的序列化和反序列化。最后,讨论了判断平衡二叉树和搜索二叉树的方法,以及完全二叉树的节点计数策略。
摘要由CSDN通过智能技术生成

题目一 实现二叉树的先序、 中序、 后序遍历, 包括递归方式和非递归方式

1 递归思路

假设有一棵二叉树如左所示,将二叉树进行遍历,忽略打印行为,递归函数依次访问节点的顺序如右所示。打印时机放在第一次来到这个节点的时候就是先序遍历,第二次就是中序遍历,第三次就是后序遍历。(代码都很像)

在这里插入图片描述

public static void preOrder(Node head){
        if(head == null){
            return;
        }
        System.out.print(head.value + " ");
        preOrder(head.left);
        preOrder(head.right);
    }

    public static void inOrder(Node head){
        if(head == null){
            return;
        }
        inOrder(head.left);
        System.out.print(head.value + " ");
        inOrder(head.right);
    }

    public static void posOrder(Node head){
        if(head == null){
            posOrder(head.left);
            posOrder(head.right);
            System.out.print(head.value + " ");
        }
    }

2 非递归思路(压栈)

先序遍历: 把头结点压进去,如果栈不为空的话把栈顶弹出来,然后如果这个节点右孩子不为空,压右孩子,左孩子不为空压左孩子,只要有往里压,压头弹栈,先右后左

public static void preOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> stack = new Stack<Node>();
            stack.push(head);
            while(!stack.isEmpty()){
                head = stack.pop();
                System.out.print(head.value + " ");
                if(head.right != null){
                    stack.push(head.right);
                }
                if(head.left != null){
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

中序遍历: 如果栈不为空或者头节点不为空,只要是当前节点就把左边界都压到栈里,来到空时,从栈中弹出一个节点,head向右移动。当前节点为空,从栈拿一个打印 ,当前节点往右跑;当前节点不为空,当前节点压入栈,当前节点往左。栈或头节点不空,压左,为空打印压右。

 public static void inOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> stack = new Stack<Node>();
            while(!stack.isEmpty() || head != null){
                if(head != null){
                    stack.push(head);
                    head = head.left;
                }else{
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();

    }

后序遍历(两个栈): 中序遍历是先中再左再右,可以换一下左右节点压栈的顺序,变成先中再右再左,后序遍历是先左再右再中,就是上一种顺序的逆序,实现该逆序可以再建一个栈,上一种方式该打印的时候不打印,都压到栈里,最后再打印第二个辅助栈的节点。妙啊!!!!中序遍历先压左再压右,都弹到另一个栈里然后弹出。

public static void posOrderUnRecur(Node head){
        if(head != null){
            Stack<Node> stack1 = new Stack<Node>();
            Stack<Node> stack2 = new Stack<Node>();
            stack1.push(head);
            while (!stack1.isEmpty()){
                head = stack1.pop();
                stack2.push(head);
                if(head.left != null){
                    stack1.push(head.left);
                }
                if(head.right != null){
                    stack1.push(head.right);
                }
            }
            while(!stack2.isEmpty()){
                System.out.print(stack2.pop().value + " ");
            }
        }
        System.out.println();
    }

后续遍历(一个栈): 书里,先不学了吧,整不明白


题目二 如何直观的打印一棵二叉树

检验自己的二叉树有没有调对

public static void printTree(Node head) {
		System.out.println("Binary Tree:");
		printInOrder(head, 0, "H", 17);
		System.out.println();
	}

	public static void printInOrder(Node head, int height, String to, int len) {
		if (head == null) {
			return;
		}
		printInOrder(head.right, height + 1, "v", len);
		String val = to + head.value + to;
		int lenM = val.length();
		int lenL = (len - lenM) / 2;
		int lenR = len - lenM - lenL;
		val = getSpace(lenL) + val + getSpace(lenR);
		System.out.println(getSpace(height * len) + val);
		printInOrder(head.left, height + 1, "^", len);
	}

	public static String getSpace(int num) {
		String space = " ";
		StringBuffer buf = new StringBuffer("");
		for (int i = 0; i < num; i++) {
			buf.append(space);
		}
		return buf.toString();
	}

题目三 在二叉树中找到一个节点的后继节点

现在有一种新的二叉树节点类型如下:

public static class Node{
        public int value;
        public Node left;
        public Node right;
        public Node parent;
        public Node(int data){
            this.value = data;
        }
    }

该结构比普通二叉树节点结构多了一个指向父节点的parent指针。 假设有一棵Node类型的节点组成的二叉树, 树中每个节点的parent指针都正确地指向自己的父节点, 头节点的parent指向null。 只给一个在二叉树中的某个节点 node, 请实现返回node的后继节点的函数。 在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。

思路

避免遍历整棵树,只需要该节点到后继节点的距离就可以找到。

两种情况:第一种,该结点有右子树时,下一个打印的一定是右子树的最左节点。 第二种,该结点没有右子树,找哪个左子树的最后一个节点被打印。往上找,直到当前节点是它父亲节点的左孩子,则父节点是后继。

public static Node getNode(Node node){
        if(node == null){
            return node;
        }
        if(node.right != null){
            return getLeftMost(node.right);
        }else{
            Node parent = node.parent;
            while(parent != null && parent.left != null){
                node = parent;
                parent = node.parent;
            }
            return parent;
        }


    }
    public static Node getLeftMost(Node node){
        if(node == null){
            return node;
        }
        while(node.left != null){
            node = node.left;
        }
        return node;
    }

题目四 介绍二叉树的序列化和反序列化

序列化是计算机内存方便记录二叉树,反序列化就是计算机重建二叉树。

//先序
    public static String serialByPre(Node head){
        if(head == null){
            return "#!";
        }
        String res = head.value + "!";
        res += serialByPre(head.left);
        res += serialByPre(head.right);
        return res;
    }

    public static Node reconByPreString(String preStr){
        String[] values = preStr.split("!");
        Queue<String> queue = new LinkedList<String>();
        for(int i = 0; i < values.length; i++){
            queue.add(values[i]);
        }
        return reconPreOrder(queue);

    }

    public static Node reconPreOrder(Queue<String> queue){
        String value = queue.poll();
        if(value.equals('#')){
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.left = reconPreOrder(queue);
        head.right = reconPreOrder(queue);
        return head;
    }

题目五 折纸问题

请把一段纸条竖着放在桌子上, 然后从纸条的下边向上方对折1次, 压出折痕后展开。 此时折痕是凹下去的, 即折痕突起的方向指向纸条的背面。 如果从纸条的下边向上方连续对折2 次, 压出折痕后展开, 此时有三条折痕, 从上到下依次是下折痕、 下折痕和上折痕。给定一 个输入参数N, 代表纸条都从下边向上方连续对折N次,请从上到下打印所有折痕的方向。 例如: N=1时, 打印: down;N=2时, 打印: down down up

N=1 down

N=2 down down up

N=3 down down up down down up up

相当于每对折一次,就在每个子节点加两个左右节点,分别是down和up。树形结构,中序遍历即可。

public static void printAllFolds(int N){
        printProcess(1, N, true);
    }
    public static void printProcess(int i, int N, boolean down){
        if(i > N){
            return;
        }
        printProcess(i + 1, N, true);
        System.out.println(down ? "down" : "up");
        printProcess(i + 1, N, false);

    }

题目六 判断一棵二叉树是否是平衡二叉树(套路)

平衡二叉树:在这棵树中任何一个节点,它左子树和右子树的高度差不超过1。不一定是满二叉树,满二叉树一定是平衡二叉树。

高度套路化的处理:递归函数很好用!

判断每个节点的信息,列出可能性:1)它的左子树是不是平衡的 2)它的右子树是不是平衡的 3)左树平衡右树平衡的条件下,左树右树的高度都需要知道,则高度差知道

则递归函数返回值应该包含两个信息:1)这棵树是否是平衡的 2)这棵树的高度是什么

public static class ReturnData{
        public boolean isB;
        public int h;

        public ReturnData(boolean isB, int h){
            this.isB = isB;
            this.h = h;
        }
    }

    public static boolean isB(Node head){
        return process(head).isB;
    }

    public static ReturnData process(Node head){
        if(head == null){
            return new ReturnData(true, 0);
        }
        ReturnData leftData = process(head.left);
        if(!leftData.isB){
            return new ReturnData(false, 0);
        }
        ReturnData rightData = process(head.right);
        if(!rightData.isB){
            return new ReturnData(false, 0);
        }
        if(Math.abs(leftData.h - rightData.h) > 1){
            return new ReturnData(false, 0);
        }
        return new ReturnData(true, Math.max(leftData.h, rightData.h) + 1);
    }

题目七 判断一棵树是否是搜索二叉树(BST)、 判断一棵树是否是完全二叉树 (CBT)

搜索二叉树:对于这棵树上任何一个节点为头的子树,左子树都比他小,右子树都比他大。二叉树中序遍历的节点都是依次升序的。 通常来讲搜索二叉树是不出现重复节点的。 中序遍历改写:

List<Integer> list = new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        if(root == null){
            return true;
        }
        inOrder(root);
        for(int i = 1; i < list.size(); i++){
            if(list.get(i) <= list.get(i - 1)){
                return false;
            }
        }
        return true;
    }

    public void inOrder(TreeNode head){
        if(head != null){
            inOrder(head.left);
            list.add(head.val);
            inOrder(head.right);
        }

    }

完全二叉树:判断逻辑是二叉树按层遍历:1)如果一个节点有右无左孩子,不是完全二叉树 2)如果一个节点,不是左右两个孩子都全 (有左没右或左右都没有),后面遍历到的所有的节点都必须是叶节点 3)不违反1也不违反2,是完全二叉树。设置了一个leaf变量,表示是否开启后续节点都是叶结点的阶段。

public static boolean isCBT(Node head){
        if(head == null){
            return true;
        }
        Queue<Node> queue = new LinkedList<Node>();
        boolean leaf = false;
        Node l = null;
        Node r = null;
        queue.offer(head);
        while(!queue.isEmpty()){
            head = queue.poll();
            l = head.left;
            r = head.right;
            if(leaf && (l != null || r != null) || (l == null && r!= null)){
                return false;
            }
            if(l != null){
                queue.offer(l);
            }
            if(r != null){
                queue.offer(r);
            }else{
                leaf = true;
            }

        }
        return true;
    }

题目八 已知一棵完全二叉树, 求其节点的个数

要求时间复杂度低于O(N), N为这棵树的节点个数。

一般情况下使用数组实现堆,二叉树实现堆的好处:1)减少空间浪费 2)没有扩容代价

时间复杂度低于O(N)证明不可以用遍历的方式,我们已知满二叉树节点个数的计算方法:若二叉树共l层,则个数为2l - 1。

思路

首先遍历左边界求树的总高度h总,接下来我们求右子树的左边界,共有两种情况:

1、右子树的左边界到最后一层:则左子树是满二叉树,将左子树的总节点数计算出来之后加上右子树的节点,右子树的节点使用递归计算。

2、右子树的左边界没有到最后一层:则右子树是满二叉树,计算右子树的总节点数,加上左子树的节点数,左子树的节点使用递归计算。

另外,主函数的变量代表的含义:node为当前节点,l为当前节点的层数,h为整个树的总深度。 bs返回的是以node为头节点的整个树的节点个数。

时间复杂度计算: 共有n个节点,一共有logN层,每层只遍历一个节点,则遍历节点的时间复杂度为O(logN),每个节点都要遍历子树的边界,时间复杂度为O(logN),则总体时间复杂度为O((logN)2 )

public static int nodeNum(Node head) {
		if (head == null) {
			return 0;
		}
		return bs(head, 1, mostLeftLevel(head, 1));
	}

	public static int bs(Node node, int l, int h) {
		if (l == h) {
			return 1;
		}
		if (mostLeftLevel(node.right, l + 1) == h) {
			return (1 << (h - l)) + bs(node.right, l + 1, h);
		} else {
			return (1 << (h - l - 1)) + bs(node.left, l + 1, h);
		}
	}

	public static int mostLeftLevel(Node node, int level) {
		while (node != null) {
			level++;
			node = node.left;
		}
		return level - 1;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值