Skip to content

Commit ce774cc

Browse files
authored
Merge pull request geekxh#25 from geekxh/feature/0817_refactor
Feature/0817 refactor
2 parents 49aa869 + 604fda7 commit ce774cc

File tree

150 files changed

+8623
-7
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+8623
-7
lines changed

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
# 和小浩学算法
22

3-
English version repo and Gitbook is on [english branch](https://github.com/geekxh/hello-algorithm/tree/english).Star
4-
! Star! Star!
5-
6-
> 本项目于 2020.7.22 位于 github 日总榜第一!截图留念!(还没有助力的,右上角快来个 star )
7-
8-
> [![图片展示失败,点击查看](https://www.geekxh.com/mark.png)](https://www.geekxh.com/mark.png)
9-
3+
<div align="center">
4+
<a href="https://www.geekxh.com"><img src="https://www.geekxh.com/logo_tmp.png" width="250px"></a>
5+
</div>
6+
107
## 简介
118

129
> [![图片展示失败,点击查看](https://www.geekxh.com/book.png)](https://www.geekxh.com/book.png)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
## 找出数组中重复的数字
2+
3+
### 题目描述
4+
在一个长度为 `n` 的数组里的所有数字都在 `0``n-1` 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为 `7` 的数组 `{2, 3, 1, 0, 2, 5, 3}`,那么对应的输出是重复的数字 `2` 或者 `3`
5+
6+
7+
### 解法
8+
#### 解法一
9+
排序后,顺序扫描,判断是否有重复,时间复杂度为 `O(n²)`
10+
11+
#### 解法二
12+
利用哈希表,遍历数组,如果哈希表中没有该元素,则存入哈希表中,否则返回重复的元素。时间复杂度为 `O(n)`,空间复杂度为 `O(n)`
13+
14+
#### 解法三
15+
长度为 `n`,元素的数值范围也为 `n`,如果没有重复元素,那么数组每个下标对应的值与下标相等。
16+
17+
从头到尾遍历数组,当扫描到下标 `i` 的数字 `nums[i]`
18+
- 如果等于 `i`,继续向下扫描;
19+
- 如果不等于 `i`,拿它与第 `nums[i]` 个数进行比较,如果相等,说明有重复值,返回 `nums[i]`。如果不相等,就把第 `i` 个数 和第 `nums[i]` 个数交换。重复这个比较交换的过程。
20+
21+
此算法时间复杂度为 `O(n)`,因为每个元素最多只要两次交换,就能确定位置。空间复杂度为 `O(1)`
22+
23+
```java
24+
25+
/**
26+
* @author Anonymous
27+
* @since 2019/10/27
28+
*/
29+
30+
public class Solution {
31+
/**
32+
* 查找数组中的重复元素
33+
* @param numbers 数组
34+
* @param length 数组长度
35+
* @param duplication duplication[0]存储重复元素
36+
* @return boolean
37+
*/
38+
public boolean duplicate(int[] numbers, int length, int[] duplication) {
39+
if (numbers == null || length < 1) {
40+
return false;
41+
}
42+
for (int e : numbers) {
43+
if (e >= length) {
44+
return false;
45+
}
46+
}
47+
48+
for (int i = 0; i < length; ++i) {
49+
while (numbers[i] != i) {
50+
if (numbers[i] == numbers[numbers[i]]) {
51+
duplication[0] = numbers[i];
52+
return true;
53+
}
54+
swap(numbers, i, numbers[i]);
55+
}
56+
}
57+
58+
return false;
59+
}
60+
61+
private void swap(int[] numbers, int i, int j) {
62+
int t = numbers[i];
63+
numbers[i] = numbers[j];
64+
numbers[j] = t;
65+
}
66+
}
67+
```
68+
69+
### 测试用例
70+
1. 长度为 n 的数组中包含一个或多个重复的数字;
71+
2. 数组中不包含重复的数字;
72+
3. 无效测试输入用例(输入空指针;长度为 n 的数组中包含 0~n-1 之外的数字)。
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
/**
3+
* @author Anonymous
4+
* @since 2019/10/27
5+
*/
6+
7+
public class Solution {
8+
/**
9+
* 查找数组中的重复元素
10+
* @param numbers 数组
11+
* @param length 数组长度
12+
* @param duplication duplication[0]存储重复元素
13+
* @return boolean
14+
*/
15+
public boolean duplicate(int[] numbers, int length, int[] duplication) {
16+
if (numbers == null || length < 1) {
17+
return false;
18+
}
19+
for (int e : numbers) {
20+
if (e >= length) {
21+
return false;
22+
}
23+
}
24+
25+
for (int i = 0; i < length; ++i) {
26+
while (numbers[i] != i) {
27+
if (numbers[i] == numbers[numbers[i]]) {
28+
duplication[0] = numbers[i];
29+
return true;
30+
}
31+
swap(numbers, i, numbers[i]);
32+
}
33+
}
34+
35+
return false;
36+
}
37+
38+
private void swap(int[] numbers, int i, int j) {
39+
int t = numbers[i];
40+
numbers[i] = numbers[j];
41+
numbers[j] = t;
42+
}
43+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
## 不修改数组找出重复的数字
2+
3+
### 题目描述
4+
在一个长度为 `n+1` 的数组里的所有数字都在 `1``n` 的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为 `8` 的数组 `{2, 3, 5, 4, 3, 2, 6, 7}`,那么对应的输出是重复的数字 `2` 或者 `3`
5+
6+
7+
### 解法
8+
#### 解法一
9+
创建长度为 `n+1` 的辅助数组,把原数组的元素复制到辅助数组中。如果原数组被复制的数是 `m`,则放到辅助数组第 `m` 个位置。这样很容易找出重复元素。空间复杂度为 `O(n)`
10+
11+
#### 解法二
12+
数组元素的取值范围是 `[1, n]`,对该范围对半划分,分成 `[1, middle]`, `[middle+1, n]`。计算数组中有多少个(count)元素落在 `[1, middle]` 区间内,如果 count 大于 middle-1+1,那么说明这个范围内有重复元素,否则在另一个范围内。继续对这个范围对半划分,继续统计区间内元素数量。
13+
14+
时间复杂度 `O(n * log n)`,空间复杂度 `O(1)`
15+
16+
注意,此方法无法找出所有重复的元素。
17+
18+
```java
19+
/**
20+
* @author Anonymous
21+
* @since 2019/10/27
22+
*/
23+
24+
public class Solution {
25+
/**
26+
* 不修改数组查找重复的元素,没有则返回-1
27+
* @param numbers 数组
28+
* @return 重复的元素
29+
*/
30+
public int getDuplication(int[] numbers) {
31+
if (numbers == null || numbers.length < 1) {
32+
return -1;
33+
}
34+
35+
int start = 1;
36+
int end = numbers.length - 1;
37+
while (end >= start) {
38+
int middle = start + ((end - start) >> 1);
39+
40+
// 调用 log n 次
41+
int count = countRange(numbers, start, middle);
42+
if (start == end) {
43+
if (count > 1) {
44+
return start;
45+
}
46+
break;
47+
} else {
48+
// 无法找出所有重复的数
49+
if (count > (middle - start) + 1) {
50+
end = middle;
51+
} else {
52+
start = middle + 1;
53+
}
54+
}
55+
}
56+
return -1;
57+
}
58+
59+
60+
/**
61+
* 计算整个数组中有多少个数的取值在[start, end] 之间
62+
* 时间复杂度 O(n)
63+
* @param numbers 数组
64+
* @param start 左边界
65+
* @param end 右边界
66+
* @return 数量
67+
*/
68+
private int countRange(int[] numbers, int start, int end) {
69+
if (numbers == null) {
70+
return 0;
71+
}
72+
int count = 0;
73+
for(int e : numbers) {
74+
if (e >= start && e <= end) {
75+
++count;
76+
}
77+
}
78+
return count;
79+
}
80+
}
81+
```
82+
83+
### 测试用例
84+
1. 长度为 n 的数组中包含一个或多个重复的数字;
85+
2. 数组中不包含重复的数字;
86+
3. 无效测试输入用例(输入空指针)。
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @author Anonymous
3+
* @since 2019/10/27
4+
*/
5+
6+
public class Solution {
7+
/**
8+
* 不修改数组查找重复的元素,没有则返回-1
9+
* @param numbers 数组
10+
* @return 重复的元素
11+
*/
12+
public int getDuplication(int[] numbers) {
13+
if (numbers == null || numbers.length < 1) {
14+
return -1;
15+
}
16+
17+
int start = 1;
18+
int end = numbers.length - 1;
19+
while (end >= start) {
20+
int middle = start + ((end - start) >> 1);
21+
22+
// 调用 log n 次
23+
int count = countRange(numbers, start, middle);
24+
if (start == end) {
25+
if (count > 1) {
26+
return start;
27+
}
28+
break;
29+
} else {
30+
// 无法找出所有重复的数
31+
if (count > (middle - start) + 1) {
32+
end = middle;
33+
} else {
34+
start = middle + 1;
35+
}
36+
}
37+
}
38+
return -1;
39+
}
40+
41+
42+
/**
43+
* 计算整个数组中有多少个数的取值在[start, end] 之间
44+
* 时间复杂度 O(n)
45+
* @param numbers 数组
46+
* @param start 左边界
47+
* @param end 右边界
48+
* @return 数量
49+
*/
50+
private int countRange(int[] numbers, int start, int end) {
51+
if (numbers == null) {
52+
return 0;
53+
}
54+
int count = 0;
55+
for(int e : numbers) {
56+
if (e >= start && e <= end) {
57+
++count;
58+
}
59+
}
60+
return count;
61+
}
62+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
## 二维数组中的查找
2+
3+
### 题目描述
4+
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
5+
6+
7+
### 解法
8+
从二维数组的右上方开始查找:
9+
- 若元素值等于 `target`,返回 `true`
10+
- 若元素值大于 `target`,砍掉这一列,即 `--j`
11+
- 若元素值小于 `target`,砍掉这一行,即 `++i`
12+
13+
也可以从二维数组的左下方开始查找,以下代码使用左下方作为查找的起点。
14+
15+
注意,不能选择左上方或者右下方的数字,因为这样无法缩小查找的范围。
16+
17+
```java
18+
/**
19+
* @author Anonymous
20+
* @since 2019/10/27
21+
*/
22+
23+
public class Solution {
24+
/**
25+
* 二维数组中的查找
26+
* @param target 目标值
27+
* @param array 二维数组
28+
* @return boolean
29+
*/
30+
public boolean find(int target, int[][] array) {
31+
if (array == null) {
32+
return false;
33+
}
34+
int rows = array.length;
35+
int columns = array[0].length;
36+
37+
int i = rows - 1;
38+
int j = 0;
39+
while (i >= 0 && j < columns) {
40+
if (array[i][j] == target) {
41+
return true;
42+
}
43+
if (array[i][j] < target) {
44+
++j;
45+
} else {
46+
--i;
47+
}
48+
}
49+
return false;
50+
}
51+
}
52+
```
53+
54+
### 测试用例
55+
1. 二维数组中包含查找的数字(查找的数字是数组中的最大值和最小值;查找的数字介于数组中的最大值和最小值之间);
56+
2. 二维数组中没有查找的数字(查找的数字大于/小于数组中的最大值;查找的数字在数组的最大值和最小值之间但数组中没有这个数字);
57+
3. 特殊输入测试(输入空指针)。
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* @author Anonymous
3+
* @since 2019/10/27
4+
*/
5+
6+
public class Solution {
7+
/**
8+
* 二维数组中的查找
9+
* @param target 目标值
10+
* @param array 二维数组
11+
* @return boolean
12+
*/
13+
public boolean find(int target, int[][] array) {
14+
if (array == null) {
15+
return false;
16+
}
17+
int rows = array.length;
18+
int columns = array[0].length;
19+
20+
int i = rows - 1;
21+
int j = 0;
22+
while (i >= 0 && j < columns) {
23+
if (array[i][j] == target) {
24+
return true;
25+
}
26+
if (array[i][j] < target) {
27+
++j;
28+
} else {
29+
--i;
30+
}
31+
}
32+
return false;
33+
}
34+
}

0 commit comments

Comments
 (0)