二叉树的相关的各种题目

  二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”。一棵深度为k,且有(2^k)-1个结点的二叉树,称为满二叉树。最少结点为k,为斜二叉树。

  了解了什么是二叉树后,自然要懂得它的三种遍历方法。前序(先序):中 -> 左 -> 右、中序:左 -> 中 -> 右、后序:左 -> 右 -> 中。(中是父节点,左是左节点,右是右节点)

递归版的三种遍历方式

先序

public static void PreOrder(TreeNode root) {
	if(root == null) return;  //判断根结点是不是为空
	System.out.println(root.val);   //直接输出根结点
	PreOrder(root.left);     //循环遍历左节点
	PreOrder(root.right);   //循环遍历右节点
}

中序

public static void InOrder(TreeNode root) {
	if(root == null) return;  //判断根结点是不是为空
	InOrder(root.left);     //循环遍历左节点
	System.out.println(root.val);   //直接输出根结点
	InOrder(root.right);   //循环遍历右节点
}

后序

public static void PostOrder(TreeNode root) {
	if(root == null) return;  //判断根结点是不是为空
	PostOrder(root.left);     //循环遍历左节点
	PostOrder(root.right);   //循环遍历右节点
	System.out.println(root.val);   //直接输出根结点
}

非递归版的三种遍历方式

先序

public static void PreOrder(TreeNode root) {
	Stack <TreeNode> stack = new Stack <TreeNode>();
	if(root==null) return;
	while(!stack.empty() || root!=null){
		while(root!=null){
			stack.push(root);
			System.out.println(root.val);  
			root = root.left;  //指向左节点
		}
		root=root.pop();  //取出栈顶元素
		root =root.right;  //遍历右孩子
	}
}

中序

public static void InOrder(TreeNode root) {
	Stack <TreeNode> stack = new Stack <TreeNode>();
	if(root==null) return;
	while(!stack.empty() || root!=null){
		while(root!=null){
			stack.push(root);
			root = root.left;  //指向左节点
		}
		System.out.println(root.val); 
		root=root.pop();  //取出栈顶元素
		root =root.right;  //遍历右孩子
	}
}

后序

public static void PostOrder(TreeNode root) {
	Stack <TreeNode> stack = new Stack <TreeNode>();
	if(root==null) return;
	while(!stack.empty() || root!=null){
		while(root!=null){
			stack.push(root);
			root = root.left;  //指向左节点
		}
	  root = stack.pop();
      if (root.right == null || root.right == prev) { //prev标记右节点
          System.out.println(root.val); 
           prev = root;
           root = null;//这一步很重要,输出节点之后,栈顶元素变为了根节点,但是不能直接输出
       } else {
           stack.push(root);
           root = root.right;
       }
	}
}

  对于非递归版的遍历方法,需要大家新建棵树去每一步的认真走一下流程,这样才能真正的理解,不至于死记硬背。我写的非递归版的都是利用栈这种数据结构,它和js中数组一样,这样的话大家理解起来也比较容易。

深度优先遍历

  深度优先遍历,也是利用栈来保存数据进行操作的。

//二叉树深度优先
		 *				    1
         *                 /  \
         *               2     3
         *              /  \   /\
         *             4  5   6  7
         * 结果是:1,2,4,5,3,6,7
     
  public static void  getDFS(TreeNode root){
	if(root == null) { return; }
	Stack <TreeNode> stack = new Stack <TreeNode>();
	stack.push(root);
	while(!stack.empty()){
		TreeNode node = stack.pop();	
		System.out.println(node.val); 
		if(node.right !=null) stack.push(node.right);
		if(node.left !=null) stack.push(node.left);
	}
  }

广度优先遍历

  上面的遍历都是运用栈来保存节点,而广度优先是利用队列来保存节点。二者的差别可自行百度

  广度优先是值先打印根节点,然后依次打印左节点和右节点(是指同一层次中的所有节点)

//二叉树广度优先
		 *				    1
         *                 /  \
         *               2     3
         *              /  \   /\
         *             4  5   6  7
         * 结果是:1,2,3,4,5,6,7

//代码实现
public static void getBFS(TreeNode root) {
	if(root == null) { return; }
	Queue<TreeNode> queue = new LinkedList<TreeNode>();
	queue.offer(root);
	while(!queue.isEmpty() || root!=null){
		TreeNode node = queue.poll();
		System.out.println(node.val);
		if(node.left!=null){   //队列是先进先出,每次出列的时候都把自己的左、右节点添加到队列中
			queue.offer(node.left);
		}
		if(node.right!=null){
			queue.offer(node.right);
		}
	}
}    

二叉搜索树的第k个结点

  二叉搜索树的特点是根节点大于左孩子,小于右孩子。

	 *				    4
     *                 / \
     *               2    6
     *              / \  / \
     *             1   3 5  7
     * 结果是:1234567
     * 
     * 主要是利用中序遍历,在弹出栈的时候记录count是否和 k 相等。
     * 
 public static void KthNode(TreeNode root,int k){
	Stack<TreeNode> stack = new Stack<TreeNode>();
	int count = 0;
	while(!stack.empty()||root!=null){
		while(root!=null){
			stack.push(root);
			root = root.left;
		}
		count++;  //计数
		if(count==k) return root;  //进行比较
		root = stack.pop();
		root = root.right;
	}
 }

二叉树中第k层结点个数

  使用递归来实现,首先需要了解的问题是:

  1. 根结点是第0层,所以当 k<0 时返回 0
  2. k=0 时,只有一个根结点,应该返回 1
  3. k>0 时,递归求 k-1 层左右结点个数相加
public int K_nodes(int k){
	if(k<0) return 0;
	return kNodes(root,k);
}
private int kNodes(TreeNode root,int k){
	if(root==null) return 0;
	if(k==0) return 1;  //只有根结点
	return kNodes(root.left,k-1)+kNodes(root.right,k-1); //递归计数
}

打印二叉树中和等于k的路径

  满足的和为k的路径是:最后一个结点要为叶子结点并且和等于k。

  在这我们会利用栈来保存满足的路径,最后遍历栈打印输出路径。

 *				    4
 *                 / \
 *               2    5
 *              / \  
 *             1   3
 * 若k=9 满足的两条路径是:4, 2, 34, 5 这两条路径
 *  
 public class sumK(TreeNode root,int k){
	Stack<TreeNode> stack = new Stack<TreeNode>();
	findPath(stack,root,k);
	void findPath(Stack<TreeNode> stack,TreeNode root,int k){
		if(root==null) return;
		stack.push(root);
		k-=root.val;  // k 减去当前结点值
		if(k==0 && root.left==null && root.right==null){  //判断是否满足条件
			for(TreeNode node:stack){   //把满足的路径打印出
				System.out.print(node.val);
			}
			System.out.println();
		}
		findPath(srack,root.left,k);  //遍历左节点
		findPath(stack,root.right,k);  //遍历右节点
		stack.pop();  //如果当前遍历节点为叶子节点,并且k!=0 把当前节点从栈顶弹出
	}
 }

二叉树中全部的路径

//这题是LeetCode上的题目,可以去看看,自己做做
class Solution {
   public List<String> binaryTreePaths(TreeNode root) {
        Stack<String> stack = new Stack<String>();
        if(root==null) return stack;
        String str="";
        AllPaths(stack,root,str);
        return stack;
    }
    void AllPaths(Stack<String> stack,TreeNode root,String str){
        if(root==null){return;}
        str+=root.val;
        if(root.left==null && root.right==null){
            stack.push(str);
            return;
        }
        if(root.left!=null) AllPaths(stack,root.left,str+"->");
        if(root.right!=null) AllPaths(stack,root.right,str+"->");
    }
}
// ["1->2->3","1->5"]
js版本(打印出和为sum的全部路径)
function FindPath(root, sum){
    if (root === null) return [];
    let res = [];
    const findSum = (root, stack, k, res) => {
        if (root === null) return;
        stack.push(root.val);
        k -= root.val;
        if (k === 0 && root.left === null && root.right === null) {
            res.push(stack.slice());
        }
        findSum(root.left, stack, k, res);
        findSum(root.right, stack, k, res);
        stack.pop()
    }
    findSum(root, [], sum, res);
    return res;
}
js版本(判断是否有和为sum的路径...和上一题有区别)
function hasPathSum( root ,  sum ) {
    if (root === null) return false;
    const findPath = (root, k) => {
        if (root === null) return false;
        k = k - root.val;
        if (k === 0 && root.left === null && root.right === null) {
            return true;
        }
       return findPath(root.left, k) || findPath(root.right, k); //重点
    };
    return findPath(root, sum);
}

额外的js简单编程题:

实现解析浏览器中url参数
/*最后 结果
{ user: ‘anonymous’,
id: [ 123, 456 ], // 重复出现的 key 要组装成数组
city: ‘北京’,
enabled: true, // 未指定值得 key 约定为 true
}
*/
url = ‘http://www.domain.com/?user=anonymous&id=123&id=456&city=“beijing”&enabled’;

function parseParam(url) {
    var obj={};
    var data=url.split('?')[1];  //若没有指定url,可以使用window.location.search
    var arr=data.split('&');  //分割后,保存到数组中
    for(let i=0;i<arr.length;i++){
        let key = arr[i].split('=')[0];
        let val = arr[i].split('=')[1];
        if(obj[key]){  //存在的情况下
            let res=[];   //定义一个数组,因为如果之前存在的话要保存为数组形式
            res=res.concat(obj[key]);  //把之前的值连接到数组中
            res.push(val);  //新解析的值添加进去 ["123","456"]
            obj[key]=res;   //别忘了添加到obj中
        }else if(!val){
            obj[key]=true;  //如果没有值的,默认为true
        }else{
            obj[key]=val;  //剩余情况
        }
    }
   return obj;
}

  斐波那契数列我想大家肯定都了解,之前写的时候可能会直接用递归和数组保存,这两种呢会消耗大量的内存空间,因为它保存了重复计算的值。

  上次面试的时候被问到如何进行优化数列,下面就来讲讲优化的方法。

function fibo(n){
	if(n<=0) return 0;
    var a=1,b=1;  //利用变量来保存结果
    if(n===1||n===2){
        return 1;
    }else{
        for(let i=3;i<=n;i++){
           let c=a;  
           a=b;     //每次计算后都把上次的值覆盖掉,大大节约了空间
           b=c+b;  //结果值
        }
        return b;  
   }
}

  以上是本篇博客为大家分享的知识点,每天进步一点点。希望和大家一起进步?

  也欢迎大家指出有误的地方,或者留下更好的解决方法?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值