Unverified Commit c11ab1e5 authored by chilimyan's avatar chilimyan Committed by GitHub
Browse files

Merge pull request #4 from MisterBooo/master

更新项目
parents 244bfaad 62bcddcb
# LeetCode 第 260 号问题:只出现一次的数字 III
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 260 号问题:只出现一次的数字 III。题目难度为 Medium,目前通过率为 72.1% 。
## 题目描述
给定一个整数数组 `nums`,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
**示例:**
```
输入: [1,2,1,3,2,5]
输出: [3,5]
```
**注意:**
1. 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
2. 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
<br>
## 题目解析
第三道题目,和第一道题目只变化了一点,就是输入数组中除了 **两** 个元素出现了一次,其余的都出现了两次。我们依然可以从第一道题目的解法去思考这道题,如果我们还是按照第一题的做法,最后我们得到的答案将会是 `ele1 ^ ele2` 的结果,我们需要思考如何从这个结果出发,得到 `ele1``ele2`。首先思考一个问题 `ele1 ^ ele2` 的结果具体是什么,或者说里面有什么样的信息,异或操作是将相同的 bit 位置 0,相异的置 1,也就是说 **`ele1` 和 `ele2` 异或的结果中为 1 的 bit 位是两个元素相异的 bit 位,再进一步讲,我们可以用这个 bit 位来区分两个元素**。于是在第一题的基础之上,用一个 bit 位作为判断条件,来决定当前遍历到的元素和那个值进行异或,因为这时我们要求的值有两个。
从上面这些题目中你可以看到位运算的强大,三道系列题目的时间复杂度均为 `O(n)`,但是位运算是更加底层的运算,实际时间消耗会比正常操作要更快一些。在理解位运算的时候,**试着把 bit 作为最小单位去思考,或许会有不一样的发现**
<br>
### 代码实现
```java
public int[] singleNumber(int[] nums) {
if (nums == null || nums.length == 0) {
return new int[2];
}
int different = 0;
for (int i : nums) {
different ^= i;
}
// 这个操作是取 different 从左往右最后一个为 1 的 bit 位
different &= -different;
int[] ans = {0, 0};
for (int i : nums) {
if ((different & i) == 0) {
ans[0] ^= i;
} else {
ans[1] ^= i;
}
}
return ans;
}
```
<br>
### 动画演示
![](../Animation/260.gif)
![](../../Pictures/qrcode.jpg)
\ No newline at end of file
# 530. 二叉搜索树的最小绝对差
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上 530. 二叉搜索树的最小绝对差. 是关于树的一道题。
## 题目
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
 
示例:
```
输入:
1
\
3
/
2
输出:
1
解释:
最小绝对差为 1,其中 2 和 1 的差的绝对值为 1(或者 2 和 3)。
```
提示:
树中至少有 2 个节点。
本题与 783 https://leetcode-cn.com/problems/minimum-distance-between-bst-nodes/ 相同
## 题目解析
计算树中任意两节点的差的绝对值的最小值,那么肯定是要遍历树,然后相邻节点求差对比是不是最小的。
二叉树的遍历有三种,前序遍历,中序遍历,后序遍历。
题目中给的是二叉搜索树,二叉搜索树有一个特色,就是中序遍历出来的结果,值是按照从小到大排列的。
所以我们只要中序遍历,保存上一个节点,然后遍历的时候取得当前节点和上一个节点的值的绝对值,如果比当前最小差还要小,那么更新最小差。
中序遍历是遍历左子树,然后根节点,最后是右子树,我们用递归去实现。
## 动画理解
<video id="video" controls="" preload="none" >
<source id="mp4" src="../Animation/0530.m4v" type="video/mp4">
</video>
## 参考代码
```javaScript
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var getMinimumDifference = function(root) {
let min = Number.MAX_VALUE
let preNode = null
var travelTree = function (node) {
if (node) {
travelTree(node.left)
if(preNode) {
min = Math.min(min, Math.abs(preNode.val - node.val))
}
preNode = node
travelTree(node.right)
}
}
travelTree(root)
return min
};
```
## 复杂度分析
时间复杂度:O(N),N为树中节点个数。
空间复杂度:O(log(N))。
![](../../Pictures/qrcode.jpg)
\ No newline at end of file
# LeetCode 图解 |
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
本题解作者:nettee
## 题目描述
给定一个包含了一些 `0``1` 的非空二维数组 `grid`
一个**岛屿**是由一些相邻的 `1` (代表土地) 构成的组合,这里的「相邻」要求两个 `1` 必须在水平或者竖直方向上相邻。你可以假设 `grid` 的四个边缘都被 `0`(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 `0`。)
**示例 1:**
```
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
```
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 `1`
**示例 2:**
```
[[0,0,0,0,0,0,0,0]]
```
对于上面这个给定的矩阵, 返回 0。
注意: 给定的矩阵 `grid` 的长度和宽度都不超过 50。
## 题目解析
这道题的主要思路是深度优先搜索。每次走到一个是 1 的格子,就搜索整个岛屿,并计算当前岛屿的面积。最后返回岛屿面积的最大值。
网格可以看成是一个无向图的结构,每个格子和它上下左右的四个格子相邻。如果四个相邻的格子坐标合法,且是陆地,就可以继续搜索。
在深度优先搜索的时候要注意避免重复遍历。我们可以把已经遍历过的陆地改成 2,这样遇到 2 我们就知道已经遍历过这个格子了,不进行重复遍历。
## 动画理解
![](../Animation/Animation.gif)
## 参考代码
C++ 代码:
```C++
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int res = 0;
for (int r = 0; r < grid.size(); r++) {
for (int c = 0; c < grid[0].size(); c++) {
if (grid[r][c] == 1) {
int a = area(grid, r, c);
res = max(res, a);
}
}
}
return res;
}
int area(vector<vector<int>>& grid, int r, int c) {
if (!(inArea(grid, r, c))) {
return 0;
}
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return 1
+ area(grid, r - 1, c)
+ area(grid, r + 1, c)
+ area(grid, r, c - 1)
+ area(grid, r, c + 1);
}
bool inArea(vector<vector<int>>& grid, int r, int c) {
return 0 <= r && r < grid.size()
&& 0 <= c && c < grid[0].size();
}
};
```
Java 代码:
```Java
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
for (int r = 0; r < grid.length; r++) {
for (int c = 0; c < grid[0].length; c++) {
if (grid[r][c] == 1) {
int a = area(grid, r, c);
res = Math.max(res, a);
}
}
}
return res;
}
int area(int[][] grid, int r, int c) {
if (!inArea(grid, r, c)) {
return 0;
}
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return 1
+ area(grid, r - 1, c)
+ area(grid, r + 1, c)
+ area(grid, r, c - 1)
+ area(grid, r, c + 1);
}
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length
&& 0 <= c && c < grid[0].length;
}
}
```
Python 代码:
```Python
class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
res = 0
for r in range(len(grid)):
for c in range(len(grid[0])):
if grid[r][c] == 1:
a = self.area(grid, r, c)
res = max(res, a)
return res
def area(self, grid: List[List[int]], r: int, c: int) -> int:
if not self.inArea(grid, r, c):
return 0
if grid[r][c] != 1:
return 0
grid[r][c] = 2
return 1 \
+ self.area(grid, r - 1, c) \
+ self.area(grid, r + 1, c) \
+ self.area(grid, r, c - 1) \
+ self.area(grid, r, c + 1)
def inArea(self, grid: List[List[int]], r: int, c: int) -> bool:
return 0 <= r < len(grid) and 0 <= c < len(grid[0])
```
## 复杂度分析
设网格的边长为 n,则时间复杂度为 O(n²)。
\ No newline at end of file
## LeetCode第942号问题:增减字符串匹配
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步个人博客:www.zhangxiaoshuai.fun
本题在leetcode中题目序号942,属于easy级别,目前通过率为71.4%
### 题目描述:
```
给定只含 "I"(增大)或 "D"(减小)的字符串 S ,令 N = S.length。
返回 [0, 1, ..., N] 的任意排列 A 使得对于所有 i = 0, ..., N-1,都有:
如果 S[i] == "I",那么 A[i] < A[i+1]
如果 S[i] == "D",那么 A[i] > A[i+1]
示例 1:
输出:"IDID"
输出:[0,4,1,3,2]
示例 2:
输出:"III"
输出:[0,1,2,3]
示例 3:
输出:"DDI"
输出:[3,2,0,1]
提示:
1 <= S.length <= 10000
S 只包含字符 "I" 或 "D"
```
**题目分析:**
```
题目中的意思很明确,我们只要满足给出的两个条件即可。
1.假如字符串的长度为N,那么目标数组的长度就为N+1;
2.数组中的数字都是从0~N,且没有重复;
3.遇见‘I’,要增加;遇见‘D’要减少;
```
### GIF动画演示:
![](../Animation/0942-di-String-Match01.gif)
### 代码:
```java
//这里搬运下官方的解法
public int[] diStringMatch(String S) {
int N = S.length();
int lo = 0, hi = N;
int[] ans = new int[N + 1];
for (int i = 0; i < N; ++i) {
if (S.charAt(i) == 'I')
ans[i] = lo++;
else
ans[i] = hi--;
}
ans[N] = lo;
return ans;
}
```
**虽然上述代码很简洁,好像已经不需要我们去实现什么;但是满足条件的序列并不止一种,官方的好像只能通过一种,下面的代码虽然有些冗余,但是得出的序列是满足题意要求的,但是并不能AC;**
### 思路:
```
(1)如果遇见的是‘I’,那么对应数组当前位置的数字要小于它右边的第一个数字
(2)如果遇见的是‘D’,那么对应数组当前位置的数字要大于它右边的第一个数字
首先对目标数组进行初始化,赋值0~N
我们开始遍历字符串,如果遇见‘I’就判断对应数组该位置上的数是否满足(1)号条件
如果满足,跳过本次循环;如果不满足,交换两个数字的位置;
对于‘D’,也是同样的思路;
```
### GIF动画演示:
![](../Animation/0942-di-String-Match02.gif)
### 代码:
```java
public int[] diStringMatch(String S) {
int[] res = new int[S.length()+1];
String[] s = S.split("");
for (int i = 0; i < res.length; i++) {
res[i] = i;
}
for (int i = 0; i < s.length; i++) {
if (s[i].equals("I")) {
//判断指定位置的数字是否符合条件
if (res[i] < res[i + 1]) {
continue;
} else {
//交换两个数字的位置
res[i] = res[i] ^ res[i+1];
res[i+1] = res[i] ^ res[i+1];
res[i] = res[i] ^ res[i+1];
}
} else {
if (res[i] > res[i + 1]) {
continue;
} else {
res[i] = res[i] ^ res[i+1];
res[i+1] = res[i] ^ res[i+1];
res[i] = res[i] ^ res[i+1];
}
}
}
return res;
}
```
**以上内容如有错误、不当之处,欢迎批评指正。**
\ No newline at end of file
## LeetCode第1054号问题:距离相等的条形码
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步个人博客:www.zhangxiaoshuai.fun
**本题选自leetcode第1054号问题,medium级别,目前通过率33.3%**
**题目描述:**
在一个仓库里,有一排条形码,其中第 i 个条形码为 barcodes[i]。
请你重新排列这些条形码,使其中两个相邻的条形码不能相等。
你可以返回任何满足该要求的答案,此题保证存在答案。
示例 1:
输入:[1,1,1,2,2,2]
输出:[2,1,2,1,2,1]
示例 2:
输入:[1,1,1,1,2,2,3,3]
输出:[1,3,1,3,2,1,2,1]
提示:
1 <= barcodes.length <= 10000
1 <= barcodes[i] <= 10000
### 题目分析:
1.首先我们需要将每个条形码和出现的次数作一记录,为了存取方便,这里使用数组(题目中已经给出了数组的最大和最小长度)进行操作;
2.找出其中出现最多次数的条形码,拿到该barcode和count;
3.先将出现次数最多的条形码存入目标数组中(偶数位或者奇数位),并对记录数组作一更新;
4.随后将剩余的barcode填充进目标数组中。
### GIF动画展示:
![](../Animation/1054-rearrangeBarcodes.gif)
### 代码:
```java
public static int[] rearrangeBarcodes(int[] barcodes){
int[] address = new int[10001];
for (int barcode : barcodes)
address[barcode]++;
// 找到出现次数最多的barcode
int maxCode = 0, maxCount = 0;
for (int i = 0; i < address.length; i++) {
if (maxCount < address[i]) {
maxCode = i;
maxCount = address[i];
}
}
int index = 0;
// 先填充最大的那一位barcode
for (; address[maxCode] > 0; index += 2) {
barcodes[index] = maxCode;
address[maxCode]--;
}
// 继续填充剩余的条形码
for (int i = 1; i < address.length; i++) {
while (address[i] > 0) {
//偶数位填充完毕
if (index >= barcodes.length) index = 1;
barcodes[index] = i;
address[i]--;
index += 2;
}
}
return barcodes;
}
```
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment