左神算法笔记(七)——二叉树

先序中序后序二叉树遍历

二叉树的遍历是二叉树结构中非常基础但是非常重要的题目,需要认真掌握
使用递归方法实现遍历,会发现代码非常的简单,这也可以看作是每种遍历方法的遍历整体的过程,其实完成的就是递归的操作,只是输出的时间不同。

public static class Node{
	public int value;
	public Node left;
	public Node right;
	public Node(int data){
		this.value = data;
	}
}
public static void preOrderRecur(Node head){
	if(head == null){
		return;
	}
	//打印放在最开始的地方就是先序遍历,打印放在中间位置,则是中序遍历,打印放在最后则是后序遍历。
	System.out.print(head.value+" ");
	preOrderRecur(head.left);
	preOderRecur(head.right);
}
//中序遍历
public static void inOrderRecur(Node head){
 if(head == null){
  return;
 }
  inOrderRecur(head.left);
 //打印放在最开始的地方就是先序遍历,打印放在中间位置,则是中序遍历,打印放在最后则是后序遍历。
 System.out.print(head.value+" ");
 inOderRecur(head.right);
}
//后序遍历
public static void posOrderRecur(Node head){
 if(head == null){
  return;
 }
 posOrderRecur(head.left);
 posOderRecur(head.right);
 //打印放在最开始的地方就是先序遍历,打印放在中间位置,则是中序遍历,打印放在最后则是后序遍历。
 System.out.print(head.value+" ");
 }

除了递归的方法解决题目,当然还有非递归的方法,只是相对而言代码稍微复杂一点,新建立一个堆栈,在堆栈中,先放入右边部分,后放入左边部分,这样在弹出的时候就可以先出左,再出右。
二叉树和栈有天然的匹配
二叉树总体结构从上到下,所以返回的结构是从下到上,如果采用队列的话,队列的结构也是从上到下,无法返回。

public static void preOrderUnrecur(Node head){
	System.out.print("pre-order:");
	if(head != null){
		Stack<Node> stack = new Stack<Node>();
		stack.add(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();
}
//中序遍历
public static void inOrderUnRecur(Node head){
	System.out.print("in-order:");
	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 posOrderUnRecur1(Node head){
	System.out.print("pos-order:");
	if(head != null){
		Stack<Node> s1 = new Stack<Node>();
		Stack<Node> s2 = new Stack<Node>();
		s1.push(head);
		while(!s1.isEmpty()){
			head = s1.pop();
			s2.push(head);
			if(head.left != null){
				s1.push(head.left);
			}
			if(head.right != null){
				s1.push(head.right);
			}
		}
		while(!s2.isEmpty()){
			System.out.print(s2.pop().value+ " ");
		}
	}
	System.out.println();
}

题目二:直观打印二叉树

题目三:在二叉树中寻找节点的后继节点

寻找后继节点和寻找前节点是基本相同的操作,可以自己写一写

public static Node getSuccessorNode(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 != node){
			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;
}

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

序列化:持久化,将结构用文件的形式记录下来就是序列化
反序列化:将文件形式记录的结构重新构造出来

二叉树的序列化:采用先序遍历的方法,在页节点的时候将所有的左右子节点的遍历为null的用特殊的标记进行表示,从而可以明确:当出现连续两个特殊标记的时候,意味着之前的节点为叶节点,从而可以明确每个二叉树节点在二叉树中的位置。

反序列化:

//序列化过程
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.offer(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.
递归函数处理二叉树很好玩用

大的思路:每颗节点为头的子树全部都是平衡二叉树,同时左树高和右树高差值小于等于1,则整个二叉树为平衡二叉树。
套路:

  1. 列出可能性
  2. 整理出返回值的类型
  3. 整个递归过程按照同样的结构得到子树的信息,利用子树的信息,加工出我的信息,往上返回
public static boolean isBalance(Node head){
	boolean[] res = new boolean[1];
	res[0] = true;
	getHight(head,1,res);
	return res[0];
}
public static int getHight(Node head,int level,boolean[] res){
	if(head == null){
		return level;
	}
	int lH = getHight(head.left,level+1,res);
	if(!res[0]){
		return level;
	}
	int rH = getHeight(head.right,level+1,res);
	if(!res[0]){
		return level;
	}
	if(Math.abs(lH-rH)>1){
		res[0] = false;
	}
	return Math.max(lH,rH);
}  
	

判断一棵树是否为搜索二叉树,完全二叉树

搜索二叉树:对于任何节点而言,左子树都比他小,右子树都比他大。
搜索二叉树在中序遍历(左中右)中是按照从小到大的顺序。

public static boolean isBST(Node head){
	if(head == null){
		return true;
	}
	boolean res = true;
	Node pre = null;
	Node cur1 = head;
	Node cur2 = null;
	while(cur1 != null){
		cur2 = cur1.left;
		if(cur2 != null){
			while(cur2.right != null && cur2.right != cur1){
				cur2 = cur2.right;
			}
			if(cur2.right == null){
				cur2.right = cur1;
				cur1 = cur1.left;
				continue;
			}else{
				cur2.right = null;
			}
		}
		if(pre != null && pre.value > cur1.value){
			res = false;
		}
		pre = cur1;
		cur1 = cur1.right;
	}
	return res;
}		

完全二叉树:

  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;
		//左边为空右边不为空或者开启了叶节点的阶段,则左右两个节点仍有子节点,则为false
		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;
}

已知一颗完全二叉树,求其节点的个数

思路:完全二叉树只有当一层满了之后才会进入下一层,所以可以先从左边遍历长度,计算出完全二叉树的高度,然后再从右边遍历长度,判断跟左边长度是否相等,如果不相等,再递归判断右子树的最左边界和左右两边的长度哪边相等,如果跟左边长度相等则证明左子树为完全二叉树,如果跟右边长度相等,则证明右子树为完全二叉树。

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;
	}
	//右子树的左最大高度如果跟h相同,则证明左子树为满二叉树,否则则证明右子树为满二叉树,可以计算出数目,下面(1<<(h-1))仅为优化后的写法,左移为放大,意味着2^(h-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;
}
发布了24 篇原创文章 · 获赞 6 · 访问量 500

猜你喜欢

转载自blog.csdn.net/qq_35065720/article/details/104157242