【LeetCode】第二次总结。

计划对LeetCode刷题,顺便总结一下,四个阶段:

  1. 找出典型中等、困难题目,走读题目根据自己还记得的算法尝试回忆一遍
  2. 对照答案,识别自己的算法弱项,巩固算法后练习一遍典型习题
  3. 尝试找出同类型题目,巩固练习一遍
  4. 找出算法短板,重复1/2/3

算法总结

题目我的算法典型算法
建筑物的最短距离暴力算法,挨个点计算距离和,然后得出最小的距离和,过程中使用BFS同时记录访问过点解法来看都是BFS,不过有两种思路:
1、从每个建筑物出发BFS,计算每个空地的权值和,最后找到权值和最小的空地点
2、从每个空地出发BFS,计算每个空地到所有建筑物的权值和,最后找到权值和最小的空地点;
感觉从建筑物出发的方式好一些。
寻找峰值暴力破解,遍历一次数组,从index=1开始,满足条件则返回;不满足则从index+2的位置进行判断我的解法是官方解法的一种,但是时间复杂度是On,不满足Onlog2n的要求,采取二分查找的方式找到这个峰值点,这样就能Onlog2n,一般使用二分的前提是有序,而本题中药将有序替换为局部有序,可以按照如下方式理解,当我在其中一个山峰,其实有三种可能
1. 左边的山峰比我现在在的山峰还高(注意确保左边还有山峰),因为只要找一种可能,我就去左边找比我高的
2. 右边的山峰更高(注意确保右边还有山峰),我就去右边找
3. 如果左右两边都没有我高,那我就是其中一个顶峰。
只出现一次的数字暴力破解,和之前做过的一个单身狗的题一样,那个题的是情人节找出广场上的单身狗使用集合或者hashset解决问题,时间复杂度Onlog2n,空间复杂度On,通过异或运算,时间复杂度On,空间复杂度O1;由于题目中只有一个不一样的,那么在一次遍历中,两两相同的数字通过异或,就会变成0,而我们要找的target = target ⊕ 0,恰好能得出target
课程表正经思路应该是并查集,先mark一下想多了,不是并查集,而是拓扑排序,拓扑排序意为对一个有向图来说,是否存在一个点的排序序列,使得任意一个有向边(u,v),u在排序序列中一定在v前面,其实就是判断图是否是一个DAG,有向无环图,有两种解法可以获得拓扑排序:
1、BFS;
2、DFS;
具体的代码暂时没看,先mark一下。
矩阵中的最长递增路径递归,遍历每个点,递归计算最深路径官方解法
解法一:记忆化深度优先搜索,其实和我想的保存最优解的递归一样,这个思路不做赘述,只是写法不同;
解法二:拓扑排序,具体的代码暂时没看,先mark一下。
被围绕的区域暴力破解,只要不爱这边的O,都变成X审题错误,和边相连的无效点,可能会和内部其他点联通,从而整体形成不被包围的多个点,类似围棋的‘气’,因此应该用DFS/BFS等搜索算法对所有点进行标记识别;
1、DFS,分别计算出有气的1点,即矩阵四周的1点,从1点DFS矩阵的1的联通点,进行标记;完成所有1点的DFS,进行遍历就算更新标记过的点不变,未标记过的全部变成X;
2、BFS,同时对所有有气的1点进行遍历标记,最终也是遍历矩阵并对标记状态进行计算;
情侣牵手没有解决办法审题错误,交换过程可以不相邻。
解法一:贪心,从0开始遍历是否满足配对,不满足则找到对方在哪儿,然后做一次交换并记录;找到对方可以通过遍历,也可以通过一次数据预处理将对方位置放在一个map里,方便快速找到对方下标没搞明白这个贪心算法在哪儿? 解题过程如下:在这里插入图片描述解法二:并查集,算法参考这个:https://www.bilibili.com/video/av38498175?p=2,解题思路如下:
待补充
寻找重复子树暴力破解,求出所有子树,去重算出结果解法一:暴力破解,不过暴力过程中,保存子树的方式是用二叉树的序列化方法表示
在这里插入图片描述
使用BFS或DFS遍历二叉树的每个节点,算出每个子树的序列化表示,通过map判断重复得到结果。
解法二:唯一标识子树,认为每个子树都可以用一个唯一id表示,id = node.val + getId(leftNode) + getId(rightNode),将所有的子树id都放入map中,判断重复可得到结果;
第一种方法需要基于每个节点计算子树,因此n个节点,要进行n次子树的计算,时间复杂度On2;解法二只用遍历一次二叉树即可获得所有子树的 id,其本质是自底向上的遍历计算,和第一种自顶向下的区别就在这里,因此时间复杂度是On

整体看题目难度,比较困难的是:寻找峰值、情侣牵手、课程表、只出现一次的数字,首先算法上情侣牵手要向并查集建模,而课程表要做拓扑排序,如果时间/空间复杂度有要求的情况下,解法相对唯一,另外包括只出现一次的数据,解法优化的唯一解也是异或。寻找峰值这个题在特殊场景下需要对二分查找进行优化。

涉及到算法:拓扑排序、异或、并查集、DFS、BFS、二分查找优化、二叉树序列化

贪心算法和拓扑排序,包括二分查找相同,泛指一类算法,本次练习过程中,发现二分查找本质上是运用规律进行折半查找,而有序只是在大多数场景下用到的一种折半原则,本次练习的寻找峰值就可以用趋势来替代有序,类似贪心算法、拓扑排序一样,实现方法有很多,总体思路是可以进行归类的。





建筑物的最短距离

链接:https://leetcode-cn.com/problems/shortest-distance-from-all-buildings

你是个房地产开发商,想要选择一片空地 建一栋大楼。你想把这栋大楼够造在一个距离周边设施都比较方便的地方,通过调研,你希望从它出发能在 最短的距离和 内抵达周边全部的建筑物。请你计算出这个最佳的选址到周边全部建筑物的 最短距离和。

提示:

你只能通过向上、下、左、右四个方向上移动。

给你一个由 0、1 和 2 组成的二维网格,其中:

0 代表你可以自由通过和选择建造的空地
1 代表你无法通行的建筑物
2 代表你无法通行的障碍物

示例:

输入:[[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]]

1 - 0 - 2 - 0 - 1
| | | | |
0 - 0 - 0 - 0 - 0
| | | | |
0 - 0 - 1 - 0 - 0
输出:7
解析:
给定三个建筑物 (0,0)、(0,4) 和 (2,2) 以及一个位于 (0,2) 的障碍物。
由于总距离之和 3+3+1=7 最优,所以位置 (1,2) 是符合要求的最优地点,故返回7。
题目数据保证至少存在一栋建筑物,如果无法按照上述规则返回建房地点,则请你返回 -1。

思路:计算图的中心点,但考虑到障碍物,不能直接算几何中心,根据提示,可能会有无法到达的建筑,目前只能想到暴力算法,挨个点计算距离和,然后得出最小的距离和
我们认为每个0点有一个属性distance[],记录到所有1点的最短距离,该0点的最短距离就是SUM distance[]
1、遍历图中的每一个0的位置,以每一个0点作为出发点,BFS算出到其他1点的距离
2、如果有1点无法到达,返回-1
3、一次BFS的过程中,可以计算出每个途径点到所有1点的最短距离
4、比对每个0点的最短距离和

按照从建筑物出发BFS/DFS,分别尝试了一下,没有AC,都卡在同一个场景下了。
题有点难,先mark,回头再做。

寻找峰值

链接:https://leetcode-cn.com/problems/find-peak-element

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞。

示例 1:

输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。
示例 2:

输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
说明:

你的解法应该是 O(logN) 时间复杂度的。

思路:暴力破解,遍历一次数组,从index=1开始,满足条件则返回;不满足则从index+2的位置进行判断
1、从index=1的位置判断是否满足条件
2、满足则返回,不满足则index=index+2,
3、重复1的逻辑

感觉暴力法就很快(第一次解答错误是因为返回的是数值而不是索引)
在这里插入图片描述

    /**
     * 1、暴力解法
     * 直接遍历,看看是否满足升序,如果出现满足,则找到峰值,最大On
     * 对于数据有倾斜的情况不太有友好,雷思思用例3
     */
    public static int findPeakElement(int[] nums) {
        for (int i = 0; i < nums.length - 1; i++) {
            if (nums.length == 1) {
                return 0;
            }

            if (nums[i + 1] > nums[i]) {
                continue;
            } else {
                return i;
            }
        }

        return nums.length - 1;
    }

用二分查找试了一下。(发现写的很费劲,好多边界情况需要考虑)
在这里插入图片描述

    /**
     * 1、二分查找
     * 根据题意,我们可以基于连续三个序列的升序/降序来判断峰值的左右情况
     * 如果升序,说明在右
     * 如果降序,说明在左
     */
    public static int findPeakElement(int[] nums) {
        if (nums.length == 1) {
            return 0;
        }

        // 从中间开始找
        int left = 0, right = nums.length;
        int index = (left + right) / 2;

        while (left <= right) {
            if (index == 0) {
                return nums[0] > nums[1] ? 0 : 1;
            }

            if (index == nums.length - 1) {
                return nums[nums.length - 2] > nums[nums.length - 1] ? nums.length - 2 : nums.length - 1;
            }

            if (nums[index - 1] < nums[index] && nums[index] < nums[index + 1]) {
                left = index + 1;
                index = (left + right) / 2;
            } else if (nums[index - 1] > nums[index] && nums[index] > nums[index + 1]) {
                right = index - 1;
                index = (left + right) / 2;
            } else {
                int max = Math.max(Math.max(nums[index - 1], nums[index + 1]), nums[index]);
                if (max == nums[index + 1]) return index + 1;
                if (max == nums[index - 1]) return index - 1;
                if (max == nums[index]) return index;
            }
        }

        return index;
    }

只出现一次的数字

链接:https://leetcode-cn.com/problems/single-number

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1]
输出: 1
示例 2:

输入: [4,1,2,1,2]
输出: 4

思路:暴力破解,和之前做过的一个单身狗的题一样,那个题的是情人节找出广场上的单身狗
1、遍历数组元素,判断是否在set/treeSet里
2、如果存在,则删除set/treeSet元素
3、如果不存在,add到set/treeSet
4、set/treeSet最后只剩下一个元素

暴力法就不写了。

    public static int singleNumber(int[] nums) {
        int result = nums[0];

        if (nums.length == 1) {
            return nums[0];
        }

        for (int i = 1; i < nums.length; i++) {
            result = result ^ nums[i];
        }

        return result;
    }

课程表

链接:https://leetcode-cn.com/problems/course-schedule

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

思路:正经思路应该是并查集,先mark一下

矩阵中的最长递增路径

链接:https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums =
[
[9,9,4],
[6,6,8],
[2,1,1]
]
输出: 4
解释: 最长递增路径为 [1, 2, 6, 9]。
示例 2:

输入: nums =
[
[3,4,5],
[3,2,6],
[2,2,1]
]
输出: 4
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

思路:递归,遍历每个点,递归计算最深路径
1、全局变量记录maxDeep
2、遍历每个元素,计算每个元素的mostDeep
3、mostDeep = mostDeep(下一个节点) + 1
4、mostDeep的逻辑是
a. 当前点已经无路可走,返回1
b. 当前点已经在existMaxDeep中,返回缓存existMaxDeep已经计算的maxDeep
c. 标记当前点已经访问,返回 mostDeep(周围未访问点) + 1,增加到缓存existMaxDeep
5、计算完一个元素后,清空标记点的缓存

被围绕的区域

链接:https://leetcode-cn.com/problems/surrounded-regions

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。

找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例:

X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:

X X X X
X X X X
X X X X
X O X X
解释:

被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

思路:暴力破解,只要不爱这边的O,都变成X
1、遍历矩阵元素,如果是X,continue
2、如果是O,且不满足以下条件时,将元素变为X

遍历索引 i != 0 && j != 0 && i!=metrc.length - 1 && j != metrc.length - 1

3、完成遍历,输出矩阵

情侣牵手

链接:https://leetcode-cn.com/problems/couples-holding-hands

N 对情侣坐在连续排列的 2N 个座位上,想要牵到对方的手。 计算最少交换座位的次数,以便每对情侣可以并肩坐在一起。 一次交换可选择任意两人,让他们站起来交换座位。

人和座位用 0 到 2N-1 的整数表示,情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2N-2, 2N-1)。

这些情侣的初始座位 row[i] 是由最初始坐在第 i 个座位上的人决定的。

示例 1:

输入: row = [0, 2, 1, 3]
输出: 1
解释: 我们只需要交换row[1]和row[2]的位置即可。
示例 2:

输入: row = [3, 2, 0, 1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。
说明:

len(row) 是偶数且数值在 [4, 60]范围内。
可以保证row 是序列 0…len(row)-1 的一个全排列。

思路:没有解决办法

寻找重复子树

链接:https://leetcode-cn.com/problems/find-duplicate-subtrees

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

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

示例 1:

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

下面是两个重复的子树:

  2
 /
4

4

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

思路:暴力破解,求出所有子树,去重算出结果
1、定义getSubTree,获取子树集合
2、根节点触发,totalTree = getSubTree(currentNode,left).addall(getSubTree(currentNode,right))
3、定义getSubTree:
a.如果left == right == null,则返回trees,trees.add(currentNode), trees.add(upNode+currentNode)
b.如果任意子节点不为空,则返回getSubTree(left/right).
4、最后计算totalTree中重复的字符串

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值