当前位置:   article > 正文

Java数据结构--二叉树笔记_java完全二叉树

java完全二叉树

一颗二叉树是结点的一个有限集合,该集合

1.或者为空

2.或者是由一个根节点加上两颗分别为左子树 右子树的二叉树组成

二叉树的性质

1.若规定根节点的层数为1,则一颗非空二叉树的第i层最多有2^(i-1) 个结点(i大于零)

2.若规定只有根节点的二叉树的深度为1,则深度为k的二叉树的最大结点数为2^k - 1 (k>=0) 

3.对任何一颗二叉树,如果其叶结点个数为n0,度为2的非叶节点的个数为n2 ,则有n0 = n2 +1

4.具有n个节点的完全二叉树的深度为log2(n+1)上取整

5.具有n个节点的完全二叉树,如果按照从上至下 从左至右的顺序对所有节点从0开始编号,则对于序号为i的节点有:

  若 i > 0 ,双亲序号:(i-1)/ 2 ,若i=0则i为根节点编号

  若2i+1<n,左孩子序号:2i+1,否则无左孩子

  若2i+2<n,右孩子序号为2i+2,否则无右孩子

需要注意的是节点个数为奇数个的完全二叉树没有度为1的节点,如下图

二叉树相关的oj题

 1.给两颗二叉树的根节点p和q,编写一个函数来检验这两棵树是否相同

如果两个树在结构上相同,并且节点具有相同的值,则认为他们是相同的

思路:先判断pq是否为空,若一个为空一个不为空,则必定不一样,返回false,若均为空,则视为相同,返回true,若均不为空,则用递归的思想 继续判断左子树和右子树是否相同

  1. class Solution{
  2. public boolean isSameTree(TreeNode p,TreeNode q){
  3. if (p != null && q == null || p==null && q!= null) {
  4. return false;//结构不一样
  5. }
  6. if (p==null&&q==null)return true;//两个都为空,相同
  7. if (p.val!=q.val){//两个均不为空,继续比较
  8. return false;
  9. }
  10. //上述代码完成走到这里说明 p!= null && q != null && 根节点的值一致
  11. return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
  12. }
  13. }

2.检验其中一棵树是否为另一棵树的子树

给出root 和 subRoot 检验root中是否包含与subRoot相同的结构和节点值的子树,如果存在返回true,否则返回false

root的每一个节点和subRoot中每一个节点比较一次,看是否相同,若m n 分别为两棵树的节点个数,则时间复杂度为m*n

  1. class Solution2{
  2. public boolean isSubtree(TreeNode rood,TreeNode subRoot){
  3. if (rood == null)return false;
  4. if (isSameTree(rood,subRoot)){
  5. return true;
  6. }
  7. if (isSameTree(rood.left,subRoot)){
  8. return true;
  9. }
  10. if (isSameTree(rood.right,subRoot)){
  11. return true;
  12. }
  13. return false;
  14. }
  15. public boolean isSameTree(TreeNode p,TreeNode q){
  16. if (p != null && q == null || p==null && q!= null) {
  17. return false;//结构不一样
  18. }
  19. if (p==null&&q==null)return true;//两个都为空,相同
  20. if (p.val!=q.val){//两个均不为空,继续比较
  21. return false;
  22. }
  23. //上述代码完成 走到这里说明 p!= null && q != null && 根节点的值一致
  24. return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
  25. }
  26. }

3.翻转二叉树

思路:首先判断二叉树是否为空,若为空则返回false,若树不为空,则递归交换根节点root的左子树和右子树

  1. class Solution3 {
  2. public TreeNode changeNode(TreeNode root){
  3. if ((root.right==null&&root.left!=null) ||(root.right!= null&&root.left==null))
  4. return root;
  5. if (root == null)return null;
  6. TreeNode tmp = new TreeNode('a');
  7. tmp = root.left;
  8. root.left = root.right;
  9. root.right = tmp;
  10. caculateSize(root.left);
  11. caculateSize(root.right);
  12. return root;
  13. }
  14. }
  15. }

4.平衡二叉树

给定一个二叉树,判断他是否是高度平衡的二叉树(一个二叉树每个节点的左右两个子树的高度的绝对值不超过1)

  1. class Solution{
  2. public boolean isBalance(TreeNode root){
  3. if (root == null){
  4. return true;
  5. }
  6. int leftH = getHeight(root.left);
  7. int rightH = getHeight(root.right);
  8. if (Math.abs(leftH - rightH)<2 && isBalance(root.right) && isBalance(root.left)){
  9. return true;
  10. }
  11. return false;
  12. }
  13. }
  1. class Solution5{//判断是否是平衡二叉树
  2. public int isBalance(TreeNode root){
  3. if (root == null){
  4. return 0;
  5. }
  6. int leftH = getHeight(root.left);
  7. int rightH = getHeight(root.right);
  8. //不平衡则返回负数
  9. if (leftH>=0 && rightH>=0 && Math.abs(leftH - rightH)<=0){
  10. return Math.max(rightH,leftH)+1;
  11. }else return -1;
  12. }
  13. }
  1. class Solution5{//判断是否是平衡二叉树
  2. public int isBalance(TreeNode root){
  3. if (root == null){
  4. return 0;
  5. }
  6. int leftH = getHeight(root.left);
  7. if (leftH < 0){
  8. return -1;
  9. }
  10. int rightH = getHeight(root.right);
  11. //不平衡则返回负数
  12. if (leftH>=0 && rightH>=0 && Math.abs(leftH - rightH)<=0){
  13. return Math.max(rightH,leftH)+1;
  14. }else return -1;
  15. }
  16. }

二叉树的遍历问题

1.二叉树的层序遍历

2.1使用非递归的方法,使用队列进行操作,首先root指针指向根节点,判断是否有左右孩子,若有,则将左右孩子按照从左往右的顺序依次入队,再对root进行出队打印,第一次出队root指向根节点,第二次出队的root更新为root.right,同时判断root是否有左右孩子,若有,任然按照从左至右的顺序入队,再对root出队打印,以此往复

  1. public void leveOrder(treeNode root){
  2. if (root == null)return;
  3. Queue<treeNode> queue = new LinkedList<>();
  4. queue.offer(root);
  5. while (!queue.isEmpty()){
  6. treeNode cur = queue.poll();
  7. System.out.println(cur.val + " ");
  8. if (cur.left != null){
  9. queue.offer(cur.left);
  10. }
  11. if (cur.right != null){
  12. queue.offer(cur.right);
  13. }
  14. }
  15. }
  1. public List<List<Character>> levelOrder2(treeNode root){
  2. List<List<Character>> listList = new ArrayList<>();
  3. if (root == null)return listList;
  4. Queue<treeNode> queue = new LinkedList<>();//申请一个队列用于存放树的每一个节点
  5. queue.offer(root);//根节点入队
  6. while (!queue.isEmpty()){
  7. //用队列里面的节点个数来控制二维数组中每个元素的大小
  8. int size = queue.size();
  9. List<Character> list = new ArrayList<>();
  10. while (size != 0){
  11. treeNode cur = queue.poll();//出队打印,并判断是否有左右孩子,若有按照从左至右的顺序入队
  12. list.add(cur.val);
  13. size--;
  14. System.out.println(cur.val + " ");
  15. if (cur.left != null){
  16. queue.offer(cur.left);
  17. }
  18. if (cur.right != null){
  19. queue.offer(cur.right);
  20. }
  21. }
  22. listList.add(list);
  23. }
  24. return listList;
  25. }

2.判断一颗二叉树是否为完全二叉树(特殊的层序遍历)

1.先把根节点入队

2.队列不为空,弹出元素,每次都用cur 记录,入队cur的左右孩子(可以为空,为空则入队null)

3.当遇cur遇到null时则停下判断队列里是否全为null,若是,则该二叉树为完全二叉树,否则不是完全二叉树

  1. public boolean isCompletelyTree(treeNode root){
  2. if (root == null)return true;
  3. else {
  4. Queue<treeNode> queue = new LinkedList<>();
  5. queue.offer(root);
  6. while (!queue.isEmpty()){
  7. treeNode cur = queue.poll();
  8. if (cur != null){
  9. queue.offer(cur.right);
  10. queue.offer(cur.right);
  11. }
  12. else break;//遇到cur为空,下一步判断队列里面是否全部为空
  13. }
  14. while (!queue.isEmpty()){
  15. treeNode treeNode = queue.poll();
  16. if (treeNode != null){
  17. return false;
  18. }
  19. }
  20. }
  21. return true;
  22. }

3.给定一颗二叉树,找到该树中两个指定节点p,q,的最近公共祖先

cur从根节root点开始遍历每个节点,找到p,q,判断p,q是否在root的来两侧,若pq在root的两侧,则root即为最近公共祖先,若cur已经遍历了root的左侧都没有找到p或者q,那么说明pq在root的右侧,此时移动root至root的右侧(root = root.right),再执行cur = root,cur继续从新的root遍历,寻找p或者q,判断是否在root的两侧,若是,则root即为最近公共祖先,若先找到p或者q,则先找到的节点即为最近公共祖先

总的来说就是递归判断pd是否在当前节点的两侧,若是则当前节点即为所求,若不是则先遇节点即为所求

  1. public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p,TreeNode q){
  2. if (root == null || root == p ||root == q){
  3. return root;
  4. }
  5. TreeNode left = lowestCommonAncestor(root.left,p,q)
  6. TreeNode right = lowestCommonAncestor(root.right,p,q)
  7. if (left != null && right != null) {//p q 分布在两侧
  8. return root;
  9. } else if (left != null) {//p q 分布在一侧,先找到哪个,哪个就为最近祖先
  10. return left;
  11. } else {
  12. return right;
  13. }
  14. }

方法二,利用两个栈来分别存放从根节点到 p,q 节点的路径节点,节点个数可能不一样,较长栈 减去两栈长度差值直到两栈长度相同,再同时出栈,直到拿到val相同的节点,该节点即为最近祖先

关于如何确定指定节点p的路径?

1.cur 从根节点开始遍历,左右孩子不为空就入栈

2.同时判断该节点是否为p节点

3.当穷尽一条路时仍然没有找到p节点,并且此时cur所指向的节点是没有左右孩子的,就说明但前路径不是所求路径

4.出栈,继续递归遍历,直到找到p节点,栈里所存放的节点即为寻找p节点的路径

  1. public boolean getPath(TreeNode node , TreeNode root, Stack<TreeNode> stack){
  2. if (root == null){
  3. return false;
  4. }
  5. stack.push(root);
  6. if (root == node){
  7. return true;
  8. }
  9. boolean flgLeft = getPath(node,root.left,stack);
  10. if (flgLeft == true){
  11. return true;
  12. }
  13. boolean flgRight = getPath(node,root.right,stack);
  14. if (flgRight == true){
  15. return true;
  16. }
  17. stack.pop();
  18. return false;
  19. }

按照以上方法求出路径,接下来就可以利用栈来求最近公共祖先

  1. public TreeNode lowestCommonAncestor2(TreeNode root,TreeNode p,TreeNode q){
  2. if (root == null){
  3. return null;
  4. }
  5. Stack<TreeNode> stack1 = new Stack<>();
  6. Stack<TreeNode> stack2 = new Stack<>();
  7. getPath(p,root,stack1);
  8. getPath(q,root,stack2);
  9. while (stack1.size() > stack2.size()){
  10. stack1.pop();
  11. }
  12. while (stack2.size() > stack1.size()){
  13. stack2.pop();
  14. }
  15. while (!stack2.isEmpty() && !stack1.isEmpty()){
  16. if (stack2.peek().equals(stack1.peek())){
  17. return stack2.pop();
  18. }else {
  19. stack2.pop();
  20. stack1.pop();
  21. }
  22. }
  23. return null;
  24. }

4.已知先序遍历和中序遍历 ,创建一颗二叉树

1.先遍历先序遍历的字符串,用cur指向

2.在 中序遍历 的指定区间内 找到cur所指向 的 相应节点,递归创建左数和右数

  1. class Solution1{//已知后序遍历和中序遍历创建一颗二叉树
  2. //postorder 给出的后序遍历序列
  3. //inorder 给出的中序遍历序列
  4. public int postIndex; //记录后序遍历序列的下标
  5. public TreeNode buildTree(int[] postorder,int[] inorder){
  6. int inBegin = 0;//inBegin 中序序列中 查找节点时 的 起始查找下标,初始值为0
  7. int inEnd = postorder.length - 1;//inEnd中序序列中 查找节点时 的 结束查找下标,初始值为length - 1
  8. postIndex = postorder.length -1;
  9. TreeNode root = buildTreeChild(postorder,inorder,inBegin,inEnd);
  10. return root;
  11. }
  12. private TreeNode buildTreeChild(int[] postorder,int[] inorder,int inBgin,int inEnd){
  13. if (inEnd >inBgin){
  14. return null;
  15. }
  16. TreeNode root = new TreeNode((char) postorder[postIndex]);//1.创建根节点
  17. //2.找到 postorder中 下标为postIndex 的节点 在inorder序列中 的下标,用 rootIndex 记录
  18. int rootIndex = FindRootIndex(inorder,inBgin,inEnd,postorder[postIndex]);
  19. postIndex--;
  20. //3.分别创建右子树和左子树
  21. root.right = buildTreeChild(postorder,inorder,rootIndex + 1,inEnd);
  22. root.left = buildTreeChild(postorder,inorder,inBgin,rootIndex - 1);
  23. return root;
  24. }
  25. private int FindRootIndex(int[] inorder,int inBgin,int inEnd,int key){//用于步骤2中寻找下标
  26. //inorder 中序遍历序列,被查找序列
  27. //inBgin 起始位置
  28. //inEnd 结束位置
  29. //查找对象的下标
  30. int i = inBgin;
  31. while (i <= inEnd){
  32. if (inorder[i] == key){
  33. return i;
  34. }
  35. i++;
  36. }
  37. return -1;
  38. }
  39. }
  40. }

5.已知中序遍历和后序遍历序列创建二叉树

 思路和 已知 先中序列创建二叉树 一样,唯一不同的就是后序遍历序列中,根节点在数组的最后,因此 要从后序遍历序列的 最后一个元素开始 向前遍历数组寻找根节点

  1. class Solution{//已知先序遍历和中序遍历创建一颗二叉树
  2. //preorder 给出的先序遍历序列
  3. //inorder 给出的中序遍历序列
  4. public int preIndex = 0; //记录先序遍历序列的下标
  5. public TreeNode buildTree(int[] preorder,int[] inorder){
  6. int inBegin = 0;//中序序列中 查找节点时 的 起始查找下标,初始值为0
  7. int inEnd = inorder.length - 1;//中序序列中 查找节点时 的 结束查找下标,初始值为length - 1
  8. TreeNode root = buildTreeChild(preorder,inorder,inBegin,inEnd);
  9. return root;
  10. }
  11. private TreeNode buildTreeChild(int[] preorder,int[] inorder,int inBgin,int inEnd){
  12. if (inEnd >inBgin){
  13. return null;
  14. }
  15. TreeNode root = new TreeNode((char) preorder[preIndex]);//1.创建根节点
  16. //2.找到 preorder中 下标为preIndex 的节点 在inorder序列中 的下标,用 rootIndex 记录
  17. int rootIndex = FindRootIndex(inorder,inBgin,inEnd,preorder[preIndex]);
  18. preIndex++;
  19. //3.分别创建左子树和右子树
  20. root.left = buildTreeChild(preorder,inorder,inBgin,rootIndex - 1);
  21. root.right = buildTreeChild(preorder,inorder,rootIndex + 1,inEnd);
  22. return root;
  23. }
  24. private int FindRootIndex(int[] inorder,int inBgin,int inEnd,int key){//用于步骤2中寻找下标
  25. //inorder 中序遍历序列,被查找序列
  26. //inBgin 起始位置
  27. //inEnd 结束位置
  28. //查找对象的下标
  29. int i = inBgin;
  30. while (i <= inEnd){
  31. if (inorder[i] == key){
  32. return i;
  33. }
  34. i++;
  35. }
  36. return -1;//若没有找到,就返回-1
  37. }
  38. }

6.二叉树转化为由括号分隔的字符串

给你二叉树的根节点root,采用先序遍历,转化为一个由括号和整数组成的字符串,返回构造出的字符串

  1. class solution2{
  2. public String tree2str(TreeNode root){
  3. StringBuilder sbu = new StringBuilder();
  4. TreetoStr( root,sbu);
  5. return sbu.toString();
  6. }
  7. private void TreetoStr(TreeNode root,StringBuilder sbu ){
  8. if (root == null){
  9. return;
  10. }
  11. sbu.append('(');
  12. //1.先递归左树
  13. if (root.left != null){
  14. sbu.append('(');
  15. TreetoStr(root.left,sbu);
  16. sbu.append(')');
  17. }else {
  18. if (root.right == null){
  19. return;
  20. }
  21. else {
  22. sbu.append('(');
  23. sbu.append(')');
  24. }
  25. }
  26. //
  27. if (root.right != null){
  28. sbu.append('(');
  29. TreetoStr(root.right,sbu);
  30. sbu.append(')');
  31. }else {
  32. return;
  33. }
  34. }
  35. }

7. 非递归的方式遍历先序序列的二叉树

  1. class preOrder{
  2. public List<Integer> preorderTraversal(TreeNode root){
  3. List<Integer> list = new ArrayList<>();
  4. if (root == null){
  5. return list;
  6. }
  7. Stack<TreeNode> stack = new Stack();
  8. TreeNode cur = root;
  9. while (cur != null || !stack.isEmpty()){
  10. while (cur != null){//从整棵树的最左节点边开始遍历,不为空则入栈
  11. stack.push(cur);
  12. list.add((int) cur.val);
  13. cur = cur.left;
  14. }
  15. TreeNode top = stack.pop();
  16. cur = top.right;
  17. }
  18. return list;
  19. }
  20. }

8. 非递归的方式遍历中序序列的二叉树

  1. class inOrder{
  2. public List<Integer> inorderTraverasal(TreeNode root){
  3. List<Integer> list = new LinkedList<>();
  4. Stack<TreeNode> stack = new Stack<>();
  5. TreeNode cur = root;
  6. if (root == null){
  7. return list;
  8. }
  9. while (!stack.isEmpty() || cur != null){
  10. while (cur!=null){
  11. stack.push(cur);
  12. cur=cur.left;
  13. }
  14. TreeNode top = stack.pop();
  15. list.add((int) cur.val);
  16. cur = top.right;
  17. }
  18. return list;
  19. }
  20. }

9. 非递归的方式遍历后序序列的二叉树

  1. class postOrder{
  2. public List<Integer> inorderTraverasal(TreeNode root){
  3. List<Integer> list = new LinkedList<>();
  4. Stack<TreeNode> stack = new Stack<>();
  5. TreeNode cur = root;
  6. TreeNode prev = null;
  7. if (root == null){
  8. return list;
  9. }
  10. while (!stack.isEmpty() || cur != null){
  11. while (cur!=null){
  12. stack.push(cur);
  13. cur=cur.left;
  14. }
  15. TreeNode top = stack.peek();
  16. if (top.right==null || top.right == prev){
  17. stack.pop();
  18. list.add((int) top.val);
  19. prev = top;
  20. }else {
  21. cur = cur.right;
  22. }
  23. }
  24. return list;
  25. }
  26. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/982293
推荐阅读
相关标签
  

闽ICP备14008679号