算法初学(第五天)

1. 实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式。

    1.1 递归版本(先序,中序,后序区别在于打印的时机)

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

  1.2 非递归版本(利用栈的特性)

        先序遍历:

        1.2.1  先压头结点,然后弹出;

        1.2.2 再先压右节点,再压左节点

        1.2.3 弹出左节点,再先压左节点的左节点,再右节点;

        1.2.4 利用堆栈,先将左节点都处理完毕后,处理右节点。

if(head!=null){
      stack.push(head);
      while(!stack.isEmpty()){
            head = stack.pop();   //head是弹出去的那个节点,即根
            System.out.println(head.value);
            if(head.right!=null){
                 stack.push(head.right);
            }
            if(head.left!=null){
                 stack.push(head.left);
                }
            }
        }

       中序遍历:

       1.3.1 先把根和所有左节点都压入栈;

       1.3.2 当走到底,则弹出栈(此时栈顶保存为根节点),然后再将节点走向右节点,压入栈;

       1.3.3 对于一个节点,他的左子树先打印,然后打印根,再把右压进栈,再弹出,一步步往回走。

        while(!stack.isEmpty()||head2!=null){
            if(head2!=null){
                stack.push(head2);
                head2 = head2.left;
            }
            else{
                head2 = stack.pop();
                System.out.print( head2.value+ " ");
                head2 = head2.right;
            }

        }

      后序遍历

      1.4.1  先序遍历为,中左右,可改为中右左,此为后序遍历的逆序;

      1.4.2  先压入根节点;

      1.4.3   弹出栈,再压入新的栈,再先压左节点,再压右节点;

      1.4.4  此时新栈中保存的尾中右左,弹出打印,即可得到后序遍历。

        if(head3!=null){
            stack.push(head3);
            while(!stack.isEmpty()){
                head3 = stack.pop();
                stack2.push(head3);
                if(head3.left!=null){
                    stack.push(head3.left);
                }
                if(head3.right!=null){
                    stack.push(head3.right);
                }
            }
            while(!stack2.isEmpty()){
                System.out.print(stack2.pop().value+" ");
            }
        }

2 在二叉树中找到一个节点的后继节点 【 题目】 现在有一种新的二叉树节点类型如下:
public 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的后继节点。

2.1 如果一个节点有右子树,那么他的后续节点右子树的最左边节点;

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

2.2 如果一个节点没有右子树,那么走向它的父节点,判断此节点是否为父节点的左子树,

      如果是,则父为后继节点;

      如果不是,则寻找父的父节点,直到为左子树,父为后继节点,如果为空则没有。

     左中右,没有右因此为树的最后一个元素,因此需要寻找父为左子树的节点,此节点相当于根节点,当前节点处于左子树

        else{
            Node parent = node.parent;
            while(parent!=null&&parent.left==node){
                node = parent;
                parent = node.parent;
            }
            return parent;
        }

3. 一个二叉树的序列化和反序列化,序列化(保存为一个字符串),反序列化(利用字符串转为二叉树)

    3.1 序列化,将二叉树变为一个字符串;

    3.2 当前节点不为空,当前节点加(下划线或者其他符合,为了方便反序列化分割),然后弄当前节点的左子树,右子树;

    3.3 如果节点为空,则返回#加(下划线或者其他符合,为了方便反序列化分割);

    3.4 字符串为序列化结果。

         先序遍历的序列化

    public static String serialByLevel(Node head) {
        String res = null;
        if(head==null){
            return "#_";
        }
        res+= head.value + "_";
        res+= serialByLevel(head.left);
        res+= serialByLevel(head.right);
        return res;
    }

   3.5 反序列化,将序列化的字符串转为二叉树;

   3.6 首先将字符串利用特殊符合来分割成数组;

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

   3.7 头结点为第一个元素,然后左子树等于剩下的反序列化,右子树等于剩下的反序列化。

public static Node serialByLevel(Queue<String> queue){
        String value = queue.poll();
        if(value.equals("#")){
            return null;
        }
        Code_04_SerializeAndReconstructTree.Node node = null;
        node = new Node(Integer.parseInt(value));
        node.left = serialByLevel(queue);
        node.right = serialByLevel(queue);
        return node;

    }

       3.7 简述层序列化:  第一个节点进来,加入队列,然后保存这个节点的值;

            进入循环,把poll队列,此时为头结点的节点,判断它左子树和右子树是否为空,不为,则添加此节点到队列,右子树同理,此时队列中保存的为左子树和右子树节点,然后循环,每次弹出一个节点,这样能够保证每次用的都是一层的数。

	public static String serialByLevel(Node head) {
		if (head == null) {
			return "#!";
		}
		String res = head.value + "!";
		Queue<Node> queue = new LinkedList<Node>();
		queue.offer(head);
		while (!queue.isEmpty()) {
			head = queue.poll();
			if (head.left != null) {
				res += head.left.value + "!";
				queue.offer(head.left);
			} else {
				res += "#!";
			}
			if (head.right != null) {
				res += head.right.value + "!";
				queue.offer(head.right);
			} else {
				res += "#!";
			}
		}
		return res;
	}

      3.8 简述层反序列化:将字符串,利用特殊符合进行分割,第一个数为头结点,然后保存到队列中,进入循环;弹出,此时node为头结点,然后他的左右就是后面两个数,然后判断他的左右是否为空,不为空则保存到队列,此时队列相当于把结点都保存下来了,然后挨个赋值。

	public static Node reconByLevelString(String levelStr) {
		String[] values = levelStr.split("!");
		int index = 0;
		Node head = generateNodeByString(values[index++]);
		Queue<Node> queue = new LinkedList<Node>();
		if (head != null) {
			queue.offer(head);
		}
		Node node = null;
		while (!queue.isEmpty()) {
			node = queue.poll();
			node.left = generateNodeByString(values[index++]);
			node.right = generateNodeByString(values[index++]);
			if (node.left != null) {
				queue.offer(node.left);
			}
			if (node.right != null) {
				queue.offer(node.right);
			}
		}
		return head;
	}

	public static Node generateNodeByString(String val) {
		if (val.equals("#")) {
			return null;
		}
		return new Node(Integer.valueOf(val));
	}

4.判断一棵二叉树是否是平衡二叉树(平衡二叉树要求层数差不能大于一)

   4.1 需要满足四个条件:如果左子树不平衡,则不平衡;如果右子树不平衡,则不平衡;如果左右平衡,左右子树高度差大于1则不平衡,反正平衡;

  4.2 因此我们需要两个信息,是否平衡和高度。利用递归大套路。

    public static class Returnh{
        int h;
        boolean isB;

        public Returnh(int h, boolean isB) {
            this.h = h;
            this.isB = isB;
        }
    }
    public static Returnh process(Node node){
        if(node==null){
            return new Returnh(0,true);
        }
        Returnh left = process(node);
        if(!left.isB){
            return new Returnh(0,false);
        }
        Returnh right = process(node);
        if(!right.isB){
            return new Returnh(0,false);
        }
        if(Math.abs(left.h-right.h)>1){
            return new Returnh(0,false);
        }
        return new Returnh(Math.max(left.h,right.h)+1,true);
        
    }

   5.判断一棵树是否是搜索二叉树、 判断一棵树是否是完全二叉树

      搜索二叉树:左子树比根小,根比二叉树小,即中序遍历如果为递增的,则为搜索二叉树;

      可以利用非递归版本的中序遍历,打印的句子改为判断当前值和上一个值的关系,看看是否为递增的

       判断是否为完全二叉树:

       1. 如果一个节点,只有右,那么不为完全二叉树;

       2. 如果节点,有左或者没有,那么他后面的节点必然是叶节点;

	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);
			}
			if(l==null||r==null){  //没左或者有左没右
				leaf = true;
			}
		}
		return true;
	}

6.已知一棵完全二叉树, 求其节点的个数    要求: 时间复杂度低于O(N), N为这棵树的节点个数
   6.1 先让二叉树的节点来到最左边,可以确定二叉树的深度h;

   6.2 再判断,二叉树的右子树的最左边的深度是否为h; 

   6.3  如果是,则表示左子树是一个满二叉树,此时节点个数为2的高度次-1+1+右子树;递归剩余部分;

   6.4  如果不是,则表示右子树为满二叉树,需要注意此时右子树深度要比二叉树小一层,节点个数还是2的高度次-1+1+左子树;递归剩余部分;

    此算法复杂度为O(logN)^2,左边界为O(logN),每一个左边界需要找右子树的最左边,复杂度O(logN)

   因此为O(logN)*O(logN)

	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 lever, int h) {
		if (lever == h) {
			return 1;
		}
		if (mostLeftLevel(node.right, lever + 1) == h) {
			return (1 << (h - lever)) + bs(node.right, lever + 1, h);
		} else {
			return (1 << (h - lever - 1)) + bs(node.left, lever + 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、付费专栏及课程。

余额充值