LeetCode 310. 最小高度树(找树中的最长路径) / 796. 旋转字符串 / 429. N 叉树的层序遍历

310. 最小高度树

2022.4.6 每日一题

题目描述

树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

示例 1:

在这里插入图片描述
输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。

示例 2:

在这里插入图片描述
输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]

提示:

1 <= n <= 2 * 10^4
edges.length == n - 1
0 <= ai, bi < n
ai != bi
所有 (ai, bi) 互不相同
给定的输入 保证 是一棵树,并且 不会有重复的边

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-height-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

明知超时,但是还是写了一遍用广度优先的,思路也很简单,差五个例子

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        //其中一个简单的思路就是每个点为起点广度优先,然后看哪一个遍历层数最少
        //一个剪枝就是如果层数超过最小,那么跳过
        //这样估计会超时

        Map<Integer, Set<Integer>> map = new HashMap<>();
        for(int[] e : edges){
            Set<Integer> set0 = map.getOrDefault(e[0], new HashSet<>());
            Set<Integer> set1 = map.getOrDefault(e[1], new HashSet<>());
            set0.add(e[1]);
            set1.add(e[0]);
            map.put(e[0], set0);
            map.put(e[1], set1);
        }
        List<Integer> list = new ArrayList<>();
        int min = n + 1;
        for(int i = 0; i < n; i++){
            Queue<Integer> queue = new LinkedList<>();
            queue.offer(i);
            boolean[] used = new boolean[n];
            used[i] = true;
            int idx = 0;
            while(!queue.isEmpty() && idx <= min){
                int s = queue.size();
                idx++;
                while(s-- > 0){
                    int top = queue.poll();
                    Set<Integer> set = map.getOrDefault(top, new HashSet<>());
                    for(int t : set){
                        if(used[t])
                            continue;
                        used[t] = true;
                        queue.offer(t);
                    }
                }
            }
            //System.out.println(idx);
            if(idx < min){
                min = idx;
                list.clear();
                list.add(i);
            }
            else if(idx == min){
                list.add(i);
            }
        }
        return list;
    }
}

考虑一根绳子,怎么样折叠才能使长度最短,那么肯定是从中间折叠
一样的道理,找到最长的路径,然后从中间折叠就行了
现在的问题就变成了怎么找最长的路径了
看了题解,找最短路径的方法:

  1. 先从任一个节点,找相距最远的节点x
  2. 然后从x出发,找相距最远的节点y
  3. x到y就是最长的路径
class Solution {
    Map<Integer, Set<Integer>> map;
    int n;
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        //其中一个简单的思路就是每个点为起点广度优先,然后看哪一个遍历层数最少
        //一个剪枝就是如果层数超过最小,那么跳过
        //这样估计会超时
        //然后在写的时候发现,中间节点为根组成的树,肯定是高度最小的,
        //所以去除掉边界节点,把所有中间节点加入到集合中就是答案,
        //而中间节点就是可以连接多个点的节点

        //不对,应该是找到最长的链,然后最长链的中间节点就是最小的根,
        //就相当于一根绳子拎起来,两头最短只能是拎中间
        //那么怎么找到这个最长的链条呢,相当于给定一棵树,找这颗树中最长的链
        //好像也做过,但是突然不会了

        //看了题解, 很巧妙
        //先从任一个节点出发找到最远的节点x
        //再从x出发,找到最远的结点y
        //x到y就是最长路径

        map = new HashMap<>();
        this.n = n;
        for(int[] e : edges){
            Set<Integer> set0 = map.getOrDefault(e[0], new HashSet<>());
            Set<Integer> set1 = map.getOrDefault(e[1], new HashSet<>());
            set0.add(e[1]);
            set1.add(e[0]);
            map.put(e[0], set0);
            map.put(e[1], set1);
        }
        List<Integer> res = new ArrayList<>();
        if(n == 1){
            res.add(0);
            return res;
        }
        //在遍历中记录当前结点的父节点,方便最后找路径
        int[] parents = new int[n];
        Arrays.fill(parents, -1);

        int x = helper(0, parents);  //其实x不用记录路径
        int y = helper(x, parents);
        parents[x] = -1;    //这里将x的父节点设为-1,这样两条路径不会混乱
        //到这里,记录了x的父节点与y的父节点
        //需要找到x到y的路径,怎么搞呢
        List<Integer> path = new ArrayList<>();
        while(y != -1){
            path.add(y);
            y = parents[y];
        }
        if(path.size() % 2 == 0){
            int mid = path.size() / 2;
            res.add(path.get(mid - 1));
            res.add(path.get(mid));
        }else{
            int mid = path.size() / 2;
            res.add(path.get(mid));
        }
        return res;
    }
    
    //广度优先找最长路径
    public int helper(int t, int[] parents){
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(t);
        boolean[] used = new boolean[n];
        used[t] = true;
        int ans = -1;
        while(!queue.isEmpty()){
            //直接返回队列中最后一个节点
            int top = queue.poll();
            ans = top;
            Set<Integer> set = map.get(top);
            for(int node : set){
                if(used[node])
                    continue;
                used[node] = true;
                queue.offer(node);
                parents[node] = top;
            }
        }
        return ans;
    }
}

或者层层剥削,就是说先找到最外圈的节点,删除以后又得到了新的一层外圈节点
然后层层删除,删除到最后肯定还剩最长链的中间节点,这两个或者一个节点就是答案

class Solution {
    public List<Integer> findMinHeightTrees(int n, int[][] edges) {
        List<Integer> ans = new ArrayList<Integer>();
        if (n == 1) {
            ans.add(0);
            return ans;
        }
        int[] degree = new int[n];
        List<Integer>[] adj = new List[n];
        for (int i = 0; i < n; i++) {
            adj[i] = new ArrayList<Integer>();
        }
        for (int[] edge : edges) {
            adj[edge[0]].add(edge[1]);
            adj[edge[1]].add(edge[0]);
            degree[edge[0]]++;
            degree[edge[1]]++;
        }
        Queue<Integer> queue = new ArrayDeque<Integer>();
        for (int i = 0; i < n; i++) {
            if (degree[i] == 1) {
                queue.offer(i);
            }
        }
        int remainNodes = n;
        while (remainNodes > 2) {
            int sz = queue.size();
            remainNodes -= sz;
            for (int i = 0; i < sz; i++) {
                int curr = queue.poll();
                for (int v : adj[curr]) {
                    degree[v]--;
                    if (degree[v] == 1) {
                        queue.offer(v);
                    }
                }
            }
        }
        while (!queue.isEmpty()) {
            ans.add(queue.poll());
        }
        return ans;
    }
}

796. 旋转字符串

2022.4.7 每日一题

题目描述

给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true 。

s 的 旋转操作 就是将 s 最左边的字符移动到最右边。

  • 例如, 若 s = ‘abcde’,在旋转一次之后结果就是’bcdea’ 。

示例 1:

输入: s = “abcde”, goal = “cdeab”
输出: true

示例 2:

输入: s = “abcde”, goal = “abced”
输出: false

提示:

1 <= s.length, goal.length <= 100
s 和 goal 由小写英文字母组成

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

class Solution {
    public boolean rotateString(String s, String goal) {
        int l = s.length();
        for(int i = 1; i <= l; i++){
            String s1 = s.substring(0, i);
            String s2 = s.substring(i, l);
            if(goal.equals(s2 + s1))
                return true;
        }
        return false;
    }
}
class Solution:
    def rotateString(self, s: str, goal: str) -> bool:
        m, n = len(s), len(goal)
        for i in range(1, m + 1):
            if (s[i: ] + s[0 : i]) == goal:
                return True
        return False

直接拼接两个字符串,看是否包含目标字符串

class Solution:
    def rotateString(self, s: str, goal: str) -> bool:
        return len(s) == len(goal) and goal in (s + s)

429. N 叉树的层序遍历

2022.4.8 每日一题

题目描述

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:

在这里插入图片描述
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

在这里插入图片描述
输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

提示:

树的高度不会超过 1000
树的节点总数在 [0, 10^4] 之间

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

就是简单的层序遍历

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> list = new ArrayList<>();
        if(root == null)
            return list;
        List<Integer> first = new ArrayList<>();
        first.add(root.val);
        list.add(first);
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            int sz = queue.size();
            List<Integer> temp = new ArrayList<>();
            while(sz-- > 0){
                Node top = queue.poll();
                List<Node> child = top.children;
                if(child.isEmpty())
                    continue;
                for(Node n : child){
                    temp.add(n.val);
                    queue.offer(n);
                }
            }
            if(!temp.isEmpty())
                list.add(temp);
        }
        return list;
    }
}

python

"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []
        ans = list()
        q = deque([root])

        while q:
            sz = len(q)
            temp = list()
            for i in range(sz):
                top = q.popleft()
                temp.append(top.val)
                for child in top.children:
                    q.append(child)
            ans.append(temp)
        return ans

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值