Compare commits
3 Commits
a503e97f11
...
2c4afffbd3
Author | SHA1 | Date |
---|---|---|
算法基地 | 2c4afffbd3 | |
goodyong | 7dd5ce1f3d | |
goodyong | 4e661354d4 |
|
@ -68,6 +68,8 @@
|
|||
|
||||
#### 题目代码
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -101,10 +103,38 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
haylen = len(haystack)
|
||||
needlen = len(needle)
|
||||
# 特殊情况
|
||||
if haylen < needlen:
|
||||
return -1
|
||||
if needlen == 0:
|
||||
return 0
|
||||
# 主串
|
||||
for i in range(0, haylen - needlen + 1):
|
||||
# 模式串
|
||||
j = 0
|
||||
while j < needlen:
|
||||
if haystack[i + j] != needle[j]:
|
||||
break
|
||||
j += 1
|
||||
# 匹配成功
|
||||
if j == needlen:
|
||||
return i
|
||||
return -1
|
||||
```
|
||||
|
||||
|
||||
我们看一下BF算法的另一种算法(显示回退),其实原理一样,就是对代码进行了一下修改,只要是看完咱们的动图,这个也能够一下就能看懂,大家可以结合下面代码中的注释和动图进行理解。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -132,3 +162,29 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
# i代表主串指针,j模式串
|
||||
i = 0
|
||||
j = 0
|
||||
# 主串长度和模式串长度
|
||||
halen = len(haystack)
|
||||
nelen = len(needle)
|
||||
# 循环条件,这里只有 i 增长
|
||||
while i < halen and j < nelen:
|
||||
# 相同时,则移动 j 指针
|
||||
if haystack[i] == needle[j]:
|
||||
j += 1
|
||||
else:
|
||||
# 不匹配时,将 j 重新只想模式串的头部,将 i 本次匹配的开始位置的下一字符
|
||||
i -= j
|
||||
j = 0
|
||||
i += 1
|
||||
# 查询成功时返回索引,查询失败时返回 -1
|
||||
renum = i - nelen if j == nelen else -1
|
||||
return renum
|
||||
```
|
||||
|
|
|
@ -122,6 +122,8 @@ BM 算法是从右往左进行比较,发现坏字符的时候此时 cac 已
|
|||
|
||||
这破图画起来是真费劲啊。下面我们来看一下算法代码,代码有点长,我都标上了注释也在网站上 AC 了,如果各位感兴趣可以看一下,不感兴趣理解坏字符和好后缀规则即可。可以直接跳到 KMP 部分
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -215,6 +217,89 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
haylen = len(haystack)
|
||||
needlen = len(needle)
|
||||
return self.bm(haystack, haylen, needle, needlen)
|
||||
|
||||
# 用来求坏字符情况下移动位数
|
||||
def badChar(self, b: str, m: int, bc: List[int]):
|
||||
# 初始化
|
||||
for i in range(0, 256):
|
||||
bc[i] = -1
|
||||
# m 代表模式串的长度,如果有两个 a,则后面那个会覆盖前面那个
|
||||
for i in range(0, m,):
|
||||
ascii = ord(b[i])
|
||||
bc[ascii] = i# 下标
|
||||
|
||||
# 用来求好后缀条件下的移动位数
|
||||
def goodSuffix(self, b: str, m: int, suffix: List[int], prefix: List[bool]):
|
||||
# 初始化
|
||||
for i in range(0, m):
|
||||
suffix[i] = -1
|
||||
prefix[i] = False
|
||||
for i in range(0, m - 1):
|
||||
j = i
|
||||
k = 0
|
||||
while j >= 0 and b[j] == b[m - 1 - k]:
|
||||
j -= 1
|
||||
k += 1
|
||||
suffix[k] = j + 1
|
||||
if j == -1:
|
||||
prefix[k] = True
|
||||
|
||||
def bm(self, a: str, n: int, b: str, m: int)->int:
|
||||
bc = [0] * 256# 创建一个数组用来保存最右边字符的下标
|
||||
self.badChar(b, m, bc)
|
||||
# 用来保存各种长度好后缀的最右位置的数组
|
||||
suffix_index = [0] * m
|
||||
# 判断是否是头部,如果是头部则True
|
||||
ispre = [False] * m
|
||||
self.goodSuffix(b, m, suffix_index, ispre)
|
||||
i = 0# 第一个匹配字符
|
||||
# 注意结束条件
|
||||
while i <= n - m:
|
||||
# 从后往前匹配,匹配失败,找到坏字符
|
||||
j = m - 1
|
||||
while j >= 0:
|
||||
if a[i + j] != b[j]:
|
||||
break
|
||||
j -= 1
|
||||
# 模式串遍历完毕,匹配成功
|
||||
if j < 0:
|
||||
return i
|
||||
# 下面为匹配失败时,如何处理
|
||||
# 求出坏字符规则下移动的位数,就是我们坏字符下标减最右边的下标
|
||||
x = j - bc[ord(a[i + j])]
|
||||
y = 0
|
||||
# 好后缀情况,求出好后缀情况下的移动位数,如果不含有好后缀的话,则按照坏字符来
|
||||
if y < m - 1 and m - 1 - j > 0:
|
||||
y = self.move(j, m, suffix_index, ispre)
|
||||
# 移动
|
||||
i += max(x, y)
|
||||
return -1
|
||||
|
||||
# j代表坏字符的下标
|
||||
def move(j: int, m: int, suffix_index: List[int], ispre: List[bool])->int:
|
||||
# 好后缀长度
|
||||
k = m - 1 - j
|
||||
# 如果含有长度为 k 的好后缀,返回移动位数
|
||||
if suffix_index[k] != -1:
|
||||
return j - suffix_index[k] + 1
|
||||
# 找头部为好后缀子串的最大长度,从长度最大的子串开始
|
||||
for r in range(j + 2, m):
|
||||
# //如果是头部
|
||||
if ispre[m - r] == True:
|
||||
return r
|
||||
# 如果没有发现好后缀匹配的串,或者头部为好后缀子串,则移动到 m 位,也就是匹配串的长度
|
||||
return m
|
||||
```
|
||||
|
||||
我们来理解一下我们代码中用到的两个数组,因为两个规则的移动位数,只与模式串有关,与主串无关,所以我们可以提前求出每种情况的移动情况,保存到数组中。
|
||||
|
||||
![头缀函数](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/头缀函数.145da63ig3s0.png)
|
||||
|
|
|
@ -113,7 +113,7 @@ class Solution {
|
|||
k = next[k];
|
||||
}
|
||||
// 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
|
||||
//然后 k - 1 又和 i 相同,最长前后缀加1,即可
|
||||
//然后 k + 1 又和 i 相同,最长前后缀加1,即可
|
||||
if (needle[k+1] == needle[i]) {
|
||||
++k;
|
||||
}
|
||||
|
@ -125,5 +125,63 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
# 两种特殊情况
|
||||
if len(needle) == 0:
|
||||
return 0
|
||||
if len(haystack) == 0:
|
||||
return -1
|
||||
# 长度
|
||||
halen = len(haystack)
|
||||
nelen = len(needle)
|
||||
# 返回下标
|
||||
return self.kmp(haystack, halen, needle, nelen)
|
||||
|
||||
def kmp(self, hasyarr: str, halen: int, nearr: str, nelen: int)->int:
|
||||
# 获取next 数组
|
||||
next = self.next(nearr, nelen)
|
||||
j = 0
|
||||
for i in range(0, halen):
|
||||
# 发现不匹配的字符,然后根据 next 数组移动指针,移动到最大公共前后缀的,
|
||||
# 前缀的后一位,和咱们移动模式串的含义相同
|
||||
while j > 0 and hasyarr[i] != nearr[j]:
|
||||
j = next[j - 1] + 1
|
||||
# 超出长度时,可以直接返回不存在
|
||||
if nelen - j + i > halen:
|
||||
return -1
|
||||
# 如果相同就将指针同时后移一下,比较下个字符
|
||||
if hasyarr[i] == nearr[j]:
|
||||
j += 1
|
||||
# 遍历完整个模式串,返回模式串的起点下标
|
||||
if j == nelen:
|
||||
return i - nelen + 1
|
||||
return -1
|
||||
|
||||
# 这一块比较难懂,不想看的同学可以忽略,了解大致含义即可,或者自己调试一下,看看运行情况
|
||||
# 我会每一步都写上注释
|
||||
def next(self, needle: str, len:int)->List[int]:
|
||||
# 定义 next 数组
|
||||
next = [0] * len
|
||||
# 初始化
|
||||
next[0] = -1
|
||||
k = -1
|
||||
for i in range(1, len):
|
||||
# 我们此时知道了 [0,i-1]的最长前后缀,但是k+1的指向的值和i不相同时,我们则需要回溯
|
||||
# 因为 next[k]就时用来记录子串的最长公共前后缀的尾坐标(即长度)
|
||||
# 就要找 k+1前一个元素在next数组里的值,即next[k+1]
|
||||
while k != -1 and needle[k + 1] != needle[i]:
|
||||
k = next[k]
|
||||
# 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
|
||||
# 然后 k + 1 又和 i 相同,最长前后缀加1,即可
|
||||
if needle[k + 1] == needle[i]:
|
||||
k += 1
|
||||
next[i] = k
|
||||
return next
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -80,6 +80,8 @@
|
|||
|
||||
我们来看一下这段代码
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -102,6 +104,25 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
leng = len(nums)
|
||||
for i in range(0, leng):
|
||||
for j in range(i + 1, leng):
|
||||
if nums[i] > nums[j]:
|
||||
self.swap(nums, i, j)
|
||||
return nums
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
我们来思考一下上面的代码,每次让关键字 nums[i] 和 nums[j] 进行比较如果 nums[i] > nums[j] 时则进行交换,这样 nums[0] 在经过一次循环后一定为最小值。那么这段代码是冒泡排序吗?
|
||||
|
||||
显然不是,我们冒泡排序的思想是两两比较**相邻记录**的关键字,注意里面有相邻记录,所以这段代码不是我们的冒泡排序,下面我们用动图来模拟一下冒泡排序的执行过程,看完之后一定可以写出正宗的冒泡排序。
|
||||
|
@ -143,6 +164,8 @@ class Solution {
|
|||
|
||||
我们来对冒泡排序进行改进
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -172,6 +195,32 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
leng = len(nums)
|
||||
# 标志位
|
||||
flag = True
|
||||
for i in range(0, leng):
|
||||
if not flag:
|
||||
break
|
||||
flag = False
|
||||
for j in range(0, leng - i - 1):
|
||||
if nums[j] > nums[j + 1]:
|
||||
self.swap(nums, j, j + 1)
|
||||
# 发生交换,则变为true,下次继续判断
|
||||
flag = True
|
||||
return nums
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
这样我们就避免掉了已经有序的情况下无意义的循环判断。
|
||||
|
||||
**冒泡排序时间复杂度分析**
|
||||
|
|
|
@ -144,6 +144,8 @@ BC < CB 整理得 B / (10 ^ b - 1) < C / (10 ^ c - 1);
|
|||
|
||||
好啦,我们证明我们定义的规则有效下面我们直接看代码吧。继续使用我们的三向切分来解决
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public String minNumber(int[] nums) {
|
||||
|
@ -166,23 +168,23 @@ class Solution {
|
|||
return;
|
||||
}
|
||||
int low = left;
|
||||
int hight = right;
|
||||
int high = right;
|
||||
int i = low+1;
|
||||
String pivot = arr[low];
|
||||
|
||||
while (i <= hight) {
|
||||
while (i <= high) {
|
||||
//比较大小
|
||||
if ((pivot+arr[i]).compareTo(arr[i]+pivot) > 0 ) {
|
||||
swap(arr,i++,low++);
|
||||
} else if ((pivot+arr[i]).compareTo(arr[i]+pivot) < 0) {
|
||||
swap(arr,i,hight--);
|
||||
swap(arr,i,high--);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
quickSort(arr,left,low-1);
|
||||
quickSort(arr,hight+1,right);
|
||||
quickSort(arr,high+1,right);
|
||||
|
||||
}
|
||||
public void swap(String[] arr, int i, int j) {
|
||||
|
@ -193,6 +195,53 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def minNumber(self, nums: List[int])->str:
|
||||
|
||||
arr = [''] * len(nums)
|
||||
# 解决大数问题,将数字转换为字符串
|
||||
for i in range(0, len(nums)):
|
||||
arr[i] = str(nums[i])
|
||||
|
||||
self.quickSort(arr, 0, len(arr) - 1)
|
||||
s = ''
|
||||
for x in arr:
|
||||
s += x
|
||||
return s
|
||||
|
||||
def quickSort(self, arr: List[str], left: int, right: int):
|
||||
if left >= right:
|
||||
return
|
||||
low = left
|
||||
high = right
|
||||
i = low + 1
|
||||
pivot = arr[low]
|
||||
|
||||
while i <= high:
|
||||
# 比较大小
|
||||
if int(pivot + arr[i]) > int(arr[i] + pivot):
|
||||
self.swap(arr, i, low)
|
||||
i += 1
|
||||
low += 1
|
||||
elif int(pivot + arr[i]) < int(arr[i] + pivot):
|
||||
self.swap(arr, i, high)
|
||||
high -= 1
|
||||
else:
|
||||
i += 1
|
||||
|
||||
self.quickSort(arr, left, low - 1)
|
||||
self.quickSort(arr, high + 1, right)
|
||||
|
||||
def swap(self, arr: List[str], i: int, j: int):
|
||||
temp = arr[i]
|
||||
arr[i] = arr[j]
|
||||
arr[j] = temp
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
> 如果阅读时,发现错误,或者动画不可以显示的问题可以添加我微信好友 **[tan45du_one](https://raw.githubusercontent.com/tan45du/tan45du.github.io/master/个人微信.15egrcgqd94w.jpg)** ,备注 github + 题目 + 问题 向我反馈
|
||||
>
|
||||
> 感谢支持,该仓库会一直维护,希望对各位有一丢丢帮助。
|
||||
>
|
||||
>刷题网站
|
||||
> 另外希望手机阅读的同学可以来我的 <u>[**公众号:袁厨的算法小屋**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u> 两个平台同步,想要和题友一起刷题,互相监督的同学,可以在我的小屋点击<u>[**刷题小队**](https://raw.githubusercontent.com/tan45du/test/master/微信图片_20210320152235.2pthdebvh1c0.png)</u>进入。
|
||||
|
||||
说堆排序之前,我们先简单了解一些什么是堆?堆这种数据结构应用场景非常多,所以我们需要熟练掌握呀!
|
||||
|
@ -99,8 +99,10 @@
|
|||
|
||||
我们一起来看一下上浮操作代码。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
public void swim (int index) {
|
||||
public void swim (int[] nums, int index) {
|
||||
while (index > 1 && nums[index/2] > nums[index]) {
|
||||
swap(index/2,index);//交换
|
||||
index = index/2;
|
||||
|
@ -108,6 +110,15 @@ public void swim (int index) {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
def swim(nums: int, index: int):
|
||||
while index > 1 and nums[int(index/2)] > nums[index]:
|
||||
swap(int(index/2), index)# 交换
|
||||
index = int(index/2)
|
||||
```
|
||||
|
||||
既然利用上浮操作建堆已经搞懂啦,那么我们再来了解一下,利用下沉操作建堆吧,也很容易理解。
|
||||
|
||||
给我们一个无序数组(不满足堆的要求),见下图
|
||||
|
@ -145,6 +156,8 @@ public void swim (int index) {
|
|||
|
||||
好啦我们一起看哈下沉操作的代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
public void sink (int[] nums, int index,int len) {
|
||||
while (true) {
|
||||
|
@ -165,6 +178,24 @@ public void sink (int[] nums, int index,int len) {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
def sink(nums: list, index: int, len: int):
|
||||
while True:
|
||||
# 获取子节点
|
||||
j = 2 * index
|
||||
if j < len-1 and nums[j] < nums[j+1]:
|
||||
j += 1
|
||||
# 交换操作,父节点下沉,与最大的孩子节点交换
|
||||
if j < len and nums[index] < nums[j]:
|
||||
swap(nums, index, j)
|
||||
else:
|
||||
break
|
||||
# 继续下沉
|
||||
index = j
|
||||
```
|
||||
|
||||
好啦,两种建堆方式我们都已经了解啦,那么我们如何进行排序呢?
|
||||
|
||||
了解排序之前我们先来,看一下如何删除堆顶元素,我们需要保证的是,删除堆顶元素后,其他元素仍能满足堆的要求,我们思考一下如何实现呢?见下图
|
||||
|
@ -187,6 +218,8 @@ public void sink (int[] nums, int index,int len) {
|
|||
|
||||
好啦,下面我们一起看代码吧
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -238,6 +271,44 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
def sortArray(nums: list)->list:
|
||||
leng = len(nums)
|
||||
a = [0] + nums
|
||||
# 下沉建堆
|
||||
for i in range(int(leng / 2), 0, -1):
|
||||
sink(a, i, leng)
|
||||
|
||||
k = leng
|
||||
# 排序
|
||||
while k > 1:
|
||||
swap(a, 1, k)
|
||||
k -= 1
|
||||
sink(a, 1, k)
|
||||
|
||||
for i in range(1, leng + 1):
|
||||
nums[i - 1] = a[i]
|
||||
return nums
|
||||
|
||||
def swap(nums: list, i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
|
||||
def sink(nums: list, k: int, end: int):
|
||||
while 2 * k <= end:
|
||||
j = 2 * k
|
||||
if j + 1 <= end and nums[j + 1] > nums[j]:
|
||||
j += 1
|
||||
if nums[j] > nums[k]:
|
||||
swap(nums, j, k)
|
||||
else:
|
||||
break
|
||||
k = j
|
||||
```
|
||||
|
||||
好啦,堆排序我们就到这里啦,是不是搞定啦,总的来说堆排序比其他排序算法稍微难理解一些,重点就是建堆,而且应用比较广泛,大家记得打卡呀。
|
||||
|
||||
好啦,我们再来分析一下堆排序的时间复杂度、空间复杂度以及稳定性。
|
||||
|
|
|
@ -70,6 +70,8 @@
|
|||
|
||||
#### 题目代码
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -103,10 +105,37 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
haylen = len(haystack)
|
||||
needlen = len(needle)
|
||||
# 特殊情况
|
||||
if haylen < needlen:
|
||||
return -1
|
||||
if needlen == 0:
|
||||
return 0
|
||||
# 主串
|
||||
for i in range(0, haylen - needlen + 1):
|
||||
# 模式串
|
||||
j = 0
|
||||
while j < needlen:
|
||||
if haystack[i + j] != needle[j]:
|
||||
break
|
||||
j += 1
|
||||
# 匹配成功
|
||||
if j == needlen:
|
||||
return i
|
||||
return -1
|
||||
```
|
||||
|
||||
我们看一下BF算法的另一种算法(显示回退),其实原理一样,就是对代码进行了一下修改,只要是看完咱们的动图,这个也能够一下就能看懂,大家可以结合下面代码中的注释和动图进行理解。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -134,6 +163,32 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
# i代表主串指针,j模式串
|
||||
i = 0
|
||||
j = 0
|
||||
# 主串长度和模式串长度
|
||||
halen = len(haystack)
|
||||
nelen = len(needle)
|
||||
# 循环条件,这里只有 i 增长
|
||||
while i < halen and j < nelen:
|
||||
# 相同时,则移动 j 指针
|
||||
if haystack[i] == needle[j]:
|
||||
j += 1
|
||||
else:
|
||||
# 不匹配时,将 j 重新只想模式串的头部,将 i 本次匹配的开始位置的下一字符
|
||||
i -= j
|
||||
j = 0
|
||||
i += 1
|
||||
# 查询成功时返回索引,查询失败时返回 -1
|
||||
renum = i - nelen if j == nelen else -1
|
||||
return renum
|
||||
```
|
||||
|
||||
|
||||
## BM算法(Boyer-Moore)
|
||||
|
@ -262,6 +317,8 @@ BM 算法是从右往左进行比较,发现坏字符的时候此时 cac 已
|
|||
|
||||
这破图画起来是真费劲啊。下面我们来看一下算法代码,代码有点长,我都标上了注释也在网站上 AC 了,如果各位感兴趣可以看一下,不感兴趣理解坏字符和好后缀规则即可。可以直接跳到 KMP 部分
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -355,6 +412,89 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
haylen = len(haystack)
|
||||
needlen = len(needle)
|
||||
return self.bm(haystack, haylen, needle, needlen)
|
||||
|
||||
# 用来求坏字符情况下移动位数
|
||||
def badChar(self, b: str, m: int, bc: List[int]):
|
||||
# 初始化
|
||||
for i in range(0, 256):
|
||||
bc[i] = -1
|
||||
# m 代表模式串的长度,如果有两个 a,则后面那个会覆盖前面那个
|
||||
for i in range(0, m,):
|
||||
ascii = ord(b[i])
|
||||
bc[ascii] = i# 下标
|
||||
|
||||
# 用来求好后缀条件下的移动位数
|
||||
def goodSuffix(self, b: str, m: int, suffix: List[int], prefix: List[bool]):
|
||||
# 初始化
|
||||
for i in range(0, m):
|
||||
suffix[i] = -1
|
||||
prefix[i] = False
|
||||
for i in range(0, m - 1):
|
||||
j = i
|
||||
k = 0
|
||||
while j >= 0 and b[j] == b[m - 1 - k]:
|
||||
j -= 1
|
||||
k += 1
|
||||
suffix[k] = j + 1
|
||||
if j == -1:
|
||||
prefix[k] = True
|
||||
|
||||
def bm(self, a: str, n: int, b: str, m: int)->int:
|
||||
bc = [0] * 256# 创建一个数组用来保存最右边字符的下标
|
||||
self.badChar(b, m, bc)
|
||||
# 用来保存各种长度好后缀的最右位置的数组
|
||||
suffix_index = [0] * m
|
||||
# 判断是否是头部,如果是头部则True
|
||||
ispre = [False] * m
|
||||
self.goodSuffix(b, m, suffix_index, ispre)
|
||||
i = 0# 第一个匹配字符
|
||||
# 注意结束条件
|
||||
while i <= n - m:
|
||||
# 从后往前匹配,匹配失败,找到坏字符
|
||||
j = m - 1
|
||||
while j >= 0:
|
||||
if a[i + j] != b[j]:
|
||||
break
|
||||
j -= 1
|
||||
# 模式串遍历完毕,匹配成功
|
||||
if j < 0:
|
||||
return i
|
||||
# 下面为匹配失败时,如何处理
|
||||
# 求出坏字符规则下移动的位数,就是我们坏字符下标减最右边的下标
|
||||
x = j - bc[ord(a[i + j])]
|
||||
y = 0
|
||||
# 好后缀情况,求出好后缀情况下的移动位数,如果不含有好后缀的话,则按照坏字符来
|
||||
if y < m - 1 and m - 1 - j > 0:
|
||||
y = self.move(j, m, suffix_index, ispre)
|
||||
# 移动
|
||||
i += max(x, y)
|
||||
return -1
|
||||
|
||||
# j代表坏字符的下标
|
||||
def move(j: int, m: int, suffix_index: List[int], ispre: List[bool])->int:
|
||||
# 好后缀长度
|
||||
k = m - 1 - j
|
||||
# 如果含有长度为 k 的好后缀,返回移动位数
|
||||
if suffix_index[k] != -1:
|
||||
return j - suffix_index[k] + 1
|
||||
# 找头部为好后缀子串的最大长度,从长度最大的子串开始
|
||||
for r in range(j + 2, m):
|
||||
# //如果是头部
|
||||
if ispre[m - r] == True:
|
||||
return r
|
||||
# 如果没有发现好后缀匹配的串,或者头部为好后缀子串,则移动到 m 位,也就是匹配串的长度
|
||||
return m
|
||||
```
|
||||
|
||||
我们来理解一下我们代码中用到的两个数组,因为两个规则的移动位数,只与模式串有关,与主串无关,所以我们可以提前求出每种情况的移动情况,保存到数组中。
|
||||
|
||||
![头缀函数](https://cdn.jsdelivr.net/gh/tan45du/photobed@master/photo/头缀函数.145da63ig3s0.png)
|
||||
|
@ -413,6 +553,8 @@ next 数组存的咱们最长公共前后缀中,前缀的结尾字符下标。
|
|||
|
||||
**注:很多教科书的 next 数组表示方式不一致,理解即可**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int strStr(String haystack, String needle) {
|
||||
|
@ -486,5 +628,64 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def strStr(self, haystack: str, needle: str)->int:
|
||||
# 两种特殊情况
|
||||
if len(needle) == 0:
|
||||
return 0
|
||||
if len(haystack) == 0:
|
||||
return -1
|
||||
# 长度
|
||||
halen = len(haystack)
|
||||
nelen = len(needle)
|
||||
# 返回下标
|
||||
return self.kmp(haystack, halen, needle, nelen)
|
||||
|
||||
def kmp(self, hasyarr: str, halen: int, nearr: str, nelen: int)->int:
|
||||
# 获取next 数组
|
||||
next = self.next(nearr, nelen)
|
||||
j = 0
|
||||
for i in range(0, halen):
|
||||
# 发现不匹配的字符,然后根据 next 数组移动指针,移动到最大公共前后缀的,
|
||||
# 前缀的后一位,和咱们移动模式串的含义相同
|
||||
while j > 0 and hasyarr[i] != nearr[j]:
|
||||
j = next[j - 1] + 1
|
||||
# 超出长度时,可以直接返回不存在
|
||||
if nelen - j + i > halen:
|
||||
return -1
|
||||
# 如果相同就将指针同时后移一下,比较下个字符
|
||||
if hasyarr[i] == nearr[j]:
|
||||
j += 1
|
||||
# 遍历完整个模式串,返回模式串的起点下标
|
||||
if j == nelen:
|
||||
return i - nelen + 1
|
||||
return -1
|
||||
|
||||
# 这一块比较难懂,不想看的同学可以忽略,了解大致含义即可,或者自己调试一下,看看运行情况
|
||||
# 我会每一步都写上注释
|
||||
def next(self, needle: str, len:int)->List[int]:
|
||||
# 定义 next 数组
|
||||
next = [0] * len
|
||||
# 初始化
|
||||
next[0] = -1
|
||||
k = -1
|
||||
for i in range(1, len):
|
||||
# 我们此时知道了 [0,i-1]的最长前后缀,但是k+1的指向的值和i不相同时,我们则需要回溯
|
||||
# 因为 next[k]就时用来记录子串的最长公共前后缀的尾坐标(即长度)
|
||||
# 就要找 k+1前一个元素在next数组里的值,即next[k+1]
|
||||
while k != -1 and needle[k + 1] != needle[i]:
|
||||
k = next[k]
|
||||
# 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
|
||||
# 然后 k - 1 又和 i 相同,最长前后缀加1,即可
|
||||
if needle[k + 1] == needle[i]:
|
||||
k += 1
|
||||
next[i] = k
|
||||
return next
|
||||
```
|
||||
|
||||
这篇文章真的写了很久很久,觉得还不错的话,就麻烦您点个赞吧,大家也可以去我的公众号看我的所有文章,每个都有动图解析,公众号:[袁厨的算法小屋](https://cdn.jsdelivr.net/gh/tan45du/tan45du.github.io.photo@master/photo/qrcode_for_gh_1f36d2ef6df9_258.5lojyphpkso0.jpg)
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
**希尔排序代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -58,6 +60,34 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
increment = len(nums)
|
||||
# 注意看结束条件
|
||||
while increment > 1:
|
||||
# 这里可以自己设置
|
||||
increment = int(increment / 2)
|
||||
# 根据增量分组
|
||||
for i in range(0, increment):
|
||||
# 这块是不是有点面熟,回去看看咱们的插入排序
|
||||
for j in range(i + increment, len(nums), increment):
|
||||
temp = nums[j]
|
||||
k = j - increment
|
||||
while k >= 0:
|
||||
if temp < nums[k]:
|
||||
nums[k + increment] = nums[k]
|
||||
k -= increment
|
||||
continue
|
||||
break
|
||||
nums[k + increment] = temp
|
||||
return nums
|
||||
```
|
||||
|
||||
|
||||
我们刚才说,我们的增量可以自己设置的,我们上面的例子是用的希尔增量,下面我们看这个例子,看看使用希尔增量会出现什么问题。
|
||||
|
||||
![](https://cdn.jsdelivr.net/gh/tan45du/bedphoto2@master/20210122/微信截图_20210127212901.62c3o3ss6pg0.png)
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
|
||||
下面我们看代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -90,6 +92,48 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
self.mergeSort(nums, 0, len(nums) - 1)
|
||||
return nums
|
||||
|
||||
def mergeSort(self, arr: List[int], left: int, right: int):
|
||||
if left < right:
|
||||
mid = left + ((right - left) >> 1)
|
||||
self.mergeSort(arr, left, mid)
|
||||
self.mergeSort(arr, mid + 1, right)
|
||||
self.merge(arr, left, mid, right)
|
||||
|
||||
# 归并
|
||||
def merge(self, arr: List[int], left: int, mid: int, right: int):
|
||||
# 第一步,定义一个新的临时数组
|
||||
temparr = [0] * (right - left + 1)
|
||||
temp1 = left
|
||||
temp2 = mid + 1
|
||||
index = 0
|
||||
# 对应第二步,比较每个指针指向的值,小的存入大集合
|
||||
while temp1 <= mid and temp2 <= right:
|
||||
if arr[temp1] <= arr[temp2]:
|
||||
temparr[index] = arr[temp1]
|
||||
index += 1
|
||||
temp1 += 1
|
||||
else:
|
||||
temparr[index] = arr[temp2]
|
||||
index += 1
|
||||
temp2 += 1
|
||||
# 对应第三步,将某一集合的剩余元素存到大集合中
|
||||
if temp1 <= mid:
|
||||
temparr[index: index + mid - temp1 + 1] = arr[temp1: temp1 + mid - temp1 + 1]
|
||||
if temp2 <= right:
|
||||
temparr[index: index + right - temp2 + 1] = arr[temp2: temp2 + right - temp2 + 1]
|
||||
|
||||
# 将大集合的元素复制回原数组
|
||||
arr[left: left + right- left + 1] = temparr[0: right - left + 1]
|
||||
```
|
||||
**归并排序时间复杂度分析**
|
||||
|
||||
我们一趟归并,需要将两个小集合的长度放到大集合中,则需要将待排序序列中的所有记录扫描一遍所以时间复杂度为O(n)。归并排序把集合一层一层的折半分组,则由完全二叉树的深度可知,整个排序过程需要进行 logn(向上取整)次,则总的时间复杂度为 O(nlogn)。另外归并排序的执行效率与要排序的原始数组的有序程度无关,所以在最好,最坏,平均情况下时间复杂度均为 O(nlogn) 。虽然归并排序时间复杂度很稳定,但是他的应用范围却不如快速排序广泛,这是因为归并排序不是原地排序算法,空间复杂度不为 O(1),那么他的空间复杂度为多少呢?
|
||||
|
@ -134,6 +178,8 @@ class Solution {
|
|||
|
||||
注:递归法和迭代法的 merge函数代码一样。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray (int[] nums) {
|
||||
|
@ -162,7 +208,7 @@ class Solution {
|
|||
}
|
||||
public void merge (int[] arr,int left, int mid, int right) {
|
||||
//第一步,定义一个新的临时数组
|
||||
int[] temparr = new int[right -left + 1];
|
||||
int[] temparr = new int[right - left + 1];
|
||||
int temp1 = left, temp2 = mid + 1;
|
||||
int index = 0;
|
||||
//对应第二步,比较每个指针指向的值,小的存入大集合
|
||||
|
@ -182,3 +228,55 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
# 代表子集合大小,1,2,4,8,16.....
|
||||
k = 1
|
||||
leng = len(nums)
|
||||
while k < leng:
|
||||
self.mergePass(nums, k, leng)
|
||||
k *= 2
|
||||
print(nums)
|
||||
return nums
|
||||
|
||||
def mergePass(self, array: List[int], k: int, leng: int):
|
||||
i = 0
|
||||
while i < leng - 2 * k:
|
||||
# 归并
|
||||
self.merge(array, i, i + k - 1, i + 2 * k - 1)
|
||||
i += 2 * k
|
||||
|
||||
# 归并最后两个序列
|
||||
if i + k < leng:
|
||||
self.merge(array, i, i + k - 1, leng - 1)
|
||||
|
||||
# 归并
|
||||
def merge(self, arr: List[int], left: int, mid: int, right: int):
|
||||
# 第一步,定义一个新的临时数组
|
||||
temparr = [0] * (right - left + 1)
|
||||
temp1 = left
|
||||
temp2 = mid + 1
|
||||
index = 0
|
||||
# 对应第二步,比较每个指针指向的值,小的存入大集合
|
||||
while temp1 <= mid and temp2 <= right:
|
||||
if arr[temp1] <= arr[temp2]:
|
||||
temparr[index] = arr[temp1]
|
||||
index += 1
|
||||
temp1 += 1
|
||||
else:
|
||||
temparr[index] = arr[temp2]
|
||||
index += 1
|
||||
temp2 += 1
|
||||
# 对应第三步,将某一集合的剩余元素存到大集合中
|
||||
if temp1 <= mid:
|
||||
temparr[index: index + mid - temp1 + 1] = arr[temp1: temp1 + mid - temp1 + 1]
|
||||
if temp2 <= right:
|
||||
temparr[index: index + right - temp2 + 1] = arr[temp2: temp2 + right - temp2 + 1]
|
||||
|
||||
# 将大集合的元素复制回原数组
|
||||
arr[left: left + right- left + 1] = temparr[0: right - left + 1]
|
||||
```
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
下面我们先来介绍下挖坑填数的分区方法
|
||||
|
||||
基本思想是我们首先以序列的第一个元素为基准数,然后将该位置挖坑,下面判断 nums[hight] 是否大于基准数,如果大于则左移 hight 指针,直至找到一个小于基准数的元素,将其填入之前的坑中,则 hight 位置会出现一个新的坑,此时移动 low 指针,找到大于基准数的元素,填入新的坑中。不断迭代直至完成分区。
|
||||
基本思想是我们首先以序列的第一个元素为基准数,然后将该位置挖坑,下面判断 nums[high] 是否大于基准数,如果大于则左移 high 指针,直至找到一个小于基准数的元素,将其填入之前的坑中,则 high 位置会出现一个新的坑,此时移动 low 指针,找到大于基准数的元素,填入新的坑中。不断迭代直至完成分区。
|
||||
|
||||
大家直接看我们的视频模拟吧,一目了然。
|
||||
|
||||
|
@ -46,6 +46,8 @@
|
|||
|
||||
是不是很容易就理解啦,下面我们直接看代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -54,30 +56,30 @@ class Solution {
|
|||
return nums;
|
||||
|
||||
}
|
||||
public void quickSort (int[] nums, int low, int hight) {
|
||||
public void quickSort (int[] nums, int low, int high) {
|
||||
|
||||
if (low < hight) {
|
||||
int index = partition(nums,low,hight);
|
||||
if (low < high) {
|
||||
int index = partition(nums,low,high);
|
||||
quickSort(nums,low,index-1);
|
||||
quickSort(nums,index+1,hight);
|
||||
quickSort(nums,index+1,high);
|
||||
}
|
||||
|
||||
}
|
||||
public int partition (int[] nums, int low, int hight) {
|
||||
public int partition (int[] nums, int low, int high) {
|
||||
|
||||
int pivot = nums[low];
|
||||
while (low < hight) {
|
||||
while (low < high) {
|
||||
//移动hight指针
|
||||
while (low < hight && nums[hight] >= pivot) {
|
||||
hight--;
|
||||
while (low < high && nums[high] >= pivot) {
|
||||
high--;
|
||||
}
|
||||
//填坑
|
||||
if (low < hight) nums[low] = nums[hight];
|
||||
while (low < hight && nums[low] <= pivot) {
|
||||
if (low < high) nums[low] = nums[high];
|
||||
while (low < high && nums[low] <= pivot) {
|
||||
low++;
|
||||
}
|
||||
//填坑
|
||||
if (low < hight) nums[hight] = nums[low];
|
||||
if (low < high) nums[high] = nums[low];
|
||||
}
|
||||
//基准数放到合适的位置
|
||||
nums[low] = pivot;
|
||||
|
@ -107,26 +109,26 @@ class Solution {
|
|||
|
||||
}
|
||||
|
||||
public void quickSort (int[] nums, int low, int hight) {
|
||||
public void quickSort (int[] nums, int low, int high) {
|
||||
|
||||
if (low < hight) {
|
||||
int index = partition(nums,low,hight);
|
||||
if (low < high) {
|
||||
int index = partition(nums,low,high);
|
||||
quickSort(nums,low,index-1);
|
||||
quickSort(nums,index+1,hight);
|
||||
quickSort(nums,index+1,high);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int partition (int[] nums, int low, int hight) {
|
||||
public int partition (int[] nums, int low, int high) {
|
||||
|
||||
int pivot = nums[low];
|
||||
int start = low;
|
||||
|
||||
while (low < hight) {
|
||||
while (low < hight && nums[hight] >= pivot) hight--;
|
||||
while (low < hight && nums[low] <= pivot) low++;
|
||||
if (low >= hight) break;
|
||||
swap(nums, low, hight);
|
||||
while (low < high) {
|
||||
while (low < high && nums[high] >= pivot) high--;
|
||||
while (low < high && nums[low] <= pivot) low++;
|
||||
if (low >= high) break;
|
||||
swap(nums, low, high);
|
||||
}
|
||||
//基准值归位
|
||||
swap(nums,start,low);
|
||||
|
@ -175,29 +177,29 @@ class Solution {
|
|||
stack.push(0);
|
||||
while (!stack.isEmpty()) {
|
||||
int low = stack.pop();
|
||||
int hight = stack.pop();
|
||||
int high = stack.pop();
|
||||
|
||||
if (low < hight) {
|
||||
int index = partition(nums, low, hight);
|
||||
if (low < high) {
|
||||
int index = partition(nums, low, high);
|
||||
stack.push(index - 1);
|
||||
stack.push(low);
|
||||
stack.push(hight);
|
||||
stack.push(high);
|
||||
stack.push(index + 1);
|
||||
}
|
||||
}
|
||||
return nums;
|
||||
}
|
||||
|
||||
public int partition (int[] nums, int low, int hight) {
|
||||
public int partition (int[] nums, int low, int high) {
|
||||
|
||||
int pivot = nums[low];
|
||||
int start = low;
|
||||
while (low < hight) {
|
||||
while (low < high) {
|
||||
|
||||
while (low < hight && nums[hight] >= pivot) hight--;
|
||||
while (low < hight && nums[low] <= pivot) low++;
|
||||
if (low >= hight) break;
|
||||
swap(nums, low, hight);
|
||||
while (low < high && nums[high] >= pivot) high--;
|
||||
while (low < high && nums[low] <= pivot) low++;
|
||||
if (low >= high) break;
|
||||
swap(nums, low, high);
|
||||
}
|
||||
swap(nums,start,low);
|
||||
return low;
|
||||
|
@ -226,13 +228,13 @@ class Solution {
|
|||
所以我们可以加上这几行代码实现三数取中法。
|
||||
|
||||
```java
|
||||
int mid = low + ((hight-low) >> 1);
|
||||
if (nums[low] > nums[hight]) swap(nums,low,hight);
|
||||
if (nums[mid] > nums[hight]) swap(nums,mid,hight);
|
||||
int mid = low + ((high-low) >> 1);
|
||||
if (nums[low] > nums[high]) swap(nums,low,high);
|
||||
if (nums[mid] > nums[high]) swap(nums,mid,high);
|
||||
if (nums[mid] > nums[low]) swap(nums,mid,low);
|
||||
```
|
||||
|
||||
其含义就是让我们将中间元素放到 nums[low] 位置做为基准值,最大值放到 nums[hight],最小值放到 nums[mid],即 [4,2,3] 经过上面代码处理后,则变成了 [3,2,4].此时我们选取 3 做为基准值,这样也就避免掉了选取最大或最小值做为基准值的情况。
|
||||
其含义就是让我们将中间元素放到 nums[low] 位置做为基准值,最大值放到 nums[high],最小值放到 nums[mid],即 [4,2,3] 经过上面代码处理后,则变成了 [3,2,4].此时我们选取 3 做为基准值,这样也就避免掉了选取最大或最小值做为基准值的情况。
|
||||
|
||||
**三数取中法**
|
||||
|
||||
|
@ -242,28 +244,28 @@ class Solution {
|
|||
quickSort(nums,0,nums.length-1);
|
||||
return nums;
|
||||
}
|
||||
public void quickSort (int[] nums, int low, int hight) {
|
||||
if (low < hight) {
|
||||
int index = partition(nums,low,hight);
|
||||
public void quickSort (int[] nums, int low, int high) {
|
||||
if (low < high) {
|
||||
int index = partition(nums,low,high);
|
||||
quickSort(nums,low,index-1);
|
||||
quickSort(nums,index+1,hight);
|
||||
quickSort(nums,index+1,high);
|
||||
}
|
||||
}
|
||||
|
||||
public int partition (int[] nums, int low, int hight) {
|
||||
public int partition (int[] nums, int low, int high) {
|
||||
//三数取中,大家也可以使用其他方法
|
||||
int mid = low + ((hight-low) >> 1);
|
||||
if (nums[low] > nums[hight]) swap(nums,low,hight);
|
||||
if (nums[mid] > nums[hight]) swap(nums,mid,hight);
|
||||
int mid = low + ((high-low) >> 1);
|
||||
if (nums[low] > nums[high]) swap(nums,low,high);
|
||||
if (nums[mid] > nums[high]) swap(nums,mid,high);
|
||||
if (nums[mid] > nums[low]) swap(nums,mid,low);
|
||||
//下面和之前一样,仅仅是多了上面几行代码
|
||||
int pivot = nums[low];
|
||||
int start = low;
|
||||
while (low < hight) {
|
||||
while (low < hight && nums[hight] >= pivot) hight--;
|
||||
while (low < hight && nums[low] <= pivot) low++;
|
||||
if (low >= hight) break;
|
||||
swap(nums, low, hight);
|
||||
while (low < high) {
|
||||
while (low < high && nums[high] >= pivot) high--;
|
||||
while (low < high && nums[low] <= pivot) low++;
|
||||
if (low >= high) break;
|
||||
swap(nums, low, high);
|
||||
}
|
||||
swap(nums,start,low);
|
||||
return low;
|
||||
|
@ -291,38 +293,38 @@ class Solution {
|
|||
return nums;
|
||||
}
|
||||
|
||||
public void quickSort (int[] nums, int low, int hight) {
|
||||
public void quickSort (int[] nums, int low, int high) {
|
||||
|
||||
if (hight - low <= INSERTION_SORT_MAX_LENGTH) {
|
||||
insertSort(nums,low,hight);
|
||||
if (high - low <= INSERTION_SORT_MAX_LENGTH) {
|
||||
insertSort(nums,low,high);
|
||||
return;
|
||||
}
|
||||
int index = partition(nums,low,hight);
|
||||
int index = partition(nums,low,high);
|
||||
quickSort(nums,low,index-1);
|
||||
quickSort(nums,index+1,hight);
|
||||
quickSort(nums,index+1,high);
|
||||
}
|
||||
|
||||
public int partition (int[] nums, int low, int hight) {
|
||||
public int partition (int[] nums, int low, int high) {
|
||||
//三数取中,大家也可以使用其他方法
|
||||
int mid = low + ((hight-low) >> 1);
|
||||
if (nums[low] > nums[hight]) swap(nums,low,hight);
|
||||
if (nums[mid] > nums[hight]) swap(nums,mid,hight);
|
||||
int mid = low + ((high-low) >> 1);
|
||||
if (nums[low] > nums[high]) swap(nums,low,high);
|
||||
if (nums[mid] > nums[high]) swap(nums,mid,high);
|
||||
if (nums[mid] > nums[low]) swap(nums,mid,low);
|
||||
int pivot = nums[low];
|
||||
int start = low;
|
||||
while (low < hight) {
|
||||
while (low < hight && nums[hight] >= pivot) hight--;
|
||||
while (low < hight && nums[low] <= pivot) low++;
|
||||
if (low >= hight) break;
|
||||
swap(nums, low, hight);
|
||||
while (low < high) {
|
||||
while (low < high && nums[high] >= pivot) high--;
|
||||
while (low < high && nums[low] <= pivot) low++;
|
||||
if (low >= high) break;
|
||||
swap(nums, low, high);
|
||||
}
|
||||
swap(nums,start,low);
|
||||
return low;
|
||||
}
|
||||
|
||||
public void insertSort (int[] nums, int low, int hight) {
|
||||
public void insertSort (int[] nums, int low, int high) {
|
||||
|
||||
for (int i = low+1; i <= hight; ++i) {
|
||||
for (int i = low+1; i <= high; ++i) {
|
||||
int temp = nums[i];
|
||||
int j;
|
||||
for (j = i-1; j >= 0; --j) {
|
||||
|
@ -366,6 +368,8 @@ class Solution {
|
|||
|
||||
**三数取中+三向切分+插入排序**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
private static final int INSERTION_SORT_MAX_LENGTH = 7;
|
||||
|
@ -374,19 +378,19 @@ class Solution {
|
|||
return nums;
|
||||
|
||||
}
|
||||
public void quickSort(int nums[], int low, int hight) {
|
||||
public void quickSort(int nums[], int low, int high) {
|
||||
//插入排序
|
||||
if (hight - low <= INSERTION_SORT_MAX_LENGTH) {
|
||||
insertSort(nums,low,hight);
|
||||
if (high - low <= INSERTION_SORT_MAX_LENGTH) {
|
||||
insertSort(nums,low,high);
|
||||
return;
|
||||
}
|
||||
//三数取中
|
||||
int mid = low + ((hight-low) >> 1);
|
||||
if (nums[low] > nums[hight]) swap(nums,low,hight);
|
||||
if (nums[mid] > nums[hight]) swap(nums,mid,hight);
|
||||
int mid = low + ((high-low) >> 1);
|
||||
if (nums[low] > nums[high]) swap(nums,low,high);
|
||||
if (nums[mid] > nums[high]) swap(nums,mid,high);
|
||||
if (nums[mid] > nums[low]) swap(nums,mid,low);
|
||||
//三向切分
|
||||
int left = low, i = low + 1, right = hight;
|
||||
int left = low, i = low + 1, right = high;
|
||||
int pvoit = nums[low];
|
||||
while (i <= right) {
|
||||
if (pvoit < nums[i]) {
|
||||
|
@ -401,11 +405,11 @@ class Solution {
|
|||
}
|
||||
}
|
||||
quickSort(nums,low,left-1);
|
||||
quickSort(nums,right+1,hight);
|
||||
quickSort(nums,right+1,high);
|
||||
}
|
||||
public void insertSort (int[] nums, int low, int hight) {
|
||||
public void insertSort (int[] nums, int low, int high) {
|
||||
|
||||
for (int i = low+1; i <= hight; ++i) {
|
||||
for (int i = low+1; i <= high; ++i) {
|
||||
int temp = nums[i];
|
||||
int j;
|
||||
for (j = i-1; j >= 0; --j) {
|
||||
|
@ -426,5 +430,65 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
INSERTION_SORT_MAX_LENGTH = 7
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
self.quickSort(nums, 0, len(nums) - 1)
|
||||
return nums
|
||||
|
||||
def quickSort(self, nums: List[int], low: int, high: int):
|
||||
# 插入排序
|
||||
if high - low <= self.INSERTION_SORT_MAX_LENGTH:
|
||||
self.insertSort(nums, low, high)
|
||||
return
|
||||
# 三数取中
|
||||
mid = low + ((high - low) >> 1)
|
||||
if nums[low] > nums[high]:
|
||||
self.swap(nums, low, high)
|
||||
if nums[mid] > nums[high]:
|
||||
self.swap(nums, mid, high)
|
||||
if nums[mid] > nums[low]:
|
||||
self. swap(nums, mid, low)
|
||||
# 三向切分
|
||||
left = low
|
||||
i = low + 1
|
||||
right = high
|
||||
pivot = nums[low]
|
||||
while i <= right:
|
||||
if pivot < nums[i]:
|
||||
self.swap(nums, i, right)
|
||||
right -= 1
|
||||
elif pivot == nums[i]:
|
||||
i += 1
|
||||
else:
|
||||
self.swap(nums, left, i)
|
||||
left += 1
|
||||
i += 1
|
||||
self.quickSort(nums, low, left - 1)
|
||||
self.quickSort(nums, right + 1, high)
|
||||
|
||||
def insertSort(self, nums: List[int], low: int, high: int):
|
||||
for i in range(low + 1, high + 1):
|
||||
temp = nums[i]
|
||||
j = i - 1
|
||||
while j >= 0:
|
||||
if temp < nums[j]:
|
||||
nums[j + 1] = nums[j]
|
||||
j -= 1
|
||||
continue
|
||||
break
|
||||
nums[j + 1] = temp
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
|
||||
好啦,一些常用的优化方法都整理出来啦,还有一些其他的优化算法九数取中,优化递归操作等就不在这里进行描述啦,感兴趣的可以自己看一下。好啦,这期的文章就到这里啦,我们下期见,拜了个拜。
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
**直接插入排序代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -48,6 +50,31 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
# 注意 i 的初始值为 1 ,也就是第二个元素开始
|
||||
for i in range(1, len(nums)):
|
||||
# 待排序的值
|
||||
temp = nums[i]
|
||||
# 需要注意
|
||||
j = i - 1
|
||||
while j >= 0:
|
||||
# 找到合适位置
|
||||
if temp < nums[j]:
|
||||
nums[j + 1] = nums[j]
|
||||
j -= 1
|
||||
continue
|
||||
# 跳出循环
|
||||
break
|
||||
# 插入到合适位置,这也就是我们没有在循环内定义变量的原因
|
||||
nums[j + 1] = temp
|
||||
return nums
|
||||
```
|
||||
|
||||
**直接插入排序时间复杂度分析**
|
||||
|
||||
最好情况时,也就是有序的时候,我们不需要移动元素,每次只需要比较一次即可找到插入位置,那么最好情况时的时间复杂度为O(n)。
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
**简单选择排序代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -46,6 +48,29 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self, nums: List[int])->List[int]:
|
||||
leng = len(nums)
|
||||
min = 0
|
||||
for i in range(0, leng):
|
||||
min = i
|
||||
# 遍历到最小值
|
||||
for j in range(i + 1, leng):
|
||||
if nums[min] > nums[j]:
|
||||
min = j
|
||||
if min != i:
|
||||
self.swap(nums, i, min)
|
||||
return nums
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
|
||||
**简单选择排序时间复杂度分析**
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
|
||||
是不是很容易理解啊,那我们直接看代码吧,仅仅是在归并排序的基础上加了几行代码。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
private int count;
|
||||
|
@ -95,3 +97,56 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
count = 0
|
||||
def reversePairs(self, nums: List[int])->int:
|
||||
self.count = 0
|
||||
self.merge(nums, 0, len(nums) - 1)
|
||||
return self.count
|
||||
|
||||
def merge(self, nums: List[int], left: int, right: int):
|
||||
if left < right:
|
||||
mid = left + ((right - left) >> 1)
|
||||
self.merge(nums, left, mid)
|
||||
self.merge(nums, mid + 1, right)
|
||||
self.mergeSort(nums, left, mid, right)
|
||||
|
||||
def mergeSort(self, nums: List[int], left: int, mid: int, right: int):
|
||||
|
||||
temparr = [0] * (right - left + 1)
|
||||
temp1 = left
|
||||
temp2 = mid + 1
|
||||
index = 0
|
||||
while temp1 <= mid and temp2 <= right:
|
||||
# 这里需要防止溢出
|
||||
if nums[temp1] > 2 * nums[temp2]:
|
||||
self.count += mid - temp1 + 1
|
||||
temp2 += 1
|
||||
else:
|
||||
temp1 += 1
|
||||
|
||||
# 记得归位,我们还要继续使用
|
||||
temp1 = left
|
||||
temp2 = mid + 1
|
||||
# 归并排序
|
||||
while temp1 <= mid and temp2 <= right:
|
||||
if nums[temp1] <= nums[temp2]:
|
||||
temparr[index] = nums[temp1]
|
||||
index += 1
|
||||
temp1 += 1
|
||||
else:
|
||||
temparr[index] = nums[temp2]
|
||||
index += 1
|
||||
temp2 += 1
|
||||
# 照旧
|
||||
if temp1 <= mid:
|
||||
temparr[index: index + mid - temp1 + 1] = nums[temp1: temp1 + mid - temp1 + 1]
|
||||
if temp2 <= right:
|
||||
temparr[index: index + right - temp2 + 1] = nums[temp2: temp2 + right - temp2 + 1]
|
||||
nums[left: left + right- left + 1] = temparr[0: right - left + 1]
|
||||
```
|
||||
|
||||
|
|
|
@ -132,6 +132,33 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortColors(self, nums: List[int]):
|
||||
leng = len(nums)
|
||||
left = 0
|
||||
# 这里和三向切分不完全一致
|
||||
i = left
|
||||
right = leng - 1
|
||||
while i <= right:
|
||||
if nums[i] == 2:
|
||||
self.swap(nums, i, right)
|
||||
right -= 1
|
||||
elif nums[i] == 0:
|
||||
self.swap(nums, i, left)
|
||||
i += 1
|
||||
left += 1
|
||||
else:
|
||||
i += 1
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
另外我们看这段代码,有什么问题呢?那就是我们即使完全符合时,仍会交换元素,这样会大大降低我们的效率。
|
||||
|
||||
例如:[0,0,0,1,1,1,2,2,2]
|
||||
|
@ -216,5 +243,29 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortColors(self, nums: List[int]):
|
||||
left = 0
|
||||
leng = len(nums)
|
||||
right = leng - 1
|
||||
for i in range(0, right + 1):
|
||||
if nums[i] == 0:
|
||||
self.swap(nums, i, left)
|
||||
left += 1
|
||||
if nums[i] == 2:
|
||||
swap(nums, i, right)
|
||||
right -= 1
|
||||
# 如果不等于 1 则需要继续判断,所以不移动 i 指针,i--
|
||||
if nums[i] != 1:
|
||||
i -= 1
|
||||
|
||||
def swap(nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
好啦,这个问题到这就结束啦,是不是很简单啊,我们明天见!
|
||||
|
||||
|
|
|
@ -128,6 +128,8 @@ temp[8] = 5。然后再将 presum[5] 减 1 。
|
|||
|
||||
一样可以,哦了,到这里我们就搞定了计数排序,下面我们来看一哈代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] sortArray(int[] nums) {
|
||||
|
@ -169,6 +171,44 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortArray(self,nums: List[int])->List[int]:
|
||||
leng = len(nums)
|
||||
if leng < 1:
|
||||
return nums
|
||||
# 求出最大最小值
|
||||
max = nums[0]
|
||||
min = nums[0]
|
||||
for x in nums:
|
||||
if max < x:
|
||||
max = x
|
||||
if min > x:
|
||||
min = x
|
||||
# 设置 presum 数组长度,然后求出我们的前缀和数组,
|
||||
# 这里我们可以把求次数数组和前缀和数组用一个数组处理
|
||||
presum = [0] * (max - min + 1)
|
||||
for x in nums:
|
||||
presum[x - min] += 1
|
||||
for i in range(1, len(presum)):
|
||||
presum[i] = presum[i - 1] + presum[i]
|
||||
# 临时数组
|
||||
temp = [0] * leng
|
||||
# 遍历数组,开始排序,注意偏移量
|
||||
for i in range(leng - 1, -1, -1):
|
||||
# 查找 presum 字典,然后将其放到临时数组,注意偏移度
|
||||
index = presum[nums[i] - min] - 1
|
||||
temp[index] = nums[i]
|
||||
# 相应位置减一
|
||||
presum[nums[i] - min] -= 1
|
||||
# copy回原数组
|
||||
nums = temp
|
||||
return nums
|
||||
```
|
||||
|
||||
好啦,这个排序算法我们已经搞定了,下面我们来扒一扒它。
|
||||
|
||||
**计数排序时间复杂度分析**
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
**题目代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
//全局变量
|
||||
|
@ -80,7 +82,51 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
count = 0
|
||||
def reversePairs(self, nums: List[int])->int:
|
||||
self.count = 0
|
||||
self.mergeSort(nums, 0, len(nums) - 1)
|
||||
return self.count
|
||||
|
||||
def mergeSort(self, arr: List[int], left: int, right: int):
|
||||
if left < right:
|
||||
mid = left + ((right - left) >> 1)
|
||||
self.mergeSort(arr, left, mid)
|
||||
self.mergeSort(arr, mid + 1, right)
|
||||
self.merge(arr, left, mid, right)
|
||||
|
||||
# 归并
|
||||
def merge(self, arr: List[int], left: int, mid: int, right: int):
|
||||
# 第一步,定义一个新的临时数组
|
||||
temparr = [0] * (right - left + 1)
|
||||
temp1 = left
|
||||
temp2 = mid + 1
|
||||
index = 0
|
||||
# 对应第二步,比较每个指针指向的值,小的存入大集合
|
||||
while temp1 <= mid and temp2 <= right:
|
||||
if arr[temp1] <= arr[temp2]:
|
||||
temparr[index] = arr[temp1]
|
||||
index += 1
|
||||
temp1 += 1
|
||||
else:
|
||||
self.count += (mid - temp1 + 1)
|
||||
temparr[index] = arr[temp2]
|
||||
index += 1
|
||||
temp2 += 1
|
||||
# 对应第三步,将某一集合的剩余元素存到大集合中
|
||||
if temp1 <= mid:
|
||||
temparr[index: index + mid - temp1 + 1] = arr[temp1: temp1 + mid - temp1 + 1]
|
||||
if temp2 <= right:
|
||||
temparr[index: index + right - temp2 + 1] = arr[temp2: temp2 + right - temp2 + 1]
|
||||
|
||||
# 将大集合的元素复制回原数组
|
||||
arr[left: left + right- left + 1] = temparr[0: right - left + 1]
|
||||
```
|
||||
|
||||
好啦,这个题目我们就解决啦,哦对,大家也可以顺手去解决下这个题目。leetcode 912 排序数组,这个题目大家可以用来练手,因为有些排序算法是面试高频考点,所以大家可以防止遗忘,多用这个题目进行练习,防止手生。下面则是我写文章时代码的提交情况,冒泡排序怎么优化都会超时,其他排序算法倒是都可以通过。
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
|
||||
其实,我们只要把握两个重点即可,我们的 maxdeque 维护的是一个单调递减的双端队列,头部为当前窗口的最大值, mindeque 维护的是一个单调递增的双端队列,头部为窗口的最小值,即可。好啦我们一起看代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int longestSubarray(int[] nums, int limit) {
|
||||
|
@ -76,3 +78,36 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
import collections
|
||||
class Solution:
|
||||
def longestSubarray(self, nums: List[int], limit: int)->int:
|
||||
maxdeque = collections.deque()
|
||||
mindeque = collections.deque()
|
||||
leng = len(nums)
|
||||
right = 0
|
||||
left = 0
|
||||
maxwin = 0
|
||||
while right < leng:
|
||||
while len(maxdeque) != 0 and maxdeque[-1] < nums[right]:
|
||||
maxdeque.pop()
|
||||
while len(mindeque) != 0 and mindeque[-1] > nums[right]:
|
||||
mindeque.pop()
|
||||
|
||||
maxdeque.append(nums[right])
|
||||
mindeque.append(nums[right])
|
||||
while (maxdeque[0] - mindeque[0]) > limit:
|
||||
if maxdeque[0] == nums[left]:
|
||||
maxdeque.popleft()
|
||||
if mindeque[0] == nums[left]:
|
||||
mindeque.popleft()
|
||||
left += 1
|
||||
# 保留最大窗口
|
||||
maxwin = max(maxwin, right - left + 1)
|
||||
right += 1
|
||||
return maxwin
|
||||
```
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
|
||||
**题目代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] twoSum(int[] nums, int target) {
|
||||
|
@ -55,6 +57,25 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def twoSum(nums: List[int], target: int)->List[int]:
|
||||
if len(nums) < 2:
|
||||
return [0]
|
||||
rearr = [0] * 2
|
||||
# 查询元素
|
||||
for i in range(0, len(nums)):
|
||||
for j in range(i + 1, len(nums)):
|
||||
# 发现符合条件情况
|
||||
if nums[i] + nums[j] == target:
|
||||
rearr[0] = i
|
||||
rearr[1] = j
|
||||
return rearr
|
||||
```
|
||||
|
||||
**哈希表**
|
||||
|
||||
**解析**
|
||||
|
@ -122,5 +143,19 @@ const twoSum = function (nums, target) {
|
|||
};
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def twoSum(self, nums: List[int], target: int)->List[int]:
|
||||
m = {}
|
||||
for i in range(0, len(nums)):
|
||||
# 如果存在则返回
|
||||
if (target - nums[i]) in m.keys():
|
||||
return [m[target - nums[i]], i]
|
||||
# 不存在则存入
|
||||
m[nums[i]] = i
|
||||
return [0]
|
||||
```
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
这个题目和我们上面那个数组中的重复数字几乎相同,只不过是增加了一个判断相隔是否小于K位的条件,我们先用 HashMap 来做一哈,和刚才思路一致,我们直接看代码就能整懂
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public boolean containsNearbyDuplicate(int[] nums, int k) {
|
||||
|
@ -41,9 +43,9 @@ class Solution {
|
|||
for (int i = 0; i < nums.length; i++) {
|
||||
// 如果含有
|
||||
if (map.containsKey(nums[i])) {
|
||||
//判断是否小于K,如果小于则直接返回
|
||||
//判断是否小于K,如果小于等于则直接返回
|
||||
int abs = Math.abs(i - map.get(nums[i]));
|
||||
if (abs <= k) return true;//小于则返回
|
||||
if (abs <= k) return true;//小于等于则返回
|
||||
}
|
||||
//更新索引,此时有两种情况,不存在,或者存在时,将后出现的索引保存
|
||||
map.put(nums[i],i);
|
||||
|
@ -53,6 +55,29 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def containsNearbyDuplicate(self, nums: List[int], k: int)->bool:
|
||||
# 特殊情况
|
||||
if len(nums) == 0:
|
||||
return False
|
||||
# 字典
|
||||
m = {}
|
||||
for i in range(0, len(nums)):
|
||||
# 如果含有
|
||||
if nums[i] in m.keys():
|
||||
# 判断是否小于K,如果小于等于则直接返回
|
||||
a = abs(i - m[nums[i]])
|
||||
if a <= k:
|
||||
return True# 小于等于则返回
|
||||
# 更新索引,此时有两种情况,不存在,或者存在时,将后出现的索引保存
|
||||
m[nums[i]] = i
|
||||
return False
|
||||
```
|
||||
|
||||
**HashSet**
|
||||
|
||||
**解析**
|
||||
|
@ -65,6 +90,8 @@ class Solution {
|
|||
|
||||
**题目代码**
|
||||
|
||||
Java Code
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public boolean containsNearbyDuplicate(int[] nums, int k) {
|
||||
|
@ -91,3 +118,25 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def containsNearbyDuplicate(self, nums: List[int], k: int)->bool:
|
||||
# 特殊情况
|
||||
if len(nums) == 0:
|
||||
return False
|
||||
# 集合
|
||||
s = set()
|
||||
for i in range(0, len(nums)):
|
||||
# 如果含有,返回True
|
||||
if nums[i] in s:
|
||||
return True
|
||||
# 添加新元素
|
||||
s.add(nums[i])
|
||||
# 维护窗口长度
|
||||
if len(s) > k:
|
||||
s.remove(nums[i - k])
|
||||
return False
|
||||
```
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
|
||||
**题目代码**
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int removeElement(int[] nums, int val) {
|
||||
|
@ -75,6 +77,29 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def removeElement(self, nums: List[int], val: int)->int:
|
||||
# 获取数组长度
|
||||
leng = len(nums)
|
||||
if 0 == leng:
|
||||
return 0
|
||||
i = 0
|
||||
while i < leng:
|
||||
# 发现符合条件的情况
|
||||
if nums[i] == val:
|
||||
# 前移一位
|
||||
for j in range(i, leng - 1):
|
||||
nums[j] = nums[j + 1]
|
||||
i -= 1
|
||||
leng -= 1
|
||||
i += 1
|
||||
return i
|
||||
```
|
||||
|
||||
**双指针**
|
||||
|
||||
快慢指针的做法比较有趣,只需要一个 for 循环即可解决,时间复杂度为 O(n) ,总体思路就是有两个指针,前面一个后面一个,前面的用于搜索需要删除的值,当遇到需要删除的值时,前指针直接跳过,后面的指针不动,当遇到正常值时,两个指针都进行移动,并修改慢指针的值。最后只需输出慢指针的索引即可。
|
||||
|
@ -100,7 +125,7 @@ class Solution {
|
|||
if (nums[j] == val) {
|
||||
continue;
|
||||
}
|
||||
// 不等于目标值时,则赋值给num[i],i++
|
||||
// 不等于目标值时,则赋值给nums[i],i++
|
||||
nums[i++] = nums[j];
|
||||
}
|
||||
return i;
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
|
||||
![缺失的第一个正数](https://cdn.jsdelivr.net/gh/tan45du/github.io.phonto2@master/myphoto/缺失的第一个正数.1it1cow5aa8w.gif)
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int firstMissingPositive(int[] nums) {
|
||||
|
@ -65,6 +67,27 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def firstMissingPositive(self, nums: List[int])->int:
|
||||
if len(nums) == 0:
|
||||
return 1
|
||||
# 因为是返回第一个正整数,不包括 0,所以需要长度加1,细节1
|
||||
res = [0] * (len(nums) + 1)
|
||||
# 将数组元素添加到辅助数组中
|
||||
for x in nums:
|
||||
if x > 0 and x < len(res):
|
||||
res[x] = x
|
||||
# 遍历查找,发现不一样时直接返回
|
||||
for i in range(1, len(res)):
|
||||
if res[i] != i:
|
||||
return i
|
||||
# 缺少最后一个,例如 1,2,3此时缺少 4 ,细节2
|
||||
return len(res)
|
||||
```
|
||||
|
||||
|
||||
我们通过上面的例子了解这个解题思想,我们有没有办法不使用辅助数组完成呢?我们可以使用原地置换,直接在 nums 数组内,将值换到对应的索引处,与上个方法思路一致,只不过没有使用辅助数组,理解起来也稍微难理解一些。
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
下面我们直接看代码吧
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int findMaxConsecutiveOnes(int[] nums) {
|
||||
|
@ -58,6 +60,29 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def findMaxConsecutiveOnes(self, nums: List[int])->int:
|
||||
leng = len(nums)
|
||||
left = 0
|
||||
right = 0
|
||||
maxcount = 0
|
||||
while right < leng:
|
||||
if nums[right] == 1:
|
||||
right += 1
|
||||
continue
|
||||
# 保存最大值
|
||||
maxcount = max(maxcount, right - left)
|
||||
# 跳过 0 的情况
|
||||
while right < leng and nums[right] == 0:
|
||||
right += 1
|
||||
# 同一起点继续遍历
|
||||
left = right
|
||||
return max(maxcount, right - left)
|
||||
```
|
||||
|
||||
|
||||
刚才的效率虽然相对高一些,但是代码不够优美,欢迎各位改进,下面我们说一下另外一种情况,一个特别容易理解的方法。
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
题目代码:
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public List<Integer> spiralOrder(int[][] matrix) {
|
||||
|
@ -83,5 +85,40 @@ class Solution {
|
|||
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def spiralOrder(self, matrix: List[List[int]])->List[int]:
|
||||
arr = []
|
||||
left = 0
|
||||
right = len(matrix[0]) - 1
|
||||
top = 0
|
||||
down = len(matrix) - 1
|
||||
while True:
|
||||
for i in range(left, right + 1):
|
||||
arr.append(matrix[top][i])
|
||||
top += 1
|
||||
if top > down:
|
||||
break
|
||||
for i in range(top, down + 1):
|
||||
arr.append(matrix[i][right])
|
||||
right -= 1
|
||||
if left > right:
|
||||
break
|
||||
for i in range(right, left - 1, -1):
|
||||
arr.append(matrix[down][i])
|
||||
down -= 1
|
||||
if top > down:
|
||||
break
|
||||
for i in range(down, top - 1, -1):
|
||||
arr.append(matrix[i][left])
|
||||
left += 1
|
||||
if left > right:
|
||||
break
|
||||
return arr
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
这个题目的题意很容易理解,就是让我们返回和为 k 的子数组的个数,所以我们直接利用双重循环解决该题,这个是很容易想到的。我们直接看代码吧。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int subarraySum(int[] nums, int k) {
|
||||
|
@ -41,6 +43,8 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3版本的代码会超时
|
||||
|
||||
下面我们我们使用前缀和的方法来解决这个题目,那么我们先来了解一下前缀和是什么东西。其实这个思想我们很早就接触过了。见下图
|
||||
|
||||
![](https://cdn.jsdelivr.net/gh/tan45du/github.io.phonto2@master/myphoto/微信截图_20210113193831.4wk2b9zc8vm0.png)
|
||||
|
@ -61,6 +65,8 @@ presum [2] = presum[1] + nums[1],presum[3] = presum[2] + nums[2] ... 所以我
|
|||
|
||||
直接上代码。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int subarraySum(int[] nums, int k) {
|
||||
|
@ -86,6 +92,8 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3版本的代码也会超时
|
||||
|
||||
我们通过上面的例子我们简单了解了前缀和思想,那么我们如果继续将其优化呢?
|
||||
|
||||
**前缀和 + HashMap**
|
||||
|
@ -94,6 +102,8 @@ class Solution {
|
|||
|
||||
其实我们在之前的两数之和中已经用到了这个方法,我们一起来回顾两数之和 HashMap 的代码.
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] twoSum(int[] nums, int target) {
|
||||
|
|
|
@ -66,6 +66,8 @@
|
|||
|
||||
题目代码:
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public List<Integer> spiralOrder(int[][] matrix) {
|
||||
|
@ -103,10 +105,45 @@ class Solution {
|
|||
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def spiralOrder(self, matrix: List[List[int]])->List[int]:
|
||||
arr = []
|
||||
left = 0
|
||||
right = len(matrix[0]) - 1
|
||||
top = 0
|
||||
down = len(matrix) - 1
|
||||
while True:
|
||||
for i in range(left, right + 1):
|
||||
arr.append(matrix[top][i])
|
||||
top += 1
|
||||
if top > down:
|
||||
break
|
||||
for i in range(top, down + 1):
|
||||
arr.append(matrix[i][right])
|
||||
right -= 1
|
||||
if left > right:
|
||||
break
|
||||
for i in range(right, left - 1, -1):
|
||||
arr.append(matrix[down][i])
|
||||
down -= 1
|
||||
if top > down:
|
||||
break
|
||||
for i in range(down, top - 1, -1):
|
||||
arr.append(matrix[i][left])
|
||||
left += 1
|
||||
if left > right:
|
||||
break
|
||||
return arr
|
||||
```
|
||||
|
||||
我们仅仅是将 54 反过来了,往螺旋矩阵里面插值,下面我们直接看代码吧,大家可以也可以对其改进,大家可以思考一下,如果修改能够让代码更简洁!
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[][] generateMatrix(int n) {
|
||||
|
@ -147,3 +184,45 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
import numpy as np
|
||||
class Solution:
|
||||
def generateMatrix(self, n: int)->List[List[int]]:
|
||||
arr = np.array([[0] * n] * n)
|
||||
left = 0
|
||||
right = n - 1
|
||||
top = 0
|
||||
buttom = n - 1
|
||||
num = 1
|
||||
numsize = n * n
|
||||
while True:
|
||||
for i in range(left, right + 1):
|
||||
arr[top][i] = num
|
||||
num += 1
|
||||
top += 1
|
||||
if num > numsize:
|
||||
break
|
||||
for i in range(top, buttom + 1):
|
||||
arr[i][right] = num
|
||||
num += 1
|
||||
right -= 1
|
||||
if num > numsize:
|
||||
break
|
||||
for i in range(right, left - 1, -1):
|
||||
arr[buttom][i] = num
|
||||
num += 1
|
||||
buttom -= 1
|
||||
if num > numsize:
|
||||
break
|
||||
for i in range(buttom, top - 1, -1):
|
||||
arr[i][left] = num
|
||||
num += 1
|
||||
left += 1
|
||||
if num > numsize:
|
||||
break
|
||||
return arr.tolist()
|
||||
```
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@
|
|||
|
||||
我们可以根据当前位 余10来判断,这样我们就可以区分属于第几种情况了,大家直接看代码吧,很容易理解的。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public int[] plusOne(int[] digits) {
|
||||
|
@ -65,3 +67,21 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def plusOne(self, digits: List[int])->List[int]:
|
||||
# 获取长度
|
||||
leng = len(digits)
|
||||
for i in range(leng - 1, -1, -1):
|
||||
digits[i] = (digits[i] + 1) % 10
|
||||
# 第一种和第二种情况,如果此时某一位不为 0 ,则直接返回即可。
|
||||
if digits[i] != 0:
|
||||
return digits
|
||||
# 第三种情况,因为数组初始化每一位都为0,我们只需将首位设为1即可
|
||||
arr = [0] * (leng + 1)
|
||||
arr[0] = 1
|
||||
return arr
|
||||
```
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
下面我们直接看代码吧,和三向切分基本一致。
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public void sortColors(int[] nums) {
|
||||
|
@ -65,6 +67,35 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortColors(self, nums: List[int]):
|
||||
leng = len(nums)
|
||||
left = 0
|
||||
# 这里和三向切分不完全一致
|
||||
i = left
|
||||
right = leng - 1
|
||||
while i <= right:
|
||||
if nums[i] == 2:
|
||||
self.swap(nums, i, right)
|
||||
right -= 1
|
||||
elif nums[i] == 0:
|
||||
self.swap(nums, i, left)
|
||||
i += 1
|
||||
left += 1
|
||||
else:
|
||||
i += 1
|
||||
return nums
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
||||
|
||||
另外我们看这段代码,有什么问题呢?那就是我们即使完全符合时,仍会交换元素,这样会大大降低我们的效率。
|
||||
|
||||
例如:[0,0,0,1,1,1,2,2,2]
|
||||
|
@ -83,6 +114,8 @@ class Solution {
|
|||
|
||||
另一种代码表示
|
||||
|
||||
Java Code:
|
||||
|
||||
```java
|
||||
class Solution {
|
||||
public void sortColors(int[] nums) {
|
||||
|
@ -114,3 +147,31 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def sortColors(self, nums: List[int]):
|
||||
left = 0
|
||||
leng = len(nums)
|
||||
right = leng - 1
|
||||
i = 0
|
||||
while i <= right:
|
||||
if nums[i] == 0:
|
||||
self.swap(nums, i, left)
|
||||
left += 1
|
||||
if nums[i] == 2:
|
||||
self.swap(nums, i, right)
|
||||
right -= 1
|
||||
# 如果不等于 1 则需要继续判断,所以不移动 i 指针,i--
|
||||
if nums[i] != 1:
|
||||
i -= 1
|
||||
i += 1
|
||||
return nums
|
||||
|
||||
def swap(self, nums: List[int], i: int, j: int):
|
||||
temp = nums[i]
|
||||
nums[i] = nums[j]
|
||||
nums[j] = temp
|
||||
```
|
|
@ -45,6 +45,22 @@ class Solution {
|
|||
}
|
||||
```
|
||||
|
||||
Python Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def findRepeatNumber(self, nums: List[int])->int:
|
||||
s = set()
|
||||
for x in nums:
|
||||
# 如果发现某元素存在,则返回
|
||||
if x in s:
|
||||
return x
|
||||
# 存入集合
|
||||
s.add(x)
|
||||
return -1
|
||||
```
|
||||
|
||||
#### **原地置换**
|
||||
|
||||
**解析**
|
||||
|
@ -101,3 +117,22 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
class Solution:
|
||||
def findRepeatNumber(self, nums: List[int])->int:
|
||||
if len(nums) == 0:
|
||||
return -1
|
||||
for i in range(0, len(nums)):
|
||||
while nums[i] != i:
|
||||
# 发现重复元素
|
||||
if nums[i] == nums[nums[i]]:
|
||||
return nums[i]
|
||||
# 置换,将指针下的元素换到属于它的索引处
|
||||
temp = nums[i]
|
||||
nums[i] = nums[temp]
|
||||
nums[temp] = temp
|
||||
return -1
|
||||
```
|
||||
|
|
|
@ -82,3 +82,27 @@ public:
|
|||
};
|
||||
```
|
||||
|
||||
Python3 Code:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
import sys
|
||||
class Solution:
|
||||
def minSubArrayLen(self, s: int, nums: List[int])->int:
|
||||
leng = len(nums)
|
||||
windowlen = sys.maxsize
|
||||
i = 0
|
||||
sum = 0
|
||||
for j in range(0, leng):
|
||||
sum += nums[j]
|
||||
while sum >= s:
|
||||
windowlen = min(windowlen, j - i + 1)
|
||||
sum -= nums[i]
|
||||
i += 1
|
||||
|
||||
if windowlen == sys.maxsize:
|
||||
return 0
|
||||
else:
|
||||
return windowlen
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in New Issue