Skip to content

Commit

Permalink
整理文件
Browse files Browse the repository at this point in the history
  • Loading branch information
MisterBooo committed Apr 17, 2020
1 parent c3aa5c5 commit f104850
Show file tree
Hide file tree
Showing 65 changed files with 3,185 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# LeetCode 131 号问题分割回文串

> 本文首发于公众号图解面试算法」, [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客https://www.algomooc.com

题目来源于 LeetCode 上第 131 号问题分割回文串题目难度为 Medium目前通过率为 45.8% 。

### 题目描述

给定一个字符串 *s*, *s* 分割成一些子串使每个子串都是回文串

返回 *s* 所有可能的分割方案

**示例:**

```yaml
输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
```

###

### 题目解析

首先对于一个字符串的分割肯定需要将所有分割情况都遍历完毕才能判断是不是回文数不能因为 **abba** 是回文串就认为它的所有子串都是回文的

既然需要将所有的分割方法都找出来那么肯定需要用到DFS深度优先搜索或者BFS广度优先搜索)。

在分割的过程中对于每一个字符串而言都可以分为两部分左边一个回文串加右边一个子串比如 "abc" 可分为 "a" + "bc"然后对"bc"分割仍然是同样的方法分为"b"+"c"

在处理的时候去优先寻找更短的回文串然后回溯找稍微长一些的回文串分割方法不断回溯分割直到找到所有的分割方法

举个🌰:分割"aac"

1. 分割为 a + ac
2. 分割为 a + a + c分割后得到一组结果再回溯到 a + ac
3. a + ac ac 不是回文串继续回溯回溯到 aac
4. 分割为稍长的回文串分割为 aa + c 分割完成得到一组结果再回溯到 aac
5. aac 不是回文串搜索结束



### 动画描述

![](../Animation/Animation.gif)

### 代码实现

```java
class Solution {
List<List<String>> res = new ArrayList<>();

public List<List<String>> partition(String s) {
if(s==null||s.length()==0)
return res;
dfs(s,new ArrayList<String>(),0);
return res;
}

public void dfs(String s,List<String> remain,int left){
if(left==s.length()){ //判断终止条件
res.add(new ArrayList<String>(remain)); //添加到结果中
return;
}
for(int right=left;right<s.length();right++){ //从left开始,依次判断left->right是不是回文串
if(isPalindroom(s,left,right)){ //判断是否是回文串
remain.add(s.substring(left,right+1)); //添加到当前回文串到list中
dfs(s,remain,right+1); //从right+1开始继续递归,寻找回文串
remain.remove(remain.size()-1); //回溯,从而寻找更长的回文串
}
}
}
/**
* 判断是否是回文串
*/
public boolean isPalindroom(String s,int left,int right){
while(left<right&&s.charAt(left)==s.charAt(right)){
left++;
right--;
}
return left>=right;
}
}
```

![](../../Pictures/qrcode.jpg)
Binary file added 0136-Single-Number/Animation/136.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 86 additions & 0 deletions 0136-Single-Number/Article/0136-Single-Number.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# LeetCode 136 号问题只出现一次的数字

> 本文首发于公众号图解面试算法」, [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客https://www.algomooc.com

题目来源于 LeetCode 上第 136 号问题只出现一次的数字题目难度为 Easy目前通过率为 66.8% 。

### 题目描述

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

**说明:**

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

**示例 1:**

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

**示例 2:**

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

### 题目解析

根据题目描述由于加上了时间复杂度必须是 O(n) ,并且空间复杂度为 O(1) 的条件因此不能用排序方法也不能使用 map 数据结构

程序员小吴想了一下午没想出来答案是使用 **位操作Bit Operation** 来解此题

将所有元素做异或运算即a[1] ⊕ a[2] ⊕ a[3] ⊕ …⊕ a[n],所得的结果就是那个只出现一次的数字时间复杂度为O(n)。

### 异或

异或运算AB的真值表如下

| A | B | ⊕ |
| :--- | :--: | ---: |
| F | F | F |
| F | T | T |
| T | F | T |
| T | T | F |

### 动画演示

![](../Animation/136.gif)

### 进阶版

有一个 n 个元素的数组除了两个数只出现一次外其余元素都出现两次让你找出这两个只出现一次的数分别是几要求时间复杂度为 O(n) 且再开辟的内存空间固定( n 无关)。

#### 示例 :

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

### 题目再解析

根据前面找一个不同数的思路算法在这里把所有元素都异或那么得到的结果就是那两个只出现一次的元素异或的结果

然后因为这两个只出现一次的元素一定是不相同的所以这两个元素的二进制形式肯定至少有某一位是不同的即一个为 0另一个为 1现在需要找到这一位

根据异或的性质 `任何一个数字异或它自己都等于 0 `,得到这个数字二进制形式中任意一个为 1 的位都是我们要找的那一位

再然后以这一位是 1 还是 0 为标准将数组的 n 个元素分成两部分

- 将这一位为 0 的所有元素做异或得出的数就是只出现一次的数中的一个
- 将这一位为 1 的所有元素做异或得出的数就是只出现一次的数中的另一个

这样就解出题目忽略寻找不同位的过程总共遍历数组两次时间复杂度为O(n)。

### 动画再演示

![](https://blog-1257126549.cos.ap-guangzhou.myqcloud.com/blog/5uz1n.gif)





![](../../Pictures/qrcode.jpg)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# LeetCode 138 号问题复制带随机指针的链表

> 本文首发于公众号图解面试算法」, [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客https://www.algomooc.com

题目来源于 LeetCode 上第 138 号问题复制带随机指针的链表题目难度为 Medium目前通过率为 40.5% 。

### 题目描述

给定一个链表每个节点包含一个额外增加的随机指针该指针可以指向链表中的任何节点或空节点

要求返回这个链表的**深拷贝**。

**示例:**

```
输入
{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}

解释
节点 1 的值是 1它的下一个指针和随机指针都指向节点 2
节点 2 的值是 2它的下一个指针指向 null随机指针指向它自己
```

### 题目解析

1. 在原链表的每个节点后面拷贝出一个新的节点

2. 依次给新的节点的随机指针赋值而且这个赋值非常容易 cur->next->random = cur->random->next

3. 断开链表可得到深度拷贝后的新链表

之所以说这个方法比较巧妙是因为相较于一般的解法如使用 Hash map来处理上面这个解法 **不需要占用额外的空间**。

### 动画描述

![](../Animation/Animation.gif)

### 代码实现

我发现带指针的题目使用 C++ 版本更容易描述所以下面的代码实现是 C++ 版本

```c++
class Solution {
public:
RandomListNode *copyRandomList(RandomListNode *head) {
if (!head) return NULL;
RandomListNode *cur = head;
while (cur) {
RandomListNode *node = new RandomListNode(cur->label);
node->next = cur->next;
cur->next = node;
cur = node->next;
}
cur = head;
while (cur) {
if (cur->random) {
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
cur = head;
RandomListNode *res = head->next;
while (cur) {
RandomListNode *tmp = cur->next;
cur->next = tmp->next;
if(tmp->next) tmp->next = tmp->next->next;
cur = cur->next;
}
return res;
}
};
```

![](../../Pictures/qrcode.jpg)

68 changes: 68 additions & 0 deletions 0139-Word-Break/Article/0139-Word-Break.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# LeetCode 139 号问题单词拆分

> 本文首发于公众号图解面试算法」, [图解 LeetCode ](<https://github.com/MisterBooo/LeetCodeAnimation>) 系列文章之一。
>
> 同步博客https://www.algomooc.com

题目来源于 LeetCode 上第 139 号问题单词拆分

### 题目描述

给定一个**非空**字符串 *s* 和一个包含**非空**单词列表的字典 *wordDict*,判定 *s* 是否可以被空格拆分为一个或多个在字典中出现的单词

**说明:**

- 拆分时可以重复使用字典中的单词
- 你可以假设字典中没有重复的单词



### 题目解析

**分割回文串** 有些类似都是拆分但是如果此题采取 深度优先搜索 的方法来解决的话答案是超时的不信的同学可以试一下~

为什么会超时呢

因为使用 深度优先搜索 会重复的计算了有些位的可拆分情况这种情况的优化肯定是需要 动态规划 来处理的

如果不知道动态规划的可以看一下小吴之前的万字长文比较详细的介绍了动态规划的概念

在这里只需要去定义一个数组 boolean[] memo其中第 i memo[i] 表示待拆分字符串从第 0 位到第 i-1 位是否可以被成功地拆分

然后分别计算每一位是否可以被成功地拆分



### 动画描述

暂无~

### 代码实现



```java
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
int n = s.length();
int max_length=0;
for(String temp:wordDict){
max_length = temp.length() > max_length ? temp.length() : max_length;
}
// memo[i] 表示 s 中以 i - 1 结尾的字符串是否可被 wordDict 拆分
boolean[] memo = new boolean[n + 1];
memo[0] = true;
for (int i = 1; i <= n; i++) {
for (int j = i-1; j >= 0 && max_length >= i - j; j--) {
if (memo[j] && wordDict.contains(s.substring(j, i))) {
memo[i] = true;
break;
}
}
}
return memo[n];
}
}
```

![](../../Pictures/qrcode.jpg)
Binary file added 0141-Linked-List-Cycle/Animation/Animation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit f104850

Please sign in to comment.