初级算法之树

简介: 树比链表稍微复杂,因为链表是线性数据结构,而树不是。 树的问题可以由 广度优先搜索 或 深度优先搜索 解决。 在本章节中,我们提供了一个对于练习 广度优先遍历 很好的题目。我们推荐以下题目:二叉树的最大深度,验证二叉搜索树,二叉树的层次遍历 和 将有序数组转换为二叉搜索树。剑指 Offer 55 - I. 二叉树的深度输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。递归法:class Solution { public int maxDepth(TreeNode root) {

树比链表稍微复杂,因为链表是线性数据结构,而树不是。 树的问题可以由 广度优先搜索 或 深度优先搜索 解决。 在本章节中,我们提供了一个对于练习 广度优先遍历 很好的题目。

我们推荐以下题目:

二叉树的最大深度,验证二叉搜索树,二叉树的层次遍历 和 将有序数组转换为二叉搜索树。

剑指 Offer 55 - I. 二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

递归法:

class Solution {

   public int maxDepth(TreeNode root) {

       return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;

   }

}

迭代法:

class Solution {

   public int maxDepth(TreeNode root) {

      if(root == null) return 0;

       Queue<TreeNode> queue = new LinkedList<>(){{

           add(root);

       }};

       int depth = 0;

       while(!queue.isEmpty()){

           int cursize = queue.size();

           for(int i = 0; i < cursize; i++){

               TreeNode temp = queue.poll();

               if(temp.left != null) queue.add(temp.left);

               if(temp.right != null) queue.add(temp.right);

           }

           depth++;

       }

       return depth;

   }

}

111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

class Solution {

   public int minDepth(TreeNode root) {

   if (root == null) {

       return 0;

   }

   // null节点不参与比较

   if (root.left == null && root.right != null) {

       return 1 + minDepth(root.right);

   }

   // null节点不参与比较

   if (root.right == null && root.left != null) {

       return 1 + minDepth(root.left);

   }


   return 1 + Math.min(minDepth(root.left), minDepth(root.right));

}

广度优先算法

class Solution {

   class QueueNode {

       TreeNode node;

       int depth;


       public QueueNode(TreeNode node, int depth) {

           this.node = node;

           this.depth = depth;

       }

   }


   public int minDepth(TreeNode root) {

       if (root == null) {

           return 0;

       }


       Queue<QueueNode> queue = new LinkedList<QueueNode>();

       queue.offer(new QueueNode(root, 1));

       while (!queue.isEmpty()) {

           QueueNode nodeDepth = queue.poll();

           TreeNode node = nodeDepth.node;

           int depth = nodeDepth.depth;

           if (node.left == null && node.right == null) {

               return depth;

           }

           if (node.left != null) {

               queue.offer(new QueueNode(node.left, depth + 1));

           }

           if (node.right != null) {

               queue.offer(new QueueNode(node.right, depth + 1));

           }

       }


       return 0;

   }

}


98. 验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。

节点的右子树只包含 大于 当前节点的数。

所有左子树和右子树自身必须也是二叉搜索树。

递归

class Solution {

       public boolean isValidBST(TreeNode root) {

       if (root == null) return true;

       return dfs(root.left, root.val, false) && dfs(root.right, root.val, true) && isValidBST(root.left) && isValidBST(root.right);

   }


   public boolean dfs(TreeNode root, int val, boolean moreThan) {

       if (root == null) return true;

       if (moreThan && root.val <= val) return false;

       if (! moreThan && root.val >= val) return false;

       if (root.left != null && ! dfs(root.left, val, moreThan)) return false;

       if (root.right != null && ! dfs(root.right, val, moreThan)) return false;

       return true;

   }

}

class Solution {

   public boolean isValidBST(TreeNode root) {

       return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);

   }


   public boolean isValidBST(TreeNode node, long lower, long upper) {

       if (node == null) {

           return true;

       }

       if (node.val <= lower || node.val >= upper) {

           return false;

       }

       return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);

   }

}

1361. 验证二叉树

二叉树上有 n 个节点,按从 0 到 n - 1 编号,其中节点 i 的两个子节点分别是 leftChild[i] 和 rightChild[i]。

只有 所有 节点能够形成且 只 形成 一颗 有效的二叉树时,返回 true;否则返回 false。

如果节点 i 没有左子节点,那么 leftChild[i] 就等于 -1。右子节点也符合该规则。

二叉树的充分条件:

1)根节点不能被访问到

2)其他节点都被访问到且只被访问一次

用一个长度为 n 的数组 cnt 来记录访问次数 做判断

public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {

       int[] cnt = new int[n];

       for(int i=0; i<n; i++) {

           if(leftChild[i] >= 0) {

               cnt[leftChild[i]] += 1;

           }

           if(rightChild[i] >= 0) {

               cnt[rightChild[i]] += 1;

           }

       }

       if(cnt[0] != 0) {

           return false;

       }

       for(int i=1; i<n; i++) {

           if(cnt[i] != 1) {

               return false;

           }

       }

       return true;

   }

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

递归法

class Solution {

   public boolean isSymmetric(TreeNode root) {

       return check(root, root);

   }


   public boolean check(TreeNode p, TreeNode q) {

       if (p == null && q == null) {

           return true;

       }

       if (p == null || q == null) {

           return false;

       }

       return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);

   }

}

迭代法

class Solution {

   public boolean isSymmetric(TreeNode root) {

       return check(root, root);

   }


   public boolean check(TreeNode u, TreeNode v) {

       Queue<TreeNode> q = new LinkedList<TreeNode>();

       q.offer(u);

       q.offer(v);

       while (!q.isEmpty()) {

           u = q.poll();

           v = q.poll();

           if (u == null && v == null) {

               continue;

           }

           if ((u == null || v == null) || (u.val != v.val)) {

               return false;

           }


           q.offer(u.left);

           q.offer(v.right);


           q.offer(u.right);

           q.offer(v.left);

       }

       return true;

   }

}


102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

//dnf方法

class Solution {

   public List<List<Integer>> levelOrder(TreeNode root) {

   List<List<Integer>> res = new ArrayList<>();

   levelHelper(res, root, 0);

   return res;

   }

   public void levelHelper(List<List<Integer>> list, TreeNode root, int level) {

   //边界条件判断

   if (root == null)

       return;

   //level表示的是层数,如果level >= list.size(),说明到下一层了,所以

   //要先把下一层的list初始化,防止下面add的时候出现空指针异常

   if (level >= list.size()) {

       list.add(new ArrayList<>());

   }

   //level表示的是第几层,这里访问到第几层,我们就把数据加入到第几层

   list.get(level).add(root.val);

   //当前节点访问完之后,再使用递归的方式分别访问当前节点的左右子节点

   levelHelper(list, root.left, level + 1);

   levelHelper(list, root.right, level + 1);

   }

}

class Solution {

   public List<List<Integer>> levelOrder(TreeNode root) {

       List<List<Integer>> ret = new ArrayList<List<Integer>>();

       if (root == null) {

           return ret;

       }


       Queue<TreeNode> queue = new LinkedList<TreeNode>();

       queue.offer(root);

       while (!queue.isEmpty()) {

           List<Integer> level = new ArrayList<Integer>();

           int currentLevelSize = queue.size();

           for (int i = 1; i <= currentLevelSize; ++i) {

               TreeNode node = queue.poll();

               level.add(node.val);

               if (node.left != null) {

                   queue.offer(node.left);

               }

               if (node.right != null) {

                   queue.offer(node.right);

               }

           }

           ret.add(level);

       }

       

       return ret;

   }

}



109. 有序链表转换二叉搜索树

给定一个单链表的头节点  head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。

class Solution {

   public TreeNode sortedListToBST(ListNode head) {

       if(head == null) return null;

       else if(head.next == null) return new TreeNode(head.val);

       ListNode pre = head;

       ListNode p = pre.next;

       ListNode q = p.next;

       //找到链表的中点p

       while(q!=null && q.next!=null){

           pre = pre.next;

           p = pre.next;

           q = q.next.next;

       }

       //将中点左边的链表分开

       pre.next = null;

       TreeNode root = new TreeNode(p.val);

       root.left = sortedListToBST(head);

       root.right = sortedListToBST(p.next);

       return root;

   }

}

class Solution {

   public TreeNode sortedListToBST(ListNode head) {

       // 快慢指针找到链表的中点,中点作为根结点,两边作为左右子树

       if(head == null) return null;

       if(head.next == null) return new TreeNode(head.val);

       // 快慢指针找中间结点

       ListNode fast = head, slow = head, pre = null;

       while(fast != null && fast.next != null){

           fast =  fast.next.next;

           pre = slow;

           slow = slow.next;

       }

       // 分割出左链表,用于构造本结点的左子树

       pre.next = null;

       // 分割出右链表,用于构造本结点的右子树

       ListNode rightList = slow.next;

       // 用中间结点构造根结点

       TreeNode root = new TreeNode(slow.val);

       // 构造左子树

       root.left = sortedListToBST(head);

       // 构造右子树

       root.right = sortedListToBST(rightList);

       // 返回本结点所在子树

       return root;

   }

}

108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

class Solution {

   public TreeNode sortedArrayToBST(int[] nums) {

       return nums == null ? null : buildTree(nums, 0, nums.length - 1);

   }



   private TreeNode buildTree(int[] nums, int l, int r) {

       if (l > r) {

           return null;

       }

       int m = l + (r - l) / 2;

       //根节点

       TreeNode root = new TreeNode(nums[m]);

       //左节点

       root.left = buildTree(nums, l, m - 1);

       //右节点

       root.right = buildTree(nums, m + 1, r);

       return root;

   }

}


相关文章
|
3月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
102 1
|
14天前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
22 2
|
5月前
|
存储 算法 Java
Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。
【6月更文挑战第21天】Java中,树与图的算法涉及二叉树的前序、中序、后序遍历以及DFS和BFS搜索。二叉树遍历通过访问根、左、右子节点实现。DFS采用递归遍历图的节点,而BFS利用队列按层次访问。以下是简化的代码片段:[Java代码略]
47 4
|
1月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
22 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
5月前
|
存储 算法 Linux
【数据结构和算法】---二叉树(1)--树概念及结构
【数据结构和算法】---二叉树(1)--树概念及结构
51 0
|
2月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
56 2
|
1月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
25 0
|
1月前
|
存储 算法 Java
数据结构和算法--分段树
数据结构和算法--分段树
16 0
|
4月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
【7月更文挑战第19天】Trie树,又称前缀树,是优化字符串搜索的高效数据结构。通过利用公共前缀,Trie树能快速插入、删除和查找字符串。
113 2
下一篇
无影云桌面