重建二叉树
我的想法:
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre==null || in==null || pre.length!=in.length) //对输入条件进行判断
return null;
return reConstructBinaryTree(pre,0,pre.length,in,0,in.length-1);
}
public TreeNode reConstructBinaryTree(int [] pre, int start1,int end1,int[] in,int start2,int end2){
if(start1>end1 || start2>end2) //判断输入
return null;
int rootData=pre[start1]; //根节点数值
TreeNode head=new TreeNode(rootData); //创建根节点
int rootIndex=findRootIndexOfIn(in,rootData,start2,end2); //找到中序中根节点所在位置序号
int offset=rootIndex-1-start2; //左子树的偏移量(大小)
//确定根节点在两个序列中的位置之后 分别对左右子树进行递归(同样操作)
TreeNode left=reConstructBinaryTree(pre,start1+1, start1+1+offset, in, start2,start2+offset);
TreeNode right=reConstructBinaryTree(pre, start1+offset+2,end1, in, rootIndex+1,end2);
head.left=left;
head.right=right;
return head; //返回根节点
}
/*找到根节点在中序所在位置*/
public int findRootIndexOfIn(int[] in,int x,int start,int end){
if(in==null || start>end)
return -1;
for(int i=start;i<=end;i++){
if(in[i]==x)
return i;
}
return -1;
}
}
最简思路:
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
if(pre==null || in==null || pre.length!=in.length) //对输入条件进行判断
return null;
return reConstructBinaryTree(pre,0,pre.length,in,0,in.length-1);
}
public TreeNode reConstructBinaryTree(int [] pre, int start1,int end1,int[] in,int start2,int end2){
if(start1>end1 || start2>end2)
return null;
TreeNode root=new TreeNode(pre[start1]);
for(int i=start2;i<=end2;i++){
if(in[i]==pre[start1]){
//int offset=i-1-start2; //偏移量
root.left=reConstructBinaryTree(pre,start1+1,start1+i-start2, in,start2,i-1);
root.right=reConstructBinaryTree(pre,start1+i-start2+1,end1,in,i+1,end2);
break; //找到根节点就需要退出循环
}
}
return root;
}
树的子结构
public class Solution {
public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
if (root2 != null && root1 != null) {
//如果找到了对应Tree2的根节点的点
if(root1.val == root2.val){
//以这个根节点为为起点判断是否包含Tree2
result = doesTree1HaveTree2(root1,root2);
}
//如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.left,root2);
}
//如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.right,root2);
}
}
//返回结果
return result;
}
public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
//如果Tree2已经遍历完了都能对应的上,返回true
if (node2 == null) {
return true;
}
//如果Tree2还没有遍历完,Tree1却遍历完了。返回false
if (node1 == null) {
return false;
}
//如果其中有一个点没有对应上,返回false
if (node1.val != node2.val) {
return false;
}
//如果根节点对应的上,那么就分别去子节点里面匹配
return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
}
二叉树的镜像
方法1:递归
public void Mirror(TreeNode root) {
if(root==null ||(root.left==null && root.right==null)) //空节点和叶子节点无需交换 直接返回 作为递归的结束条件
return ;
TreeNode tmp=root.left; //交换根节点的左右子节点,只要不是两个节点都为空即可
root.left=root.right;
root.right=tmp;
if(root.left!=null){ //若根节点存在左节点 则继续以此节点作递归
Mirror(root.left);
}
if(root.right!=null){
Mirror(root.right);
}
}
方法2:非递归(循环)用到栈public void Mirror(TreeNode root) {
if(root==null)
return;
Stack<TreeNode> s=new Stack<TreeNode>();
s.push(root); //压入根节点入栈
while(!s.empty()){
TreeNode tree=s.pop(); //弹出根节点
if(tree.left!=null || tree.right!=null){ //假如有子节点---则交换
TreeNode temp=tree.left;
tree.left=tree.right;
tree.right=temp;
}
if(tree.left!=null) //若左节点不为空 则继续压入
s.push(tree.left);
if(tree.right!=null)
s.push(tree.right);
}
}
从上往下打印二叉树
关键:模拟队列
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> al=new ArrayList<Integer> ();
ArrayList<TreeNode> queue=new ArrayList<TreeNode> (); //模拟队列
if(root==null) //若根节点为空 返回list为空
return al;
queue.add(root);
while(queue.size()!=0){
root=queue.remove(0);
al.add(root.val);
if(root.left!=null){
queue.add(root.left);
}
if(root.right!=null){
queue.add(root.right);
}
}
return al;
}
}
二叉搜索树的后序遍历序列
import java.util.Arrays;
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence==null || sequence.length<=0)
return false;
int length=sequence.length;
int root=sequence[length-1]; //最右边元素一定为根节点的值
int i=0; //找左右子树的分界点
for(;i<length-1;i++){
if(sequence[i]>root) //循环数组 若有比根节点值大的元素 则认为是分界点
break;
}
int j=i;
for( ;j<length-1;j++){ //在右子树中有比根节点值小的 则不符合要求
if(sequence[j]< root)
return false;
}
//到此为止 证明第一轮左右子树均符合要求 之后继续递归 分别判断左右子树
boolean left=true;
if(i>0) //分界点>0 证明有左子树
left=VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,i));//不包含i
boolean right=true;
if(i<length-1) //分界点<length-1 证明有右子树
right=VerifySquenceOfBST(Arrays.copyOfRange(sequence,i,length-1));
return right && left ;
}
}
二叉树 中和为某一值的路径
方法1: 借助于ArrayList结构(牛客网上参考)
public class Solution {
private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> list = new ArrayList<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null)
return listAll;
list.add(root.val);
target -= root.val;
//叶子节点且值同 则加路径
if(target == 0 && root.left == null && root.right == null)
listAll.add(new ArrayList<Integer>(list));
//没有到达叶子节点则 继续遍历左右节点
FindPath(root.left, target);
FindPath(root.right, target);
//回退时 去掉该节点 回到父节点
list.remove(list.size()-1); target+=root.val;
return listAll;
}
}
方法2 :用Stack 结构实现(我的想法)
public class Solution {
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
ArrayList<ArrayList<Integer>> al=new ArrayList<ArrayList<Integer>>();
if(root==null || target<=0)
return al;
Stack<Integer> s=new Stack<Integer>();
int curSum=0; //现有求和
findPath(root,target,s,curSum,al);
return al;
}
public ArrayList<ArrayList<Integer>> findPath(TreeNode root,int target,Stack<Integer> s,int curSum,ArrayList<ArrayList<Integer>> al){
ArrayList<Integer> all=new ArrayList<Integer>();
s.push(root.val); //压入根节点值
curSum+=root.val; //现有求和+根节点值
//已经到达叶子节点 则判断和是否满足给定值 若满足 则记录路径 若不满足 则回退至父节点
if(root.left==null && root.right==null){
if(curSum==target){ //若满足条件
for(int i:s ){
all.add(i);
}
al.add(all); //一条路径存入al中
}
}
//未到达叶子节点 则继续遍历节点
if(root.left!=null){
findPath(root.left,target,s,curSum,al);
}
if(root.right!=null){
findPath(root.right,target,s,curSum,al);
}
//返回到调用函数前 需要回退
root.val=s.pop();
curSum-=root.val;
return al;
}
}
二叉树的深度
递归法:
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int TreeDepth(TreeNode root) {
if(root ==null){
return 0;
}
int lDepth=TreeDepth(root.left);
int rDepth=TreeDepth(root.right);
return 1+(lDepth> rDepth? lDepth: rDepth);
}
}
二叉平衡树:
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root==null)
return true;
int ldepth=treeDepth(root.left); //分别求出每个左右节点的深度
int rdepth=treeDepth(root.right);
int dif=ldepth-rdepth; //判断是否超过1
if(dif<-1 || dif>1)
return false;
return IsBalanced_Solution(root.left)&& IsBalanced_Solution(root.right);
}
public int treeDepth(TreeNode root){
if(root==null)
return 0;
int ldepth=treeDepth(root.left);
int rdepth=treeDepth(root.right);
return 1+(ldepth> rdepth? ldepth: rdepth);
}
}
C++代码:
但是 Java中left 和right 不行(不是指针类型)
故 换用类的属性来代替深度:
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return isBalanced(root, new Holder());
}
//用类和对象的方式
private class Holder {
int depth;
}
boolean isBalanced(TreeNode root, Holder h) {
if (root == null) {
h.depth = 0;
return true;
}
Holder left = new Holder(); //相当于统计左节点的深度
Holder right = new Holder();
if (isBalanced(root.left, left) && isBalanced(root.right, right)) {
int diff=left.depth-right.depth;
if (diff>=-1 && diff<= 1){
h.depth =(left.depth > right.depth ? left.depth : right.depth) + 1;
return true;
}
}
return false;
}
}
二叉树的下一节点:
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
/*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null; //为父节点
TreeLinkNode(int val) {
this.val = val;
}
}
*/
/*分为几种情况
1 若节点为空 则返回空
2 若该节点有右孩子----则下一个节点是其右孩子的最左的孩子
3 若该节点有父节点---是父节点的左孩子 下一节点则是父节点 ----是父节点的右孩子,下一节点则是父节点的父节点-直到其是某个父节点的左孩子
4 如果直到根节点都没有 则返回null
*/
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode==null)
return null;
//若该节点有右孩子
if(pNode.right!=null){
pNode=pNode.right;
while(pNode.left!=null){
pNode=pNode.left;
}
return pNode;
}
//若有父节点
while(pNode.next!=null){
if(pNode.next.left==pNode)
return pNode.next;
//不是左节点
pNode=pNode.next; //继续找父节点
}
//到根节点了 还没找到 即pNode.next==null
return null;
}
}
对称的二叉树:
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
方法1 :用递归的思路
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
/*思路:首先根节点以及其左右子树,
左子树的左子树和右子树的右子树相同
* 左子树的右子树和右子树的左子树相同即可,采用递归
* 非递归也可,采用栈或队列存取各级子树根节点
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null){
return true;
}
return comRoot(pRoot.left, pRoot.right);
}
private boolean comRoot(TreeNode left, TreeNode right) {
if(left == null && right==null)
return true;
if(right == null || left==null)
return false;
//若左右子树值不同 则false;
if(left.val != right.val)
return false;
//递归比较左子树的左子树和右子树的右子树相同 / 左子树的右子树和右子树的左子树相同
return comRoot(left.left, right.right) &&comRoot(left.right, right.left) ;
}
}
方法2 :用非递归的方法(我采用队列思想)
import java.util.LinkedList;
import java.util.Queue;
//采用非递归的做法 因为是前序遍历 --所以用队列来做比较好 根左右
public class Solution {
boolean isSymmetrical(TreeNode pRoot){
if(pRoot==null)
return true;
//生成两个队列
Queue<TreeNode> q1=new LinkedList<>();
Queue<TreeNode> q2=new LinkedList<>();
TreeNode left;
TreeNode right;
//使用队列 --入队根节点左右子节点
q1.add(pRoot.left);
q2.add(pRoot.right);
//开始循环判断 前提是队列非空
while(!q1.isEmpty() && !q2.isEmpty()){
left=q1.poll();
right=q2.poll();
//将从两个队列中分别弹出的元素进行比较
if(left==null && right==null){
continue; //注意!!之前直接return true 是不对的,这里是continue
}
if(left==null || right==null)
return false;
if(left.val!=right.val)
return false;
//经过考验的证明当前节点层符合要求
//继续考察其子节点们
q1.add(left.left);
q1.add(left.right);
q2.add(right.right);
q2.add(right.left);
}
return true;
}
}
按照之字形打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
方法1 :利用LinkedList的双向性 (构建队列)
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Iterator;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
/*将每层的数据存进ArrayList中,偶数层时进行reverse操作,
* 在海量数据时,这样效率太低了。
* 下面的实现:不必将每层的数据存进ArrayList中,偶数层时进行reverse操作,直接按打印顺序存入
* 思路:利用Java中的LinkedList的底层实现是双向链表的特点。
* 1)可用做队列,实现树的层次遍历
* 2)可双向遍历,奇数层时从前向后遍历,偶数层时从后向前遍历
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ret=new ArrayList<>();
if(pRoot==null)
return ret;
//生成队列和每一层记录的列表
ArrayList<Integer> list=new ArrayList<>();
LinkedList<TreeNode> queue=new LinkedList<>();
//定义行分隔符 null 并压入最初的pRoot
queue.addLast(null); //每一层起始的分隔符
queue.addLast(pRoot);
boolean leftToRight=true ; //定义打印顺序标记
//循环结束 队列中只有最后一个null值 即所有节点均打印完毕 最后一批是叶子节点没有左右子节点 故只加了null标记
while(queue.size()!=1){
//从队列中取出当前首元素
TreeNode node=queue.removeFirst();
//如果是null 则证明该打印数据--按照leftToRight标记进行顺序/逆序打印
if(node==null){
Iterator<TreeNode> iter=null;
if(leftToRight)
iter=queue.iterator();
else
iter=queue.descendingIterator();
//规定好顺序 令标记取反 以便下回使用
leftToRight=!leftToRight;
//开始打印null后的所有值
while(iter.hasNext()){
TreeNode temp=(TreeNode)iter.next();
list.add(temp.val); //list记录一层的节点值
}
ret.add(new ArrayList<Integer> (list)); // 注意!!!list是引用变量,你所有添加的list都是引用的这一个list
list.clear(); //清空上次记录的内容
queue.addLast(null); //继续记录下层开始的标记
continue; //若弹出首节点为null 则不用继续之后压入左右节点的操作 直接继续下次循环
}
//若node不是开始标记 则继续压入其非空节点到队列中
if(node.left!=null){
queue.addLast(node.left);
}
if(node.right!=null){
queue.addLast(node.right);
}
}
return ret;
}
}
优化体现在:
1. 这个代码并不是每次new 一个ArrayList用来存储,如果每次new 一个ArrayList,会在堆建立数据,在栈创建地址引用,该代码使用匿名内部类,没有创建栈中的引用,对于大量数据来说,确实省去了很多在栈中的内存开销
2. 该代码利用Java中的LinkedList的底层实现是双向链表的特点,由于是链表是双向的,所以他在实现反向遍历的时候和正向是一样的
补充:
方法2 :利用Stack栈的特性
public class Solution {
public static ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
int layer = 1;
//s1存奇数层节点
Stack<TreeNode> s1 = new Stack<TreeNode>();
s1.push(pRoot);
//s2存偶数层节点
Stack<TreeNode> s2 = new Stack<TreeNode>();
ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
while (!s1.empty() || !s2.empty()) {
if (layer%2 != 0) {
ArrayList<Integer> temp = new ArrayList<Integer>();
while (!s1.empty()) {
TreeNode node = s1.pop();
if(node != null) {
temp.add(node.val);
System.out.print(node.val + " ");
s2.push(node.left);
s2.push(node.right);
}
}
if (!temp.isEmpty()) {
list.add(temp);
layer++;
System.out.println();
}
} else {
ArrayList<Integer> temp = new ArrayList<Integer>();
while (!s2.empty()) {
TreeNode node = s2.pop();
if(node != null) {
temp.add(node.val);
System.out.print(node.val + " ");
s1.push(node.right);
s1.push(node.left);
}
}
if (!temp.isEmpty()) {
list.add(temp);
layer++;
System.out.println();
}
}
}
return list;
}
}
把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
方法1 : 我沿用上一个题的思路 使用null作为每一层的标记 结合队列来打印
import java.util.ArrayList;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.LinkedList;
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//层序遍历 并且按行打印 ---先序遍历
ArrayList<ArrayList<Integer>> ret=new ArrayList<>();
if(pRoot==null)
return ret;
ArrayList<Integer> list=new ArrayList<Integer>();
LinkedList<TreeNode> queue=new LinkedList<>();
queue.addLast(null); //行标记
queue.addLast(pRoot);
while(queue.size()!=1){
TreeNode temp=queue.removeFirst();
if(temp==null){
for(TreeNode t: queue){
list.add(t.val);
}
ret.add(new ArrayList<Integer>(list));
list.clear();
queue.addLast(null);
continue;
}
if(temp.left!=null){
queue.addLast(temp.left);
}
if(temp.right!=null){
queue.addLast(temp.right);
}
}
return ret;
}
}
方法2 :参考他人思路 队列: 用loc / h 直接限定一层的规模 在此区间内打印
import java.util.LinkedList;
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//层序遍历 并且按行打印 ---先序遍历
ArrayList<ArrayList<Integer>> ret=new ArrayList<>();
if(pRoot==null)
return ret;
ArrayList<Integer> list=new ArrayList<Integer>();
LinkedList<TreeNode> queue=new LinkedList<>();
queue.addLast(pRoot);
//当队列不为空的情况下
while(!queue.isEmpty()){
int loc=0; //定义当前节点输出位置
int h=queue.size(); //该层总的规模
while(loc++<h){ //在当前层上进行顺序打印
TreeNode temp=queue.removeFirst();
list.add(temp.val);
if(temp.left!=null)
queue.addLast(temp.left);
if(temp.right!=null)
queue.addLast(temp.right);
}
//一层记录之后 再保存到总的列表中
ret.add(new ArrayList<Integer>(list));
list.clear(); //注意!!一定记得清除每一层打印的节点
}
return ret;
}
}
方法3 :喜欢的思路!! 队列: 记录当前层节点数目 和下一层节点数 并不断更新
import java.util.LinkedList;
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
//层序遍历 并且按行打印 ---先序遍历
ArrayList<ArrayList<Integer>> ret=new ArrayList<>();
if(pRoot==null)
return ret;
ArrayList<Integer> list=new ArrayList<Integer>();
LinkedList<TreeNode> queue=new LinkedList<>();
queue.addLast(pRoot);
//定义当前层节点数
int current=1;
//定义下一层节点数
int nextLev=0;
while(!queue.isEmpty()){
TreeNode temp=queue.pop();
list.add(temp.val);
current--; //弹出一个则计数--
if(temp.left!=null){
queue.add(temp.left);
nextLev++; //新加入一个则下一层节点数目+1
}
if(temp.right!=null){
queue.add(temp.right);
nextLev++;
}
if(current==0){ //当当前层节点全部打印完,则用ret记录一层节点结果
current=nextLev; //并将当前层数更新为下一层数目
nextLev=0; //下一层又重新恢复到0
ret.add(new ArrayList<Integer>(list));
list.clear();
}
}
return ret;
}
}
相关的有 打印每层行号问题:
序列化二叉树:
请实现两个函数,分别用来序列化和反序列化二叉树
方法1: 通过先序遍历 实现(递归做法) 超级简单
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.LinkedList;
public class Solution {
//序列化 先序遍历---每次读完节点j加!结束标志 若节点为空则#!
String Serialize(TreeNode root) {
if(root==null){
return "#!"; //如果根节点为空 则直接返回#!表示
}
String res=root.val+"!"; //根节点
res+=Serialize(root.left); //左节点
res+=Serialize(root.right); //右节点
return res;
}
//反序列化的过程就是将字符串-->字符串数组--->重新构建一棵树
TreeNode Deserialize(String str) {
String [] tree=str.split("!"); //将字符串根据分隔符号进行数组划分
LinkedList<String> queue=new LinkedList<>();
for(int i=0;i<tree.length;i++){
queue.add(tree[i]); //用队列进行记录
}
return initialTree(queue);
}
public TreeNode initialTree(LinkedList<String> queue){ //重新构建树的函数
String temp=queue.poll();
if(temp.equals("#")){ //若弹出元素为#则代表此节点为空
return null;
}
TreeNode head=new TreeNode(Integer.valueOf(temp)); //构造根节点
head.left=initialTree(queue); //然后是左节点
head.right=initialTree(queue); //最后是右节点部分
return head;
}
}
方法2: 通过层序遍历 实现(递归做法) 超级简单
import java.util.LinkedList;
public class Solution {
//序列化 先序遍历---每次读完节点j加!结束标志 若节点为空则#! 使用层序遍历的思想 循环解决问题
String Serialize(TreeNode root) {
if(root==null){
return "#!";
}
String res=root.val+"!";
LinkedList<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode temp=queue.poll();
if(temp.left!=null){
res=res+temp.left.val+"!";
queue.add(temp.left);
}else{
res+="#!";
}
if(temp.right!=null){
res=res+temp.right.val+"!";
queue.add(temp.right);
}else{
res+="#!";
}
}
return res;
}
TreeNode Deserialize(String str) {
String [] values=str.split("!");
int index=0;
TreeNode head=generateNodeByString(values[index++]);
LinkedList<TreeNode> queue=new LinkedList<>();
if(head!=null){
queue.offer(head);
}
TreeNode 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 TreeNode generateNodeByString (String val){
if(val.equals("#"))
return null;
return new TreeNode(Integer.valueOf(val));
}
}
二叉搜索树的第k个结点
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
方法1 :二叉搜索树的中序遍历(也就是按照顺序打印) “递归”的思路
public class Solution {
int count=0; //定义计数器
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot==null || k==0)
return null;
//根节点不为空 从其左节点开始
TreeNode node=KthNode(pRoot.left, k);
if(node!=null)
return node;
if(++count==k) //如果找到第K个顺序节点 则直接返回当前节点
return pRoot;
//左节点未找到 根也没对上 则找寻右节点
node=KthNode(pRoot.right,k);
if(node!=null)
return node;
return null;
}
}
方法2 :完全模拟中序遍历的“非递归”方式 适当修改
import java.util.Stack;
public class Solution {
int count=0; //定义计数器
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot==null || k==0)
return null;
//采用非递归去做 完全类似于非递归的中序遍历
Stack<TreeNode> s=new Stack<TreeNode>();
while(pRoot!=null || !s.isEmpty()){
if(pRoot!=null){
s.push(pRoot);
pRoot=pRoot.left ; //不断找最左节点
}else{
pRoot=s.pop();
count++;
if(count==k)
return pRoot;
pRoot=pRoot.right;
}
}
return null;
}
}
数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
方法1: 用优先队列模拟两个堆,一个最大堆,一个最小堆 。
时间:nO(log(n/2)) 空间O(log(n/2))
/***********方式一、用两个优先队列来模拟两个堆---主要思路************************
1.先用java集合PriorityQueue来设置一个小顶堆和大顶堆,大顶堆需要先重写一下里面的比较器
2.主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
小顶堆存较大的数,从小到大的顺序排序,
显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
3.当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中
这样就可以保证,每次插入新值时,都保证小顶堆中值大于大顶堆中的值,并且都是有序的。
4.由于第一个数是插入到小顶堆中的,所以在最后取中位数的时候,若是奇数,就从小顶堆中取即可。
这样,当count为奇数的时候,中位数就是小顶堆的根节点;当count为偶数的时候,中位数为大顶堆和小顶堆两个根节点之和的平均数
5.例如,传入的数据为:[5,2,3,4,1,6,7,0,8],那么按照要求,输出是"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
a.那么,第一个数为5,count=0,那么存到小顶堆中,
步骤是:先存到大顶堆;然后弹出大顶堆root,就是最大值给小顶堆,第一次执行完,就是小顶堆为5,count+1=1;
此时若要输出中位数,那么就是5.0,因为直接返回的是小顶堆最小值(第一次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
b.继续传入一个数为2,那么先存到小顶堆中,将小顶堆最小值弹出给大顶堆,即2,那么这次执行完,小顶堆为5,大顶堆为2,count+1=2
此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(5+2)/2=3.5(第二次塞入到大顶堆中,是从小顶堆中找到最小的给他的)
c.继续传入一个数为3,那么此时count为偶数,那么执行第一个if,先存到大顶堆中,大顶堆弹出最大值,那么3>2,就是弹出3
3存到小顶堆中,那么此时小顶堆为3,5,大顶堆为2,count+1=3(第三次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
此时若要输出中位数,因为是奇数,那么取小顶堆的最小值,即3.0
d.继续传入一个数为4,先存到小顶堆中,小顶堆此时为3,4,5,弹出最小值为3,给大顶堆
此时大顶堆为3,2,小顶堆为4,5,(第四次塞入到小顶堆中,是从大顶堆中找到最大的给他的)
此时若要输出中位数,因为是偶数,那么取两个头的平均值,即(3+4)/2=3.5
e.依次类推。。。
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
//建立一个计数器 统计奇偶数
int count=0;
//建立两个堆去处理数据 相当于将数据流分为两部分(按照大 小 顺序)
//优先队列默认是升序(最小堆构造)--要想改为最大堆则需要将比较的方法进行更改
PriorityQueue<Integer> minHeap=new PriorityQueue<>();
PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer o1, Integer o2){
return o2-o1;
}
});
//新增数据
public void Insert(Integer num) {
//偶数情况下 数据经最大堆---到最小堆--连接成一个数组(前半段--后半段)
if(count%2==0){
maxHeap.offer(num);
Integer temp=maxHeap.poll();
minHeap.offer(temp);
}else{ //为奇数时 数据经最小堆---最大堆--同样连接成一个数组--中位数在最小堆里
minHeap.offer(num);
Integer temp=minHeap.poll();
maxHeap.offer(temp);
}
count++; //存入一个则计数一次
}
//获取元素
public Double GetMedian() {
if(count%2==0){
return new Double(maxHeap.peek()+minHeap.peek())/2;
}else{
return new Double(minHeap.peek());
}
}
}
方法2: 不用堆 直接用列表 每次加入一个元素总是排序
/***************方式二、ArrayList***********************
用ArrayList来存输入的数据流,然后每次用Collections.sort(list)来保证数据流有序,然后再取中位数
思想非常简单,但是每次都要进行排序,时间复杂度可想而知
时间O(nlogn)*n 空间O(n)
import java.util.*;
public class Solution {
ArrayList<Integer> list = new ArrayList<Integer>();
public void Insert(Integer num) {
list.add(num);
Collections.sort(list); //每次插入新元素总是要排序
}
public Double GetMedian() {
int mid = list.size() / 2; //选择中间位置
if((list.size()&1) == 1) return list.get(mid)/1.0; //奇数情况下
else return (list.get(mid-1) + list.get(mid))/2.0; //偶数情况下
}
}
方法3: 插入排序
/***************方式三、插入排序,插入到对应的位置***********************
LinkedList<Integer> data = new LinkedList<Integer>();
public void Insert(Integer num) {
for (int i = data.size() - 1; i >= 0 ; i--) {
if (num >= data.get(i)){
data.add(i+1,num);
return;
}
}
data.addFirst(num);
}
****************************************/