LeetCode 540. 有序数组中的单一元素(二分+异或技巧) / 1380. 矩阵中的幸运数 / 1719. 重构一棵树的方案数

540. 有序数组中的单一元素

2022.2.14 每日一题

题目描述

给你一个仅由整数组成的有序数组,其中每个元素都会出现两次,唯有一个数只会出现一次。

请你找出并返回只出现一次的那个数。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: nums = [3,3,7,7,10,11,11]
输出: 10

提示:

1 <= nums.length <= 10^5
0 <= nums[i] <= 10^5

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

思路

一个二分思路,

class Solution {
    public int singleNonDuplicate(int[] nums) {
        //异或是On
        //对数复杂度的话二分,怎么二分了
        //有奇数个数,如果当前查找的数是第偶数个,且前面一个数和当前数相同,那么说明前面都是成对的
        //如果前面一个数和当前数不同,那么就说明前面的数有单独的
        //如果当前个数是奇数个,那么也看前面一个数,如果和前面的相同,那么说明前面也不对了

        int n = nums.length;
        int left = 0;
        int right = n - 1;
        while(left < right){
            int mid = (right - left) / 2 + left;
            //如果当前个数是偶数
            if((mid + 1) % 2 == 0){
                //如果和前面的相同,那么说明前面没有单独的数
                if(mid > 0 && (nums[mid] == nums[mid - 1])){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }else{
                if(mid > 0 && (nums[mid] == nums[mid - 1])){
                    right = mid - 2;
                }else{
                    left = mid;
                }
            }
        }
        return nums[left];
    }
}

那么这道题主要学习的是什么呢,就是官解的这个异或
如果是奇数,那么异或就找的是前一个数;如果是偶数,异或找的就是后一个数
所以可以把两个逻辑合并到一个逻辑里

如果当前下标是偶数,那么和后一个数比,如果相同,说明前面没问题
如果当前下标是奇数,那么和前一个数比,如果相同,也说没前面没问题
可以写到一起

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int n = nums.length;
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] == nums[mid ^ 1]) l = mid + 1;
            else r = mid;
        }
        return nums[r];
    }
}


作者:AC_OIer
链接:https://leetcode-cn.com/problems/single-element-in-a-sorted-array/solution/gong-shui-san-xie-er-duan-xing-fen-xi-yu-17nv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1380. 矩阵中的幸运数

2022.2.15 每日一题

题目描述

给你一个 m * n 的矩阵,矩阵中的数字 各不相同 。请你按 任意 顺序返回矩阵中的所有幸运数。

幸运数是指矩阵中满足同时下列两个条件的元素:

在同一行的所有元素中最小
在同一列的所有元素中最大

示例 1:

输入:matrix = [[3,7,8],[9,11,13],[15,16,17]]
输出:[15]
解释:15 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。

示例 2:

输入:matrix = [[1,10,4,2],[9,3,8,7],[15,16,17,12]]
输出:[12]
解释:12 是唯一的幸运数,因为它是其所在行中的最小值,也是所在列中的最大值。

示例 3:

输入:matrix = [[7,8],[1,2]]
输出:[7]

提示:

m == mat.length
n == mat[i].length
1 <= n, m <= 50
1 <= matrix[i][j] <= 10^5
矩阵中的所有元素都是不同的

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

思路

模拟

class Solution {
    public List<Integer> luckyNumbers (int[][] matrix) {
        int m = matrix.length;
        int n = matrix[0].length;

        int[] hang = new int[m];

        int[] lie = new int[n];
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(matrix[i][j] < matrix[i][hang[i]]){
                    hang[i] = j;
                }
                if(matrix[i][j] > matrix[lie[j]][j])
                    lie[j] = i;
            }
        }

        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < m; i++){
            if(lie[hang[i]] == i)
                res.add(matrix[i][hang[i]]);
        }
        return res;
    }
}

1719. 重构一棵树的方案数

2022.2.16 每日一题

题目描述

给你一个数组 pairs ,其中 pairs[i] = [xi, yi] ,并且满足:

pairs 中没有重复元素
xi < yi

令 ways 为满足下面条件的有根树的方案数:

树所包含的所有节点值都在 pairs 中。
一个数对 [xi, yi] 出现在 pairs 中 当且仅当 xi 是 yi 的祖先或者 yi 是 xi 的祖先。
注意:构造出来的树不一定是二叉树。

两棵树被视为不同的方案当存在至少一个节点在两棵树中有不同的父节点。

请你返回:

如果 ways == 0 ,返回 0 。
如果 ways == 1 ,返回 1 。
如果 ways > 1 ,返回 2 。

一棵 有根树 指的是只有一个根节点的树,所有边都是从根往外的方向。

我们称从根到一个节点路径上的任意一个节点(除去节点本身)都是该节点的 祖先 。根节点没有祖先。

示例 1:

在这里插入图片描述
输入:pairs = [[1,2],[2,3]]
输出:1
解释:如上图所示,有且只有一个符合规定的有根树。

示例 2:

在这里插入图片描述
输入:pairs = [[1,2],[2,3],[1,3]]
输出:2
解释:有多个符合规定的有根树,其中三个如上图所示。

示例 3:

输入:pairs = [[1,2],[2,3],[2,4],[1,5]]
输出:0
解释:没有符合规定的有根树。

提示:

1 <= pairs.length <= 10^5
1 <= xi < yi <= 500
pairs 中的元素互不相同。

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

思路

说实话,真的难
看了一下题解,写的很详细,仔细看还是可以看懂的,但是在家也不想仔细研究了,cv了

class Solution {
    public int checkWays(int[][] pairs) {
        //根据第三个例子可以理解到一个题意,就是所有的关系都会在pairs数组中给出
        //在第三个例子中,因为所构造的树中的关系,pairs中并没有全部给出,所以不能形成一棵树
        //换句话也就是说,pairs中的关系不能形成一棵树

        Map<Integer, Set<Integer>> adj = new HashMap<Integer, Set<Integer>>();
        for (int[] p : pairs) {
            adj.putIfAbsent(p[0], new HashSet<Integer>());
            adj.putIfAbsent(p[1], new HashSet<Integer>());
            adj.get(p[0]).add(p[1]);
            adj.get(p[1]).add(p[0]);
        }
        /* 检测是否存在根节点*/
        int root = -1;
        Set<Map.Entry<Integer, Set<Integer>>> entries = adj.entrySet();
        for (Map.Entry<Integer, Set<Integer>> entry : entries) {
            int node = entry.getKey();
            Set<Integer> neighbours = entry.getValue();
            if (neighbours.size() == adj.size() - 1) {
                root = node;
            }
        }
        if (root == -1) {
            return 0;
        }

        int res = 1;
        for (Map.Entry<Integer, Set<Integer>> entry : entries) {
            int node = entry.getKey();
            Set<Integer> neighbours = entry.getValue();
            if (node == root) {
                continue;
            }
            int currDegree = neighbours.size();
            int parent = -1;
            int parentDegree = Integer.MAX_VALUE;

            /* 根据 degree 的大小找到 node 的父节点 parent */
            for (int neighbour : neighbours) {
                if (adj.get(neighbour).size() < parentDegree && adj.get(neighbour).size() >= currDegree) {
                    parent = neighbour;
                    parentDegree = adj.get(neighbour).size();
                }
            }
            if (parent == -1) {
                return 0;
            }

            /* 检测 neighbours 是否是 adj[parent] 的子集 */
            for (int neighbour : neighbours) {
                if (neighbour == parent) {
                    continue;
                }
                if (!adj.get(parent).contains(neighbour)) {
                    return 0;
                }
            }
            if (parentDegree == currDegree) {
                res = 2;
            }
        }
        return res;

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值