深度优先遍历 DFS
题目均来自leetcode,版权为leetcode所有
练习:
1. 平衡二叉树
实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。
//二叉树节点定义
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
class Test {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int left_height = height(root.left);
int right_height = height(root.right);
if(Math.abs(left_height - right_height) > 1){
return false;
}
else{
return isBalanced(root.left) && isBalanced(root.right);
//必须满足左右同时成立才能考察树的平衡性
}
}
public int height(TreeNode root){
if(root == null){
return 0;
}
//递归地获取某节点所在高度
int left_height = height(root.left);
int right_height = height(root.right);
if(left_height >= right_height){
return 1+ left_height;
}
else{
return 1 + right_height;
}
}
}
执行用时:1 ms
内存消耗:38.4 MB
2. 叶子相似的树
一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。如果两棵树的叶值序列相同,则认为两棵树是叶子相似的树。给定两颗树,判断它们是否为叶子相似的树。
思路:一个非叶子节点有大于等于1个孩子,无法用遍历并返回数值来记录所有叶子。于是在递归遍历的同时记录叶子节点的值。
import java.util.ArrayList;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Test {
private ArrayList a = new ArrayList(200);
private ArrayList b = new ArrayList(200);
public boolean leafSimilar(TreeNode root1, TreeNode root2) {
getLeaf(root1, a);
getLeaf(root2, b);
if(a.equals(b)){
return true;
}
else{
return false;
}
}
public void getLeaf(TreeNode root, ArrayList record){
if(root == null){
return;
}
if(root.left == null && root.right == null){
record.add(root.val);
return;
}
else if(root.left == null){
getLeaf(root.right, record);
}
else if(root.right == null){
getLeaf(root.left, record);
}
else {
getLeaf(root.left, record);
getLeaf(root.right, record);
}
}
}
执行用时:0 ms
内存消耗: 36.2 MB
3. 水域大小
你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
import java.util.Arrays;
class Test {
int[] dirX = {0, 1, 0, -1, 1, 1, -1, -1};
int[] dirY = {1, 0, -1, 0, 1, -1, 1, -1};
public int[] pondSizes(int[][] land) {
int[] result = new int[1];
int x = 0;
int y = 0;
for(int i=0;i<land.length;i++){
for(int j=0;j<land[0].length;j++){
if(land[i][j] == 0){
//这两部实际上是以便添加数据以便扩展长度,实际是向数组尾部追加数据
result[result.length-1] = dfs(land,i,j);
result=Arrays.copyOf(result, result.length+1);
}
}
}
//去数组末尾
result=Arrays.copyOf(result, result.length-1);
Arrays.sort(result);
return result;
}
public int dfs(int[][] land, int x, int y){
int count = 0;
if(land[x][y] == 0){
count++;
land[x][y] = 10;//用10表示该点已经遍历过了
}
for(int i = 0;i<8;i++){
int tx = x + dirX[i];
int ty = y + dirY[i];
if(tx<0||ty<0||tx>=land.length||ty>=land[0].length||land[tx][ty] != 0){
continue;
}
count += dfs(land,tx,ty);
}
return count;
}
}
执行用时:65ms
内存消耗:77.2MB
4. 省份数量(邻接表dfs)
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。
class Test {
public int findCircleNum(int[][] isConnected){
boolean[] marked = new boolean[isConnected.length];
int result = 0;
for(int i=0;i<isConnected.length;i++){
if(!marked[i]){
dfs(isConnected, marked, i);
result++;
}
}
return result;
}
//找到所有和点x相邻的点
public void dfs(int[][] isConnected, boolean[] marked, int x){
for(int i=0;i<isConnected.length;i++){
if(isConnected[x][i] == 1 && x != i && !marked[i]){
marked[i] = true;
dfs(isConnected, marked, i);
}
}
}
}
执行用时:1ms
内存消耗:39.2MB
5. 把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树
思路1:最先出现的递归调用会不断地把程序递出,而最后出现的递归会在归来的过程中递出。所以与本题类似的问题,当在递归过程中会不定时出现分枝,可以使用两个递归,前一个是一定会发生的递归,后一个是分枝的递归。(本题解来自leetcode)
//Definition for a binary tree node.
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
if(root != null){
convertBST(root.right);
sum += root.val;
root.val = sum;
if(root.left != null){
convertBST(root.left);
}
}
return root;
}
}
执行用时:0 ms
内存消耗:39 MB
思路2:累加的过程是中序遍历的反过程,即反序中序遍历,可以使用Morris遍历的方法:利用树的大量空闲指针,减少内存开销。规则如下:
1.如果当前节点的右子节点为空,处理当前节点,并遍历当前节点的左子节点;
-
如果当前节点的右子节点不为空,找到当前节点右子树的最左节点(该节点为当前节点中序遍历的前驱节点);
-
如果最左节点的左指针为空,将最左节点的左指针指向当前节点,遍历当前节点的右子节点;
-
如果最左节点的左指针不为空,将最左节点的左指针重新置为空(恢复树的原状),处理当前节点,并将当前节点置为其左节点;
重复步骤 1 和步骤 2,直到遍历结束。(本题解来自leetcode)
class Solution {
public TreeNode convertBST(TreeNode root) {
int sum = 0;
TreeNode node = root;
while (node != null) {
//规则1
if (node.right == null) {
sum += node.val;
node.val = sum;
node = node.left;
} else {
TreeNode succ = getSuccessor(node);
if (succ.left == null) {
succ.left = node;
node = node.right;
} else {
succ.left = null;
sum += node.val;
node.val = sum;
node = node.left;
}
}
}
return root;
}
public TreeNode getSuccessor(TreeNode node) {
TreeNode succ = node.right;
while (succ.left != null && succ.left != node) {
succ = succ.left;
}
return succ;
}
}
执行用时:1 ms
内存消耗: 38.6 MB
6. 求根节点到叶节点的数字之和
给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:
- 例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。
class Solution {
public int sumNumbers(TreeNode root) {
return dfs(root,0);
}
public int dfs(TreeNode root, int prevSum){
if(root == null){
return 0;
}
int sum = prevSum*10 + root.val;
//由于前面有节点为空则返回0的声明,所以可以写在一起;
if(root.left != null || root.right != null){
sum = dfs(root.left, sum) + dfs(root.right, sum);
}
return sum;
}
}
执行用时:0 ms
内存消耗:35.7 MB
7. 找树左下角的值
给定一个二叉树,在树的最后一行找到最左边的值。
思路:和第5题的思路1相同,因为所求结果与左右有关,故在dfs函数中递归调用两次,后一次的递送方向与最终所求(左)是相同的。
class Solution {
int deepest = 0;
int result = 0;
public int findBottomLeftValue(TreeNode root) {
dfs(root, 1);
return result;
}
public void dfs(TreeNode root, int floor){
if(root == null){
return;
}
if(root.right != null){
dfs(root.right, floor + 1);
}
if(deepest <= floor){
deepest = floor;
result = root.val;
}
if(root.left != null){
dfs(root.left, floor + 1);
}
}
}
执行用时:0 ms
内存消耗:37.9 MB
(未完,待更新)