Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
wwwanlingxiao
LeetCodeAnimation
Commits
f1048508
Commit
f1048508
authored
Apr 17, 2020
by
程序员吴师兄
Browse files
整理文件
parent
c3aa5c59
Changes
65
Hide whitespace changes
Inline
Side-by-side
0169-Majority-Element/Animation/Animation2.gif
0 → 100644
View file @
f1048508
141 KB
0169-Majority-Element/Animation/Animation3.gif
0 → 100644
View file @
f1048508
238 KB
0169-Majority-Element/Article/0169-Majority-Element.md
0 → 100644
View file @
f1048508
# 【数组中超过一半的数字】三种解法,最后一个解法太牛逼了!
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
今天分享的题目来源于 LeetCode 上第 169 号问题:求众数(求数组中超过一半的数字)。题目难度为 Easy,目前通过率为 45.8% 。
最后一种解法
**Cool**
!!!
# 题目描述
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
**示例 1:**
```
输入: [3,2,3]
输出: 3
```
**示例 2:**
```
输入: [2,2,1,1,1,2,2]
输出: 2
```
# 题目解析
题目意思很好理解:给你一个数组,里面有一个数字出现的次数超过了一半,你要找到这个数字并返回。
## 解法一:暴力解法
遍历整个数组,同时统计每个数字出现的次数。
最后将出现次数大于一半的元素返回即可。
### 动画描述

### **代码实现**
```
java
class
Solution
{
public
int
majorityElement
(
int
[]
nums
)
{
int
majorityCount
=
nums
.
length
/
2
;
for
(
int
num
:
nums
)
{
int
count
=
0
;
for
(
int
elem
:
nums
)
{
if
(
elem
==
num
)
{
count
+=
1
;
}
}
if
(
count
>
majorityCount
)
{
return
num
;
}
}
}
}
```
### 复杂度分析
**时间复杂度**
:O(n
<sup>
2
</sup>
)
暴力解法包含两重嵌套的 for 循环,每一层 n 次迭代,因此时间复杂度为 O(n
<sup>
2
</sup>
) 。
**空间复杂度**
:O(1)
暴力解法没有分配任何与输入规模成比例的额外的空间,因此空间复杂度为 O(1)。
## 解法二:哈希表法
这个问题可以视为查找问题,对于查找问题往往可以使用时间复杂度为 O(1) 的
**哈希表**
,通过以空间换时间的方式进行优化。
直接遍历整个
**数组**
,将每一个数字(num)与它出现的次数(count)存放在
**哈希表**
中,同时判断该数字出现次数是否是最大的,动态更新 maxCount,最后输出 maxNum。
### 动画描述

### 代码实现
```
java
class
Solution
{
public
int
majorityElement
(
int
[]
nums
)
{
Map
<
Integer
,
Integer
>
map
=
new
HashMap
<>();
// maxNum 表示元素,maxCount 表示元素出现的次数
int
maxNum
=
0
,
maxCount
=
0
;
for
(
int
num:
nums
)
{
int
count
=
map
.
getOrDefault
(
num
,
0
)
+
1
;
map
.
put
(
num
,
count
);
if
(
count
>
maxCount
)
{
maxCount
=
count
;
maxNum
=
num
;
}
}
return
maxNum
;
}
}
```
### 复杂度分析
**时间复杂度**
:O(n)
总共有一个循环,里面哈希表的插入是常数时间的,因此时间复杂度为 O(n)。
**空间复杂度**
:O(n)
哈希表占用了额外的空间 O(n),因此空间复杂度为 O(n)。
## 解法三:摩尔投票法
再来回顾一下题目:寻找数组中超过一半的数字,这意味着数组中
**其他数字出现次数的总和都是比不上这个数字出现的次数**
。
即如果把 该众数记为
`+1`
,把其他数记为
`−1`
,将它们全部加起来,和是大于 0 的。
所以可以这样操作:
*
设置两个变量 candidate 和 count,
**candidate**
用来保存数组中遍历到的某个数字,
**count**
表示当前数字的出现次数,一开始
**candidate**
保存为数组中的第一个数字,
**count**
为 1
*
遍历整个数组
*
如果数字与之前
**candidate**
保存的数字相同,则
**count**
加 1
*
如果数字与之前
**candidate**
保存的数字不同,则
**count**
减 1
*
如果出现次数
**count**
变为 0 ,
**candidate**
进行变化,保存为当前遍历的那个数字,并且同时把
**count**
重置为 1
*
遍历完数组中的所有数字即可得到结果
### 动画描述

### 代码实现
```
java
class
Solution
{
public
int
majorityElement
(
int
[]
nums
)
{
int
candidate
=
nums
[
0
],
count
=
1
;
for
(
int
i
=
1
;
i
<
nums
.
length
;
++
i
)
{
if
(
count
==
0
)
{
candidate
=
nums
[
i
];
count
=
1
;
}
else
if
(
nums
[
i
]
==
candidate
)
{
count
++;
}
else
{
count
--;
}
}
return
candidate
;
}
}
```
### 复杂度分析
**时间复杂度**
:O(n)
总共只有一个循环,因此时间复杂度为 O(n)。
**空间复杂度**
:O(1)
只需要常数级别的额外空间,因此空间复杂度为 O(1)。

\ No newline at end of file
0172-Factorial-Trailing-Zeroes/Article/0172-Factorial-Trailing-Zeroes.md
0 → 100644
View file @
f1048508
# LeetCode第 172 号问题:阶乘后的零
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 172 号问题:阶乘后的零。题目难度为 Easy,目前通过率为 38.0% 。
### 题目描述
给定一个整数
*n*
,返回
*n*
! 结果尾数中零的数量。
**示例 1:**
```
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
```
**示例 2:**
```
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
```
**说明:**
你算法的时间复杂度应为
*O*
(log
*n*
) 。
### 题目解析
题目很好理解,数阶乘后的数字末尾有多少个零。
最简单粗暴的方法就是先乘完再说,然后一个一个数。
事实上,你在使用暴力破解法的过程中就能发现规律:
**这 9 个数字中只有 2(它的倍数) 与 5 (它的倍数)相乘才有 0 出现**
。
所以,现在问题就变成了这个阶乘数中能配
**多少对 2 与 5**
。
举个复杂点的例子:
` 10! = 【 2 *( 2 * 2 )* 5 *( 2 * 3 )*( 2 * 2 * 2 )*( 2 * 5)】`
在 10!这个阶乘数中可以匹配两对 2
*
5 ,所以10!末尾有 2 个 0。
可以发现,一个数字进行拆分后 2 的个数肯定是大于 5 的个数的,所以能匹配多少对取决于 5 的个数。(好比现在男女比例悬殊,最多能有多少对异性情侣取决于女生的多少)。
那么问题又变成了
**统计阶乘数里有多少个 5 这个因子**
。
需要注意的是,像 25,125 这样的不只含有一个 5 的数字的情况需要考虑进去。
比如
`n = 15`
。那么在
`15!`
中 有
`3`
个
`5`
(来自其中的
`5`
,
`10`
,
`15`
), 所以计算
`n/5`
就可以 。
但是比如
`n=25`
,依旧计算
`n/5`
,可以得到
`5`
个
`5`
,分别来自其中的
`5, 10, 15, 20, 25`
,但是在
`25`
中其实是包含
`2 `
个
`5`
的,这一点需要注意。
所以除了计算
`n/5`
, 还要计算
`n/5/5 , n/5/5/5 , n/5/5/5/5 , ..., n/5/5/5,,,/5`
直到商为0,然后求和即可。
### 代码实现
```
java
public
class
Solution
{
public
int
trailingZeroes
(
int
n
)
{
return
n
==
0
?
0
:
n
/
5
+
trailingZeroes
(
n
/
5
);
}
}
```

0201-Bitwise-And-Of-Numbers-Range/Article/0201-Bitwise-And-Of-Numbers-Range.md
0 → 100644
View file @
f1048508
# LeetCode 第 201 号问题:数字范围按位与
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 201 号问题:数字范围按位与。题目难度为 Medium,目前通过率为 39.1% 。
### 题目描述
给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。
**示例 1:**
```
输入: [5,7]
输出: 4
```
**示例 2:**
```
输入: [0,1]
输出: 0
```
### 题目解析
以 [ 26 ,30] 为例。
首先,将 [ 26 , 30 ] 的范围数字用二进制表示出来:
**11**
010
**11**
011
**11**
100
**11**
101
**11**
110
而输出 24 的二进制是 11000 。
可以发现,只要找到二进制的
**左边公共部分**
即可。
所以,可以先建立一个 32 位都是 1 的 mask,然后每次向左移一位,比较 m 和 n 是否相同,不同再继续左移一位,直至相同,然后把 m 和 mask 相与就是最终结果。
### 动画描述
暂无
### 代码实现
```
c++
class
Solution
{
public:
int
rangeBitwiseAnd
(
int
m
,
int
n
)
{
unsigned
int
d
=
INT_MAX
;
while
((
m
&
d
)
!=
(
n
&
d
))
{
d
<<=
1
;
}
return
m
&
d
;
}
};
```

0203-Remove-Linked-List-Elements/Animation/Animation.gif
0 → 100644
View file @
f1048508
279 KB
0203-Remove-Linked-List-Elements/Article/0203-Remove-Linked-List-Elements.md
0 → 100644
View file @
f1048508
# LeetCode 第 203 号问题:移除链表元素
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 203 号问题:移除链表元素。题目难度为 Easy,目前通过率为 55.8% 。
### 题目描述
删除链表中等于给定值
**val**
的所有节点。
**示例:**
```
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
```
### 题目解析
主要考察了基本的链表遍历和设置指针的知识点。
定义一个虚拟头节点
`dummyHead `
,遍历查看原链表,遇到与给定值相同的元素,将该元素的前后两个节点连接起来,然后删除该元素即可。
### 动画描述

### 代码实现
#### 代码一
```
// 203. Remove Linked List Elements
// https://leetcode.com/problems/remove-linked-list-elements/description/
// 使用虚拟头结点
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 创建虚拟头结点
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur->next != NULL){
if(cur->next->val == val){
ListNode* delNode = cur->next;
cur->next = delNode->next;
delete delNode;
}
else
cur = cur->next;
}
ListNode* retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
};
```
#### 代码二
用递归来解。
通过递归调用到链表末尾,然后回来,需要删的元素,将链表next指针指向下一个元素即可。
```
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if (!head) return NULL;
head->next = removeElements(head->next, val);
return head->val == val ? head->next : head;
}
};
```

\ No newline at end of file
0206-Reverse-Linked-List/Animation/Animation.gif
0 → 100644
View file @
f1048508
439 KB
0206-Reverse-Linked-List/Article/0206-Reverse-Linked-List.md
0 → 100644
View file @
f1048508
# LeetCode 第 206 号问题:反转链表
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 206 号问题:反转链表。题目难度为 Easy,目前通过率为 45.8% 。
### 题目描述
反转一个单链表。
**示例:**
```
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
```
**进阶:**
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
### 题目解析
设置三个节点
`pre`
、
`cur`
、
`next`
-
(1)每次查看
`cur`
节点是否为
`NULL`
,如果是,则结束循环,获得结果
-
(2)如果
`cur`
节点不是为
`NULL`
,则先设置临时变量
`next`
为
`cur`
的下一个节点
-
(3)让
`cur`
的下一个节点变成指向
`pre`
,而后
`pre`
移动
`cur`
,
`cur`
移动到
`next`
-
(4)重复(1)(2)(3)
### 动画描述

### 代码实现
```
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* cur = head;
while(cur != NULL){
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
```

\ No newline at end of file
0209-Minimum-Size-Subarray-Sum/Animation/Animation.gif
0 → 100644
View file @
f1048508
143 KB
0209-Minimum-Size-Subarray-Sum/Article/0209-Minimum-Size-Subarray-Sum.md
0 → 100644
View file @
f1048508
# LeetCode 第 209 号问题:长度最小的子数组
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 209 号问题:长度最小的子数组。题目难度为 Medium,目前通过率为 25.8% 。
### 题目描述
给定一个含有
**n**
个正整数的数组和一个正整数
**s ,**
找出该数组中满足其和
**≥ s**
的长度最小的连续子数组
**。**
如果不存在符合条件的连续子数组,返回 0。
**示例:**
```
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
```
**进阶:**
如果你已经完成了
*O*
(
*n*
) 时间复杂度的解法, 请尝试
*O*
(
*n*
log
*n*
) 时间复杂度的解法。
### 题目解析
定义两个指针 left 和 right ,分别记录子数组的左右的边界位置。
*
(1)让 right 向右移,直到子数组和大于等于给定值或者 right 达到数组末尾;
*
(2)更新最短距离,将 left 像右移一位,sum 减去移去的值;
*
(3)重复(1)(2)步骤,直到 right 到达末尾,且 left 到达临界位置
### 动画描述

设置滑动窗口的长度为 0 ,位于数轴的最左端。
##### 1 .滑动窗口右端 R 开始移动,直到区间满足给定的条件,也就是和大于 7 ,此时停止于第三个元素 2,当前的最优长度为 4

##### 2. 滑动窗口左端 L 开始移动,缩小滑动窗口的大小,停止于第一个元素 3,此时区间和为 6,使得区间和不满足给定的条件(此时不大于 7)

#### 3. 滑动窗口右端 R 继续移动,停止于第四个元素 4,此时和位 10 ,但最优长度仍然为 4

### 代码实现
```
// 滑动窗口的思路
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public int minSubArrayLen(int s, int[] nums) {
int l= 0,r = -1; // nums[l...r]为我们的滑动窗口
int sum = 0;
int result = nums.length + 1;
while (l < nums.length){ // 窗口的左边界在数组范围内,则循环继续
if( r+1 <nums.length && sum < s){
r++;
sum += nums[r];
}else { // r已经到头 或者 sum >= s
sum -= nums[l];
l++;
}
if(sum >= s){
result = (r-l+1) < result ? (r-l+1) : result ;
}
}
if(result==nums.length+1){
return 0;
}
return result;
}
}
```

\ No newline at end of file
0219-Contains-Duplicate-II/Animation/Animation.gif
0 → 100644
View file @
f1048508
327 KB
0219-Contains-Duplicate-II/Article/0219-Contains-Duplicate-II.md
0 → 100644
View file @
f1048508
# LeetCode 第 219 号问题:存在重复元素 II
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 219 号问题:存在重复元素 II。题目难度为 Easy,目前通过率为 34.8% 。
### 题目描述
给定一个整数数组和一个整数
*k*
,判断数组中是否存在两个不同的索引
*i*
和
*j*
,使得
**nums [i] = nums [j]**
,并且
*i*
和
*j*
的差的绝对值最大为
*k*
。
**示例 1:**
```
输入: nums = [1,2,3,1], k = 3
输出: true
```
**示例 2:**
```
输入: nums = [1,0,1,1], k = 1
输出: true
```
**示例 3:**
```
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
```
### 题目解析
考虑用滑动窗口与查找表来解决。
*
设置查找表
`record`
,用来保存每次遍历时插入的元素,
`record `
的最大长度为
`k `
*
遍历数组
`nums`
,每次遍历的时候在
`record `
查找是否存在相同的元素,如果存在则返回
`true`
,遍历结束
*
如果此次遍历在
`record `
未查找到,则将该元素插入到
`record `
中,而后查看
`record `
的长度是否为
`k + 1`
*
如果此时
`record `
的长度是否为
`k + 1`
,则删减
`record`
的元素,该元素的值为
`nums[i - k]`
*
如果遍历完整个数组
`nums`
未查找到则返回
`false`
### 动画描述

### 代码实现
```
// 219. Contains Duplicate II
// https://leetcode.com/problems/contains-duplicate-ii/description/
// 时间复杂度: O(n)
// 空间复杂度: O(k)
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
if(nums.size() <= 1) return false;
if(k <= 0) return false;
unordered_set<int> record;
for(int i = 0 ; i < nums.size() ; i ++){
if(record.find(nums[i]) != record.end()){
return true;
}
record.insert(nums[i]);
// 保持record中最多有k个元素
// 因为在下一次循环中会添加一个新元素,使得总共考虑k+1个元素
if(record.size() == k + 1){
record.erase(nums[i - k]);
}
}
return false;
}
};
```

\ No newline at end of file
0231-Power-Of-Two/Article/0231-Power-Of-Two.md
0 → 100644
View file @
f1048508
# LeetCode 第 231 号问题:2 的幂
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 231 号问题:2 的幂。题目难度为 Easy,目前通过率为 45.6% 。
### 题目描述
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
**示例 1:**
```
输入: 1
输出: true
解释: 2^0 = 1
```
**示例 2:**
```
输入: 16
输出: true
解释: 2^4 = 16
```
**示例 3:**
```
输入: 218
输出: false
```
### 题目解析
首先,先来分析一下 2 的次方数的二进制写法:

仔细观察,可以看出 2 的次方数都只有一个 1 ,剩下的都是 0 。根据这个特点,只需要每次判断最低位是否为 1 ,然后向右移位,最后统计 1 的个数即可判断是否是 2 的次方数。
代码很简单:
```
c++
class
Solution
{
public:
bool
isPowerOfTwo
(
int
n
)
{
int
cnt
=
0
;
while
(
n
>
0
)
{
cnt
+=
(
n
&
1
);
n
>>=
1
;
}
return
cnt
==
1
;
}
};
```
该题还有一种巧妙的解法。再观察上面的表格,如果一个数是 2 的次方数的话,那么它的二进数必然是最高位为1,其它都为 0 ,那么如果此时我们减 1 的话,则最高位会降一位,其余为 0 的位现在都为变为 1,那么我们把两数相与,就会得到 0。
比如 2 的 3 次方为 8,二进制位 1000 ,那么
` 8 - 1 = 7`
,其中 7 的二进制位 0111。
### 图片描述

### 代码实现
利用这个性质,只需一行代码就可以搞定。
```
c++
class
Solution
{
public:
bool
isPowerOfTwo
(
int
n
)
{
return
(
n
>
0
)
&&
(
!
(
n
&
(
n
-
1
)));
}
};
```

0237-Delete-Node-in-a-Linked-List/Animation/Animation.gif
0 → 100644
View file @
f1048508
63 KB
0237-Delete-Node-in-a-Linked-List/Article/0237-Delete-Node-in-a-Linked-List.md
0 → 100644
View file @
f1048508
# LeetCode 第 237 号问题:删除链表中的节点
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 237 号问题:删除链表中的节点。题目难度为 Easy,目前通过率为 72.6% 。
### 题目描述
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 -- head = [4,5,1,9],它可以表示为:

**示例 1:**
```
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
```
**示例 2:**
```
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
```
**说明:**
-
链表至少包含两个节点。
-
链表中所有节点的值都是唯一的。
-
给定的节点为非末尾节点并且一定是链表中的一个有效节点。
-
不要从你的函数中返回任何结果。
### 题目解析
此题注意的点是没有给我们链表的起点,只给我们了一个要删的节点,与以往处理的情况稍许不同。
**这道题的处理方法是先把当前节点的值用下一个节点的值覆盖,然后我们删除下一个节点即可**
### 动画描述

### 代码实现
```
class Solution {
public:
void deleteNode(ListNode* node) {
if (node == NULL) return;
if (node->next == NULL) {
delete node;
node = NULL;
return;
}
node->val = node->next->val;
ListNode *delNode = node->next;
node->next = delNode->next;
delete delNode;
}
};
```

0239-Sliding-Window-Maximum/Animation/Animation.gif
0 → 100644
View file @
f1048508
1.92 MB
0239-Sliding-Window-Maximum/Article/0239-Sliding-Window-Maximum.md
0 → 100644
View file @
f1048508
# LeetCode 第 239 号问题:滑动窗口最大值
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
题目来源于 LeetCode 上第 239 号问题:滑动窗口最大值。题目难度为 Hard,目前通过率为 40.5% 。
### 题目描述
给定一个数组
*nums*
,有一个大小为
*k*
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口
*k*
内的数字。滑动窗口每次只向右移动一位。
返回滑动窗口最大值。
**示例:**
```
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
```
**注意:**
你可以假设
*k*
总是有效的,1 ≤ k ≤ 输入数组的大小,且输入数组不为空。
**进阶:**
你能在线性时间复杂度内解决此题吗?
### 题目解析
利用一个
**双端队列**
,在队列中存储元素在数组中的位置, 并且维持队列的严格递减,,也就说维持队首元素是
**最大的 **
,当遍历到一个新元素时, 如果队列里有比当前元素小的,就将其移除队列,以保证队列的递减。当队列元素位置之差大于 k,就将队首元素移除。
### 补充:什么是双端队列(Dqueue)
Deque 的含义是 “double ended queue”,即双端队列,它具有队列和栈的性质的数据结构。顾名思义,它是一种前端与后端都支持插入和删除操作的队列。
Deque 继承自 Queue(队列),它的直接实现有 ArrayDeque、LinkedList 等。
### 动画描述

### 代码实现
```
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//有点坑,题目里都说了数组不为空,且 k > 0。但是看了一下,测试用例里面还是有nums = [], k = 0,所以只好加上这个判断
if (nums == null || nums.length < k || k == 0) return new int[0];
int[] res = new int[nums.length - k + 1];
//双端队列
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < nums.length; i++) {
//在尾部添加元素,并保证左边元素都比尾部大
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.addLast(i);
//在头部移除元素
if (deque.getFirst() == i - k) {
deque.removeFirst();
}
//输出结果
if (i >= k - 1) {
res[i - k + 1] = nums[deque.getFirst()];
}
}
return res;
}
}
```

\ No newline at end of file
0268-Missing-Number/Animation/Animation.gif
0 → 100644
View file @
f1048508
372 KB
0268-Missing-Number/Article/0268-Missing-Number.md
0 → 100644
View file @
f1048508
# LeetCode 第 268 号问题:缺失数字
> 本文首发于公众号「图解面试算法」,是 [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客:https://www.algomooc.com
今天分享一道很简单的算法题。
题目来源于 LeetCode 上第 268 号问题:缺失数字。题目难度为 Easy,目前通过率为 50.2% 。
## 题目描述
给定一个包含
`0, 1, 2, ..., n`
中
*n*
个数的序列,找出 0 ..
*n*
中没有出现在序列中的那个数。
**说明:**
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

## 题目解析
这道题目有三种解法。
### 解法一:异或法
和之前那道
**只出现一次的数字**
很类似:
> 只出现一次的数字: 给定一个**非空**整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
如果我们补充一个完整的数组和原数组进行组合,那所求解的问题就变成了
**只出现一次的数字**
。
将少了一个数的数组与 0 到 n 之间完整的那个数组进行异或处理,因为相同的数字异或会变为了 0 ,那么全部数字异或后,剩下的就是少了的那个数字。

#### 代码实现1
```
java
class
Solution
{
public
int
missingNumber
(
int
[]
nums
)
{
int
res
=
0
;
int
i
=
0
;
//注意数组越界情况
for
(;
i
<
nums
.
length
;
i
++){
// i 表示完整数组中的数字,与原数组中的数字 nums[i] 进行异或,再与保存的结果异或
res
=
res
^
i
^
nums
[
i
];
}
//最后需要与循环中无法使用到的那个最大的数异或
return
res
^
i
;
}
}
```
#### 代码实现2
```
java
class
Solution
{
public
int
missingNumber
(
int
[]
nums
)
{
int
res
=
nums
.
length
;
for
(
int
i
=
0
;
i
<
nums
.
length
;
++
i
){
res
^=
nums
[
i
];
res
^=
i
;
}
return
res
;
}
}
```
### 解法二:求和法
-
求出 0 到 n 之间所有的数字之和
-
遍历数组计算出原始数组中数字的累积和
-
两和相减,差值就是丢失的那个数字

```
java
//小吴之前担心会数据溢出,不过估计这题考察的不是这个,所以测试用例没写这种吧,还是能 AC 的
class
Solution
{
public
int
missingNumber
(
int
[]
nums
)
{
int
n
=
nums
.
length
;
int
sum
=
(
n
+
0
)*(
n
+
1
)/
2
;
for
(
int
i
=
0
;
i
<
n
;
i
++){
sum
-=
nums
[
i
];
}
return
sum
;
}
}
```
### 解法三:二分法
将数组进行排序后,利用二分查找的方法来找到缺少的数字,注意搜索的范围为 0 到 n 。
-
首先对数组进行排序
-
用元素值和下标值之间做对比,如果元素值大于下标值,则说明缺失的数字在左边,此时将 right 赋为 mid ,反之则将 left 赋为 mid + 1 。
> 注:由于一开始进行了排序操作,因此使用二分法的性能是不如上面两种方法。
```
java
public
class
Solution
{
public
int
missingNumber
(
int
[]
nums
)
{
Arrays
.
sort
(
nums
);
int
left
=
0
;
int
right
=
nums
.
length
;
while
(
left
<
right
){
int
mid
=
(
left
+
right
)
/
2
;
if
(
nums
[
mid
]
>
mid
){
right
=
mid
;
}
else
{
left
=
mid
+
1
;
}
}
return
left
;
}
}
```

\ No newline at end of file
Prev
1
2
3
4
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment