Java实现LeetCode 题号:651 - 660

164 阅读4分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

652. 寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

两棵树重复是指它们具有相同的结构以及相同的结点值。

示例 1:

 1
   / \
  2   3
 /   / \
4   2   4
   /
  4

下面是两个重复的子树:

      2
     /
    44

因此,你需要以列表的形式返回上述重复子树的根结点。


class Solution {
  private int treeId = 100;
    //这个用来保存树得,用一个id去替换树
    private Map<String, Integer> tree = new HashMap<>();
    //下面这个是用上面树换的id来保存次数得,只有两次得时候才会加入到下面得list里面
    private Map<Integer, Integer> count = new HashMap<>();
    private List<TreeNode> res;

    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        res = new ArrayList<>();
        this.dfs(root);
        return res;
    }

   

    private int dfs(TreeNode node){
        if(node == null) return 0;

        String content = new StringBuilder().append(node.val).append(",").append(this.dfs(node.left)).append(",").append(this.dfs(node.right)).toString();

        Integer id = tree.get(content);
        if(id == null) {
            id = treeId++;
            tree.put(content, id);
        }

        int num = count.getOrDefault(id, 0);
        count.put(id, ++num);

        if(num == 2) {
            res.add(node);
        }
        return id;
    }

}

653. 两数之和 IV - 输入 BST

给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。

案例 1:

输入:

    5
   / \
  3   6
 / \   \
2   4   7

Target = 9

输出: True

案例 2:

输入:

    5
   / \
  3   6
 / \   \
2   4   7

Target = 28

输出: False

PS:
    遍历树,遍历的过程中检查有没有能凑成k的数

class Solution {
      boolean found = false;
    public boolean findTarget(TreeNode root, int k) {
        inorder(new HashSet(),root,k);
        return found;
    }
    private void inorder(Set<Integer> set,TreeNode root,int k){
        if(root==null || found) return;
        inorder(set,root.left,k);
        set.add(root.val);
        if(set.contains(k-root.val) && root.val!=k-root.val){
            found = true;
        }
        inorder(set,root.right,k);
    }
}

654. 最大二叉树

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构建最大二叉树,并且输出这个树的根节点。

示例 :

输入:[3,2,1,6,0,5] 输出:返回下面这棵树的根节点:

      6
    /   \
   3     5
    \    / 
     2  0   
       \
        1

提示:

给定的数组的大小在 [1, 1000] 之间。

 
class Solution {
     //寻找l到r区域中的最大值索引
	public int max(int[] nums,int l,int r){
		int max_i = l;
		for(int i=l;i<r;i++){
			if(nums[max_i] < nums[i]){
				max_i = i;
			}
		}
		return max_i;
	}
    public TreeNode construct(int nums[] ,int l,int r){
		if(l == r) return null;
		int max_i = max(nums,l,r);
		TreeNode root = new TreeNode(nums[max_i]);
		root.left = construct(nums, l, max_i);
		root.right = construct(nums, max_i+1, r);
		return root;
	}
    public TreeNode constructMaximumBinaryTree(int[] nums) {
		return construct(nums, 0, nums.length);
	}
}

655. 输出二叉树

在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则:

行数 m 应当等于给定二叉树的高度。 列数 n 应当总是奇数。 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间。根节点所在的行与列会将剩余空间划分为两部分(左下部分和右下部分)。你应该将左子树输出在左下部分,右子树输出在右下部分。左下和右下部分应当有相同的大小。即使一个子树为空而另一个非空,你不需要为空的子树输出任何东西,但仍需要为另一个子树留出足够的空间。然而,如果两个子树都为空则不需要为它们留出任何空间。 每个未使用的空间应包含一个空的字符串""。 使用相同的规则输出子树。 示例 1:

输入:
     1
    /
   2
输出:
[["", "1", ""],
 ["2", "", ""]]

示例 2:

输入:
     1
    / \
   2   3
    \
     4
输出:
[["", "", "", "1", "", "", ""],
 ["", "2", "", "", "", "3", ""],
 ["", "", "4", "", "", "", ""]]

示例 3:

输入:
      1
     / \
    2   5
   / 
  3 
 / 
4 
输出:
[["",  "",  "", "",  "", "", "", "1", "",  "",  "",  "",  "", "", ""]
 ["",  "",  "", "2", "", "", "", "",  "",  "",  "",  "5", "", "", ""]
 ["",  "3", "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""]
 ["4", "",  "", "",  "", "", "", "",  "",  "",  "",  "",  "", "", ""]]

注意: 二叉树的高度在范围 [1, 10] 中。

PS:
	先判断数组大小
	DFS+二分填充
        
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
 public List<List<String>> printTree(TreeNode root) {
        if (root == null) {
            return new ArrayList<>(1);
        }
        //先获取数组大小
        int depth = getDepth(root);
        int width = (int)(Math.pow(2, depth) - 1);
        width = width > 0 ? width : 1;
        String[][] result = new String[depth][width];
        List<List<String>> res = new LinkedList<>();
        //填充操作
        fill(result, 0, 0, width - 1, root);
        for (int i = 0; i < result.length; i++) {
            LinkedList<String> linkedList = new LinkedList<>();
            for (int j = 0; j < result[i].length; j++) {
                if (result[i][j] == null) {
                    linkedList.add("");
                } else {
                    linkedList.add(result[i][j]);
                }
            }
            res.add(linkedList);
        }
        return res;
    }

    public void fill(String[][] ints, int depth, int start, int end, TreeNode node) {
        if (node == null) {
            return;
        }
        int mid = (start + end) / 2;
        ints[depth][mid] = String.valueOf(node.val);
        fill(ints, depth + 1, start, mid - 1, node.left);
        fill(ints, depth + 1, mid + 1, end, node.right);
    }

    public int getDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int left = getDepth(root.left) + 1;
        int right = getDepth(root.right) + 1;
        return right > left ? right : left;
    }
}

657. 机器人能否返回原点

在二维平面上,有一个机器人从原点 (0, 0) 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在 (0, 0) 处结束。

移动顺序由字符串表示。字符 move[i] 表示其第 i 次移动。机器人的有效动作有 R(右),L(左),U(上)和 D(下)。如果机器人在完成所有动作后返回原点,则返回 true。否则,返回 false。

注意:机器人“面朝”的方向无关紧要。 “R” 将始终使机器人向右移动一次,“L” 将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。

示例 1:

输入: "UD" 输出: true 解释:机器人向上移动一次,然后向下移动一次。所有动作都具有相同的幅度,因此它最终回到它开始的原点。因此,我们返回 true。 示例 2:

输入: "LL" 输出: false 解释:机器人向左移动两次。它最终位于原点的左侧,距原点有两次 “移动” 的距离。我们返回 false,因为它在移动结束时没有返回原点。

PS:
    暴力模拟四个方向行走
class Solution {
    public boolean judgeCircle(String moves) {
        int col = 0, row = 0;
        for(char ch : moves.toCharArray()){
            if(ch == 'U') row++;
            else if(ch == 'D') row--;
            else if(ch == 'L') col--;
            else col++;
        }
        return col == 0 && row == 0;
    }
}

658. 找到 K 个最接近的元素

给定一个排序好的数组,两个整数 k 和 x,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。如果有两个数与 x 的差值一样,优先选择数值较小的那个数。

示例 1:

输入: [1,2,3,4,5], k=4, x=3 输出: [1,2,3,4]

示例 2:

输入: [1,2,3,4,5], k=4, x=-1 输出: [1,2,3,4]

说明:

k 的值为正数,且总是小于给定排序数组的长度。 数组不为空,且长度不超过 104 数组里的每个元素与 x 的绝对值不超过 104

更新(2017/9/19): 这个参数 arr 已经被改变为一个整数数组(而不是整数列表)。 请重新加载代码定义以获取最新更改。

PS:
    从大范围开始向下缩减,以x为标准,如果左端点和x的差比右端点和x的差大,左端点右移
    否则就右端点右移
class Solution {
      public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> res = new ArrayList();
        int start =0;
        int end = arr.length-1;
        while(end - start>=k){
            if(x-arr[start] > arr[end]-x){
                start++;
            }else{
                end--;
                
            }
        }
        for(int i=start;i<=end;i++){
            res.add(arr[i]);
        }
        return res;

    }
}

659. 分割数组为连续子序列

输入一个按升序排序的整数数组(可能包含重复数字),你需要将它们分割成几个子序列,其中每个子序列至少包含三个连续整数。返回你是否能做出这样的分割?

示例 1:

输入: [1,2,3,3,4,5] 输出: True 解释: 你可以分割出这样两个连续子序列 : 1, 2, 3 3, 4, 5

示例 2:

输入: [1,2,3,3,4,4,5,5] 输出: True 解释: 你可以分割出这样两个连续子序列 : 1, 2, 3, 4, 5 3, 4, 5

示例 3:

输入: [1,2,3,4,4,5] 输出: False

提示:

输入的数组长度范围为 [1, 10000]

PS:
   按照某个元素开始找,找连续的数,如果连续的数少于三个,并且大于0个,就证明无法凑成
   如果能循环完所有证明是可以凑成的
    
class Solution {
     public boolean isPossible(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i : nums) {
            map.put(i, map.getOrDefault(i, 0)+1);
        }
        for (int i : nums) {
            //子数组的元素个数
            int subNum = 0;
            int p = 1;
            //下个元素
            int next = i;
            while (map.getOrDefault(next, 0) >= p) {
                p = map.get(next);
                map.put(next, p-1);
                ++subNum;
                ++next;
            }
            if (subNum > 0 && subNum < 3) {
                return false;
            }
        }
        return true;
     }
}