【左程云 数据结构与算法笔记】P8 图

222 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情 下面是我整理的跟着b站左程云的数据结构与算法学习笔记,欢迎大家一起学习。

搜索二叉树

搜索二叉树:每一颗子树,左树的节点都比他小,右树的节点都比他大 判断搜索二叉树:中序遍历时一定升序 思路分析 先判断左树上节点,将其赋值给一个全局变量,将头节点和右树上的节点依次比较,若有小于情况,则返回false,左树上的节点和头节点都已经比较,最后返回右树的比较结果 代码实现:采用递归的方法

public static int preValue=Integer.MIN_VALUE;  
public static boolean isBST(Node head){  
    if (head==null){  
        return true;  
    }  
    //先判断左树
    boolean isLeftBST = isBST(head.left);  
    if (!isLeftBST){  
        return false;  
    }  
    if (head.value<=preValue){  
        return false;  
    }else {  
        preValue=head.value;  
    }  
    return isBST(head.right);  
}

实质是利用中序遍历再比较判断是否为升序

//    相当于  
    public static boolean isBST2(Node head){  
        List<Node> inOrderList = new ArrayList<>();  
        process(head, inOrderList);  
        //此时inOrderList存放的是中序遍历的结果  
        for (int i = 0; i < inOrderList.size()-1 ; i++) {  
            if (inOrderList.get(i).value>inOrderList.get(i+1).value){  
                return false;  
            }  
        }  
        return true;  
    }  
    public static void process(Node head, List<Node> inOrderList ){  
        if (head==null){  
            return;  
        }  
        process(head.left, inOrderList);  
        inOrderList.add(head);  
        process(head.right, inOrderList);  
    }

也可以采用非递归的方法

//非递归方式  
public static boolean isBST3(Node head){  
    if (head==null){  
        return true;  
    }  
    int preValue=Integer.MIN_VALUE;  
    Stack<Node> stack = new Stack<>();  
    while (!stack.isEmpty()||head!=null){  
        if (head!=null){  
            stack.push(head);  
            head=head.left;  
        }else {  
            head=stack.pop();  
            if (head.value<preValue){  
                return false;  
            }else {  
                preValue=head.value;  
            }  
            head=head.right;  
        }  
  
    }  
    return true;  
}

其他思路 左右树都为搜索二叉树,左树得到max,右树得到min,可以将递归得到该树是否为搜索二叉树,最小值和最大值 代码实现

public static class ReturnData{  
        public boolean isBST;  
        public int max;  
        public int min;  
  
        public ReturnData(boolean isBST, int max, int min) {  
            this.isBST = isBST;  
            this.max = max;  
            this.min = min;  
        }  
    }  
    public static ReturnData process(Node head){  
        if (head==null){  
            return null;  
        }  
        ReturnData leftData = process(head.left);  
        ReturnData rightData = process(head.right);  
        int min=head.value;  
        int max= head.value;  
        if (leftData!=null){  
            min=Math.min(min, leftData.min);  
            max=Math.min(max, leftData.max);  
        }  
        if (rightData!=null){  
            min=Math.min(min, rightData.min);  
            max=Math.max(max, rightData.max);  
        }  
    /*    boolean isBST=true;  
        if (leftData!=null&&(!leftData.isBST||leftData.max>= head.value)){            isBST=false;        }        if (rightData!=null&&(!rightData.isBST||rightData.min<= head.value)){            isBST=false;        }*///        等效于  
        boolean isBST=false;  
        if (  
                (leftData!=null?(leftData.isBST&&leftData.max<head.value):true)  
                &&  
                (rightData!=null?(rightData.isBST&&rightData.min> head.value):true)  
        ){  
            isBST=true;  
        }  
        return new ReturnData(isBST, max,min);  
    }

完全二叉树

完全二叉树 从左到右依次编满 判断完全二叉树的方法

  1. 即任一节点有右无左则返回false
  2. 在1条件满足下,如果遇到第一个左右孩子不全,后续皆为叶节点 诸如

代码实现

public static boolean isCBT(Node head) {  
    if (head == null) {  
        return true;  
    }  
    LinkedList<Node> queue = new LinkedList<>();  
    boolean leaf = false;  
    Node l = null;  
    Node r = null;  
    queue.add(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.add(l);  
        }  
        if (r != null) {  
            queue.add(r);  
        } else {  
            leaf = true;  
        }  
    }  
    return true;  
}

满二叉树

节点个数N和最大深度L满足 N=2^L-1 可以通过得到节点个数N和最大深度L判断

平衡二叉树

平衡二叉树:对于任何一个子树来说,左树和右树的高度差都不超过1

代码实现

public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

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

	public static class ReturnType {
		public boolean isBalanced;
		public int height;

		public ReturnType(boolean isB, int hei) {
			isBalanced = isB;
			height = hei;
		}
	}

	public static ReturnType process(Node x) {
		if (x == null) {
			return new ReturnType(true, 0);
		}
		ReturnType leftData = process(x.left);
		ReturnType rightData = process(x.right);
		int height = Math.max(leftData.height, rightData.height);
		boolean isBalanced = leftData.isBalanced && rightData.isBalanced
				&& Math.abs(leftData.height - rightData.height) < 2;
		return new ReturnType(isBalanced, height);
	}

题型总结 二叉树的树形DP问题,即可以通过左树要信息和右树要信息,都可以通过递归的方法求解

找到node1和node2得到最低公共祖先

以该图为例,E和F的最低公共节点为E,D与F的最低公共节点为B

将D节点的父节点依次遍历放进set中,不断遍历f的父节点,当set中包含节点时,遍历结束 代码实现

public static Node leastAncestor(Node head,Node o1,Node o2){  
    Node resultNode=null;  
    HashMap<Node,Node> ancestorMap = new HashMap<>();  
    ancestorMap.put(head, head);  
    accomplishNodeToMap(head, ancestorMap);  
    HashSet<Node> set1 = new HashSet<>();  
  
    Node cur=o1;  
    while (cur!=ancestorMap.get(cur)){  
        set1.add(cur);  
        cur=ancestorMap.get(cur);  
    }  
    set1.add(head);  
    cur=o2;  
    //此时set里面保存了  
    while (cur!=ancestorMap.get(cur)){  
        if (set1.contains(cur)){  
            resultNode=cur;  
            break;  
        }  
    }  
    return resultNode;  
}
public static void accomplishNodeToMap(Node head,HashMap<Node,Node> ancestorMap ){  
    if (head==null){  
        return;  
    }  
    ancestorMap.put(head.left,head);  
    ancestorMap.put(head.right, head);  
    accomplishNodeToMap(head.left, ancestorMap);  
    accomplishNodeToMap(head.right, ancestorMap);  
}

代码简化

public static Node lowestAncestor(Node head,Node O1,Node O2){  
    if (head==null||head==O1||head==O2){  
        return  head;  
    }  
    Node left=lowestAncestor(head.left, O1, O2);  
    Node right=lowestAncestor(head.right, O1, O2);  
    if (left!=null&&right!=null){  
        return head;  
    }  
    //左右两颗树 并不都有返回值
    return left!=null?left:right;  
}

情况分析:

  1. O1,O2之间有一个为最低公共祖先
  2. O1与O2不互为最低公共祖先,需要往上汇聚才能找到

代码运行解析

  1. 在第一种情况中,可能两者节点都在左边,右边返回null,左边返回最低公共祖先,返回正确情况
  2. 在第二种情况中 会命中左边不为空和右边不为空,返回本身

问题:找到后继节点 查找后继节点 把所有节点的后继节点找到,需要的代码复杂度为O(N) 如果有父节点D可以很方便找到自己的后继节点B,E可以遍历两次找到自己的后继节点A,可将代码的时间复杂度优化到 O(K) k为两者相距的距离 情况分析:

  1. x有右树的时候 后继节点为右树上的最左节点
  2. x无右树 如果是最右节点,后继为空 如果不是最右上的节点 不断往上找 当发现有一个节点是其父节点上的左孩子,返回父节点 当父节点走到空,即头节点,此时节点为最右节点,没有后继节点,返回空

问题 二叉树的序列化和反序列化

先序方式遍历的序列化 其中#表示为空 ,__ 表示值的地址 代码实现

//序列化  
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;  
}

不断消费队列重构树

折纸问题

image.png 中序遍历的方式打印,但是只使用了N的空间