二叉树常见算法

1、二叉树的遍历

1.1 前序遍历
中 -> 左 -> 右

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

1.2 中序遍历
左 -> 中 -> 右

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

1.3 后序遍历
左 -> 右 -> 中

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

2、判断一颗二叉树是否为平衡二叉树(AVL)

它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
思路1:从根节点开始,求出根的左右子树的高度,如果根的左右子树的高度差大于1,返回FALSE,否则递归的判断根的左子树和右子树是否满足条件。

public boolean IsBalanced_Solution(TreeNode root) {         
         if(root==null)
             return true;
//如果树为 null 返回 TRUE。否则判断根的左右子树的高度差的绝对值是否大于1,若大于1 则返回false。
// 否则判断树的左右孩子是否是平衡二叉树,当两者都是平衡二叉树时返回TRUE,否则返回false.
         else if(Math.abs(TreeDepth(root.left)-TreeDepth(root.right))>1)
             return false;
         else return IsBalanced_Solution(root.left)&&IsBalanced_Solution(root.right);
 
        }
        //求树的深度。
     public int TreeDepth(TreeNode root)
     {
         if(root==null)
             return 0;
             //如果树为 null 返回0 否则返回左右孩子的最大值+1。
         return Math.max(TreeDepth(root.left), TreeDepth(root.right))+1;

思路2: 上面的方法中,重复的计算子树的高度。可以用后序遍历,从下到上遍历如果子树中任一不满足条件返回 false,否则返回 true 这样每个节点的高度只会算一次。


public class IsBalancedTree {
    boolean isBalance=true;
    public boolean IsBalanced_Solution(TreeNode root) {         
         TreeDepth1(root);
         return isBalance;
        //isBalance 会在 TreeDepth1(root)中赋值。
        }
    public int TreeDepth1(TreeNode root)
     {
         if(root==null)
             return 0;
         int left=TreeDepth1(root.left);
         //左子树高度
         int right=TreeDepth1(root.right);
         //右子树高度
         if(Math.abs(left-right)>1)
         {
             isBalance=false;
             //只要有一个子树的左右子树的高度绝对值大于 1 isBalance=false
         }
         return Math.max(left, right)+1;

3、判断一棵树是否为搜索二叉树

对于任何节点而言,左子树都比他小,右子树都比他大。
**思路:**搜索二叉树在中序遍历(左中右)中是按照从小到大的顺序。
我们可以利用一个pre变量来保存前驱结点,并初始化pre为尽可能小的值。
反过来讲,如果存在一个当前结点小于其前驱结点,那么这棵树就不是二叉搜索树。

//判断给定的二叉树是否为BST树的改进算法
    boolean isBSTOfBetter(BinaryTreeNode<T> root,int pre) {
    	if(root == null)
    		return true;
    	//如果有一棵左子树不是BST树则返回false
    	if(!isBSTOfBetter(root.getLeft(), pre))
    		return false;
    	//如果当前结点小于前驱结点则返回false
    	if((int)root.getData() < pre)
    		return false;
    	//更新前驱结点
    	pre = (int) root.getData();
    	//如果有一棵右子树不是BST树则返回false
    	if(!isBSTOfBetter(root.getRight(), pre))
    		return false;
    	return true;
    }

4、判断一棵树是否为完全二叉树

完全二叉树:约定从根起,自上而下,自左而右,给满二叉树中的每个结点从1到n连续编号。
**思路:**根据完全二叉树的定义,对完全二叉树按照从上到下、从左到右的层次遍历,应该满足一下两条要求:
●某节点没有左孩子,则一定无右孩子
●若某节点缺左或右孩子,则其所有后继一定无孩子
若不满足上述任何一条,均不为完全二叉树。

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;
	}

5、trie树(前缀树/字典树)

基本性质
1,根节点不包含字符,除根节点意外每个节点只包含一个字符。
2,从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
3,每个节点的所有子节点包含的字符串不相同。

(1) 字符串检索
事先将已知的一些字符串(字典)的有关信息保存到trie树里,查找另外一些未知字符串是否出现过或者出现频率。
(2)文本预测、自动完成,see also,拼写检查
(3)词频统计

例子:
一个字符串类型的数组arr1,另一个字符串类型的数组arr2。arr2中有哪些字符,是arr1中出现的?请打印。arr2中有哪些字符,是作为arr1中某个字符串前缀出现?请打印。arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最多的前缀。

//前缀树节点
public static class TrieNode{
	public int pass; //经过节点的次数
	public int end; //作为字符串尾节点的次数
	public TriNode[] nexts; //节点next节点们的集合(字符种类多时,用HashMap<char, Node> nexts;

	public TrieNode(){
		pass = 0;
		end = 0;
		nexts = new TrieNode[26];
		//每个节点后都有26条路,连向26个字母,一开始全为null,next[0]代表a字母,next[1]代表b字母....
		//nexts[0] == null 没有走向‘a’的路
		//next[1] != null  有走向‘a’的路
	}

//建立前缀树
public static class Trie{
	//根节点
	private TrieNode root;
	public Trie(){
		root = new TrieNode();
	}
	//将word单词插进前缀树中
	public void insert(String word){
		if(word == null){
			return;
		}
		//分割字符串
		char[] chs = word.toCharArray();
		//标志节点指向根节点
		TrieNode node = root;
		//根节点的pass++
		node.pass++;
		//index代表nexts[index]中的下标
		int index = 0;
		for(int i = 0; i < chs.length; i++){
			//用ASCII来计算下一条路的下标,如‘a’-‘a’=0,则选next[0]的路
			index = chs[i] - 'a';
			//如果当前节点没有去nexts[index]的路,新建一条
			if(node.nexts[index] == null){
				node.nexts[index] = new TrieNode();
			}
			//然后node;来到下一个节点,即nexts[index]
			node = node.nexts[index];
			//pass++,继续循环遍历
			node.pass++;
		}
		//遍历完字符串则end++
		node.end++;
	}

	//查询word这个单词加入过前缀树几次
	public int search(String word){
		if(word == null){
			return 0;
		}
		//分割字符串
		char[] chs = word.toCharArray();
		//从根节点开始遍历
		TrieNode node = root;
		int index = 0;
		for(int i = 0, i < chs.length, i++){
			index = chs[i] - 'a';
			//如果出现有一个字符串没有路,则没有出现过这个word,直接返回0
			if(node.nexts[index] == null){
				return 0;
			}
			//node不断指向下一条路
			node = node.nexts[index];
		}
		//遍历完字符串后返回的end节点就是出现的次数
		return node.end;
	}
			
	//求所有加入的字符串中,有几个是以pre这个字符串作为前缀出现的
	public int prefixNumber(String pre){
		if(pre == null){
			return 0;
		}
		char[] chs = pre.toCharArray();
		TrieNode node = root;
		int index = 0;
		for(int i = 0, i < chs.length, i++){
			index = chs[i] - 'a';
			if(node.nexts[index] == null){
				return 0;
			}
			node = node.nexts[index];
		}
		return node.pass;
	}	
	
	//删除word字符串
	public void delete(String word){
		if(search(word) != 0){
			char[] chs = word.toCharArray();
			TrieNode node = root;
			node.pass--;
			int index = 0;
			for(int i = 0, i < chs.length, i++){
				index = chs[i] - 'a';
				if(--node.nexts[index].pass == 0){
					//只有java能这样 c++要析构来释放内存
					//遇到pass--后变0的节点,直接标空,后面的节点自动释放
					node.nexts[index] = null;
					return;
				}
				//没遇到就继续遍历,继续--node.pass
				node = node.nexts[index];
			}
			//删除完整个字符串后end要--
			node.end--;
		}
	}	
}				
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值