This commit is contained in:
krahets
2023-09-04 03:16:55 +08:00
parent f07e94ab0c
commit 8f74a87eba
54 changed files with 23015 additions and 23015 deletions
+255 -255
View File
@@ -51,26 +51,24 @@ comments: true
值得注意的是,由于 $i$ 和 $j$ 都是 `int` 类型,**因此 $i + j$ 可能会超出 `int` 类型的取值范围**。为了避免大数越界,我们通常采用公式 $m = \lfloor {i + (j - i) / 2} \rfloor$ 来计算中点。
=== "Java"
=== "Python"
```java title="binary_search.java"
/* 二分查找(双闭区间) */
int binarySearch(int[] nums, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = nums.length - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```python title="binary_search.py"
def binary_search(nums: list[int], target: int) -> int:
"""二分查找(双闭区间)"""
# 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
i, j = 0, len(nums) - 1
# 循环,当搜索区间为空时跳出(当 i > j 时为空)
while i <= j:
# 理论上 Python 的数字可以无限大(取决于内存大小),无须考虑大数越界问题
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # 此情况说明 target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # 此情况说明 target 在区间 [i, m-1] 中
else:
return m # 找到目标元素,返回其索引
return -1 # 未找到目标元素,返回 -1
```
=== "C++"
@@ -95,24 +93,48 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="binary_search.py"
def binary_search(nums: list[int], target: int) -> int:
"""二分查找(双闭区间)"""
# 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
i, j = 0, len(nums) - 1
# 循环,当搜索区间为空时跳出(当 i > j 时为空)
while i <= j:
# 理论上 Python 的数字可以无限大(取决于内存大小),无须考虑大数越界问题
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # 此情况说明 target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # 此情况说明 target 在区间 [i, m-1] 中
else:
return m # 找到目标元素,返回其索引
return -1 # 未找到目标元素,返回 -1
```java title="binary_search.java"
/* 二分查找(双闭区间) */
int binarySearch(int[] nums, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = nums.length - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "C#"
```csharp title="binary_search.cs"
/* 二分查找(双闭区间) */
int binarySearch(int[] nums, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = nums.Length - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Go"
@@ -138,6 +160,30 @@ comments: true
}
```
=== "Swift"
```swift title="binary_search.swift"
/* 二分查找(双闭区间) */
func binarySearch(nums: [Int], target: Int) -> Int {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
var i = 0
var j = nums.count - 1
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
```
=== "JS"
```javascript title="binary_search.js"
@@ -190,98 +236,6 @@ comments: true
}
```
=== "C"
```c title="binary_search.c"
/* 二分查找(双闭区间) */
int binarySearch(int *nums, int len, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = len - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "C#"
```csharp title="binary_search.cs"
/* 二分查找(双闭区间) */
int binarySearch(int[] nums, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = nums.Length - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Swift"
```swift title="binary_search.swift"
/* 二分查找(双闭区间) */
func binarySearch(nums: [Int], target: Int) -> Int {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
var i = 0
var j = nums.count - 1
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
```
=== "Zig"
```zig title="binary_search.zig"
// 二分查找(双闭区间)
fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
var i: usize = 0;
var j: usize = nums.items.len - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
var m = i + (j - i) / 2; // 计算中点索引 m
if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
} else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
} else { // 找到目标元素,返回其索引
return @intCast(m);
}
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Dart"
```dart title="binary_search.dart"
@@ -332,6 +286,52 @@ comments: true
}
```
=== "C"
```c title="binary_search.c"
/* 二分查找(双闭区间) */
int binarySearch(int *nums, int len, int target) {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
int i = 0, j = len - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Zig"
```zig title="binary_search.zig"
// 二分查找(双闭区间)
fn binarySearch(comptime T: type, nums: std.ArrayList(T), target: T) T {
// 初始化双闭区间 [0, n-1] ,即 i, j 分别指向数组首元素、尾元素
var i: usize = 0;
var j: usize = nums.items.len - 1;
// 循环,当搜索区间为空时跳出(当 i > j 时为空)
while (i <= j) {
var m = i + (j - i) / 2; // 计算中点索引 m
if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j] 中
i = m + 1;
} else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m-1] 中
j = m - 1;
} else { // 找到目标元素,返回其索引
return @intCast(m);
}
}
// 未找到目标元素,返回 -1
return -1;
}
```
**时间复杂度 $O(\log n)$** :在二分循环中,区间每轮缩小一半,循环次数为 $\log_2 n$ 。
**空间复杂度 $O(1)$** :指针 $i$ 和 $j$ 使用常数大小空间。
@@ -342,26 +342,23 @@ comments: true
我们可以基于该表示实现具有相同功能的二分查找算法。
=== "Java"
=== "Python"
```java title="binary_search.java"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int[] nums, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = nums.length;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```python title="binary_search.py"
def binary_search_lcro(nums: list[int], target: int) -> int:
"""二分查找(左闭右开)"""
# 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
i, j = 0, len(nums)
# 循环,当搜索区间为空时跳出(当 i = j 时为空)
while i < j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # 此情况说明 target 在区间 [m+1, j) 中
elif nums[m] > target:
j = m # 此情况说明 target 在区间 [i, m) 中
else:
return m # 找到目标元素,返回其索引
return -1 # 未找到目标元素,返回 -1
```
=== "C++"
@@ -386,23 +383,48 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="binary_search.py"
def binary_search_lcro(nums: list[int], target: int) -> int:
"""二分查找(左闭右开)"""
# 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
i, j = 0, len(nums)
# 循环,当搜索区间为空时跳出(当 i = j 时为空)
while i < j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # 此情况说明 target 在区间 [m+1, j) 中
elif nums[m] > target:
j = m # 此情况说明 target 在区间 [i, m) 中
else:
return m # 找到目标元素,返回其索引
return -1 # 未找到目标元素,返回 -1
```java title="binary_search.java"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int[] nums, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = nums.length;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "C#"
```csharp title="binary_search.cs"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int[] nums, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = nums.Length;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Go"
@@ -428,6 +450,30 @@ comments: true
}
```
=== "Swift"
```swift title="binary_search.swift"
/* 二分查找(左闭右开) */
func binarySearchLCRO(nums: [Int], target: Int) -> Int {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
var i = 0
var j = nums.count
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while i < j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中
j = m
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
```
=== "JS"
```javascript title="binary_search.js"
@@ -481,98 +527,6 @@ comments: true
}
```
=== "C"
```c title="binary_search.c"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int *nums, int len, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = len;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "C#"
```csharp title="binary_search.cs"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int[] nums, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = nums.Length;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Swift"
```swift title="binary_search.swift"
/* 二分查找(左闭右开) */
func binarySearchLCRO(nums: [Int], target: Int) -> Int {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
var i = 0
var j = nums.count
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while i < j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target { // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1
} else if nums[m] > target { // 此情况说明 target 在区间 [i, m) 中
j = m
} else { // 找到目标元素,返回其索引
return m
}
}
// 未找到目标元素,返回 -1
return -1
}
```
=== "Zig"
```zig title="binary_search.zig"
// 二分查找(左闭右开)
fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
var i: usize = 0;
var j: usize = nums.items.len;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i <= j) {
var m = i + (j - i) / 2; // 计算中点索引 m
if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
} else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m) 中
j = m;
} else { // 找到目标元素,返回其索引
return @intCast(m);
}
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Dart"
```dart title="binary_search.dart"
@@ -623,6 +577,52 @@ comments: true
}
```
=== "C"
```c title="binary_search.c"
/* 二分查找(左闭右开) */
int binarySearchLCRO(int *nums, int len, int target) {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
int i = 0, j = len;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i < j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
else if (nums[m] > target) // 此情况说明 target 在区间 [i, m) 中
j = m;
else // 找到目标元素,返回其索引
return m;
}
// 未找到目标元素,返回 -1
return -1;
}
```
=== "Zig"
```zig title="binary_search.zig"
// 二分查找(左闭右开)
fn binarySearchLCRO(comptime T: type, nums: std.ArrayList(T), target: T) T {
// 初始化左闭右开 [0, n) ,即 i, j 分别指向数组首元素、尾元素+1
var i: usize = 0;
var j: usize = nums.items.len;
// 循环,当搜索区间为空时跳出(当 i = j 时为空)
while (i <= j) {
var m = i + (j - i) / 2; // 计算中点索引 m
if (nums.items[m] < target) { // 此情况说明 target 在区间 [m+1, j) 中
i = m + 1;
} else if (nums.items[m] > target) { // 此情况说明 target 在区间 [i, m) 中
j = m;
} else { // 找到目标元素,返回其索引
return @intCast(m);
}
}
// 未找到目标元素,返回 -1
return -1;
}
```
如图 10-3 所示,在两种区间表示下,二分查找算法的初始化、循环条件和缩小区间操作皆有所不同。
由于“双闭区间”表示中的左右边界都被定义为闭区间,因此指针 $i$ 和 $j$ 缩小区间操作也是对称的。这样更不容易出错,**因此一般建议采用“双闭区间”的写法**。
+144 -144
View File
@@ -20,20 +20,18 @@ status: new
当遇到以上两种情况时,直接返回 $-1$ 即可。
=== "Java"
=== "Python"
```java title="binary_search_edge.java"
/* 二分查找最左一个 target */
int binarySearchLeftEdge(int[] nums, int target) {
// 等价于查找 target 的插入点
int i = binary_search_insertion.binarySearchInsertion(nums, target);
// 未找到 target ,返回 -1
if (i == nums.length || nums[i] != target) {
return -1;
}
// 找到 target ,返回索引 i
return i;
}
```python title="binary_search_edge.py"
def binary_search_left_edge(nums: list[int], target: int) -> int:
"""二分查找最左一个 target"""
# 等价于查找 target 的插入点
i = binary_search_insertion(nums, target)
# 未找到 target ,返回 -1
if i == len(nums) or nums[i] != target:
return -1
# 找到 target ,返回索引 i
return i
```
=== "C++"
@@ -52,18 +50,36 @@ status: new
}
```
=== "Python"
=== "Java"
```python title="binary_search_edge.py"
def binary_search_left_edge(nums: list[int], target: int) -> int:
"""二分查找最左一个 target"""
# 等价于查找 target 的插入点
i = binary_search_insertion(nums, target)
# 未找到 target ,返回 -1
if i == len(nums) or nums[i] != target:
return -1
# 找到 target ,返回索引 i
return i
```java title="binary_search_edge.java"
/* 二分查找最左一个 target */
int binarySearchLeftEdge(int[] nums, int target) {
// 等价于查找 target 的插入点
int i = binary_search_insertion.binarySearchInsertion(nums, target);
// 未找到 target ,返回 -1
if (i == nums.length || nums[i] != target) {
return -1;
}
// 找到 target ,返回索引 i
return i;
}
```
=== "C#"
```csharp title="binary_search_edge.cs"
/* 二分查找最左一个 target */
int binarySearchLeftEdge(int[] nums, int target) {
// 等价于查找 target 的插入点
int i = binary_search_insertion.binarySearchInsertion(nums, target);
// 未找到 target ,返回 -1
if (i == nums.Length || nums[i] != target) {
return -1;
}
// 找到 target ,返回索引 i
return i;
}
```
=== "Go"
@@ -82,6 +98,22 @@ status: new
}
```
=== "Swift"
```swift title="binary_search_edge.swift"
/* 二分查找最左一个 target */
func binarySearchLeftEdge(nums: [Int], target: Int) -> Int {
// 等价于查找 target 的插入点
let i = binarySearchInsertion(nums: nums, target: target)
// 未找到 target ,返回 -1
if i == nums.count || nums[i] != target {
return -1
}
// 找到 target ,返回索引 i
return i
}
```
=== "JS"
```javascript title="binary_search_edge.js"
@@ -114,50 +146,6 @@ status: new
}
```
=== "C"
```c title="binary_search_edge.c"
[class]{}-[func]{binarySearchLeftEdge}
```
=== "C#"
```csharp title="binary_search_edge.cs"
/* 二分查找最左一个 target */
int binarySearchLeftEdge(int[] nums, int target) {
// 等价于查找 target 的插入点
int i = binary_search_insertion.binarySearchInsertion(nums, target);
// 未找到 target ,返回 -1
if (i == nums.Length || nums[i] != target) {
return -1;
}
// 找到 target ,返回索引 i
return i;
}
```
=== "Swift"
```swift title="binary_search_edge.swift"
/* 二分查找最左一个 target */
func binarySearchLeftEdge(nums: [Int], target: Int) -> Int {
// 等价于查找 target 的插入点
let i = binarySearchInsertion(nums: nums, target: target)
// 未找到 target ,返回 -1
if i == nums.count || nums[i] != target {
return -1
}
// 找到 target ,返回索引 i
return i
}
```
=== "Zig"
```zig title="binary_search_edge.zig"
[class]{}-[func]{binarySearchLeftEdge}
```
=== "Dart"
```dart title="binary_search_edge.dart"
@@ -190,6 +178,18 @@ status: new
}
```
=== "C"
```c title="binary_search_edge.c"
[class]{}-[func]{binarySearchLeftEdge}
```
=== "Zig"
```zig title="binary_search_edge.zig"
[class]{}-[func]{binarySearchLeftEdge}
```
## 10.3.2 &nbsp; 查找右边界
那么如何查找最右一个 `target` 呢?最直接的方式是修改代码,替换在 `nums[m] == target` 情况下的指针收缩操作。代码在此省略,有兴趣的同学可以自行实现。
@@ -208,22 +208,20 @@ status: new
请注意,返回的插入点是 $i$ ,因此需要将其减 $1$ ,从而获得 $j$ 。
=== "Java"
=== "Python"
```java title="binary_search_edge.java"
/* 二分查找最右一个 target */
int binarySearchRightEdge(int[] nums, int target) {
// 转化为查找最左一个 target + 1
int i = binary_search_insertion.binarySearchInsertion(nums, target + 1);
// j 指向最右一个 target ,i 指向首个大于 target 的元素
int j = i - 1;
// 未找到 target ,返回 -1
if (j == -1 || nums[j] != target) {
return -1;
}
// 找到 target ,返回索引 j
return j;
}
```python title="binary_search_edge.py"
def binary_search_right_edge(nums: list[int], target: int) -> int:
"""二分查找最右一个 target"""
# 转化为查找最左一个 target + 1
i = binary_search_insertion(nums, target + 1)
# j 指向最右一个 target ,i 指向首个大于 target 的元素
j = i - 1
# 未找到 target ,返回 -1
if j == -1 or nums[j] != target:
return -1
# 找到 target ,返回索引 j
return j
```
=== "C++"
@@ -244,20 +242,40 @@ status: new
}
```
=== "Python"
=== "Java"
```python title="binary_search_edge.py"
def binary_search_right_edge(nums: list[int], target: int) -> int:
"""二分查找最右一个 target"""
# 转化为查找最左一个 target + 1
i = binary_search_insertion(nums, target + 1)
# j 指向最右一个 target ,i 指向首个大于 target 的元素
j = i - 1
# 未找到 target ,返回 -1
if j == -1 or nums[j] != target:
return -1
# 找到 target ,返回索引 j
return j
```java title="binary_search_edge.java"
/* 二分查找最右一个 target */
int binarySearchRightEdge(int[] nums, int target) {
// 转化为查找最左一个 target + 1
int i = binary_search_insertion.binarySearchInsertion(nums, target + 1);
// j 指向最右一个 target ,i 指向首个大于 target 的元素
int j = i - 1;
// 未找到 target ,返回 -1
if (j == -1 || nums[j] != target) {
return -1;
}
// 找到 target ,返回索引 j
return j;
}
```
=== "C#"
```csharp title="binary_search_edge.cs"
/* 二分查找最右一个 target */
int binarySearchRightEdge(int[] nums, int target) {
// 转化为查找最左一个 target + 1
int i = binary_search_insertion.binarySearchInsertion(nums, target + 1);
// j 指向最右一个 target ,i 指向首个大于 target 的元素
int j = i - 1;
// 未找到 target ,返回 -1
if (j == -1 || nums[j] != target) {
return -1;
}
// 找到 target ,返回索引 j
return j;
}
```
=== "Go"
@@ -278,6 +296,24 @@ status: new
}
```
=== "Swift"
```swift title="binary_search_edge.swift"
/* 二分查找最右一个 target */
func binarySearchRightEdge(nums: [Int], target: Int) -> Int {
// 转化为查找最左一个 target + 1
let i = binarySearchInsertion(nums: nums, target: target + 1)
// j 指向最右一个 target ,i 指向首个大于 target 的元素
let j = i - 1
// 未找到 target ,返回 -1
if j == -1 || nums[j] != target {
return -1
}
// 找到 target ,返回索引 j
return j
}
```
=== "JS"
```javascript title="binary_search_edge.js"
@@ -314,54 +350,6 @@ status: new
}
```
=== "C"
```c title="binary_search_edge.c"
[class]{}-[func]{binarySearchRightEdge}
```
=== "C#"
```csharp title="binary_search_edge.cs"
/* 二分查找最右一个 target */
int binarySearchRightEdge(int[] nums, int target) {
// 转化为查找最左一个 target + 1
int i = binary_search_insertion.binarySearchInsertion(nums, target + 1);
// j 指向最右一个 target ,i 指向首个大于 target 的元素
int j = i - 1;
// 未找到 target ,返回 -1
if (j == -1 || nums[j] != target) {
return -1;
}
// 找到 target ,返回索引 j
return j;
}
```
=== "Swift"
```swift title="binary_search_edge.swift"
/* 二分查找最右一个 target */
func binarySearchRightEdge(nums: [Int], target: Int) -> Int {
// 转化为查找最左一个 target + 1
let i = binarySearchInsertion(nums: nums, target: target + 1)
// j 指向最右一个 target ,i 指向首个大于 target 的元素
let j = i - 1
// 未找到 target ,返回 -1
if j == -1 || nums[j] != target {
return -1
}
// 找到 target ,返回索引 j
return j
}
```
=== "Zig"
```zig title="binary_search_edge.zig"
[class]{}-[func]{binarySearchRightEdge}
```
=== "Dart"
```dart title="binary_search_edge.dart"
@@ -398,6 +386,18 @@ status: new
}
```
=== "C"
```c title="binary_search_edge.c"
[class]{}-[func]{binarySearchRightEdge}
```
=== "Zig"
```zig title="binary_search_edge.zig"
[class]{}-[func]{binarySearchRightEdge}
```
### 2. &nbsp; 转化为查找元素
我们知道,当数组不包含 `target` 时,最终 $i$ 和 $j$ 会分别指向首个大于、小于 `target` 的元素。
+174 -174
View File
@@ -29,25 +29,22 @@ status: new
因此二分结束时一定有:$i$ 指向首个大于 `target` 的元素,$j$ 指向首个小于 `target` 的元素。**易得当数组不包含 `target` 时,插入索引为 $i$** 。
=== "Java"
=== "Python"
```java title="binary_search_insertion.java"
/* 二分查找插入点(无重复元素) */
int binarySearchInsertionSimple(int[] nums, int target) {
int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
return m; // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i;
}
```python title="binary_search_insertion.py"
def binary_search_insertion_simple(nums: list[int], target: int) -> int:
"""二分查找插入点(无重复元素)"""
i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1]
while i <= j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # target 在区间 [i, m-1] 中
else:
return m # 找到 target ,返回插入点 m
# 未找到 target ,返回插入点 i
return i
```
=== "C++"
@@ -71,22 +68,46 @@ status: new
}
```
=== "Python"
=== "Java"
```python title="binary_search_insertion.py"
def binary_search_insertion_simple(nums: list[int], target: int) -> int:
"""二分查找插入点(无重复元素)"""
i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1]
while i <= j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # target 在区间 [i, m-1] 中
else:
return m # 找到 target ,返回插入点 m
# 未找到 target ,返回插入点 i
return i
```java title="binary_search_insertion.java"
/* 二分查找插入点(无重复元素) */
int binarySearchInsertionSimple(int[] nums, int target) {
int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
return m; // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i;
}
```
=== "C#"
```csharp title="binary_search_insertion.cs"
/* 二分查找插入点(无重复元素) */
int binarySearchInsertionSimple(int[] nums, int target) {
int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
return m; // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i;
}
```
=== "Go"
@@ -115,6 +136,27 @@ status: new
}
```
=== "Swift"
```swift title="binary_search_insertion.swift"
/* 二分查找插入点(无重复元素) */
func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int {
var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target {
i = m + 1 // target 在区间 [m+1, j] 中
} else if nums[m] > target {
j = m - 1 // target 在区间 [i, m-1] 中
} else {
return m // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i
}
```
=== "JS"
```javascript title="binary_search_insertion.js"
@@ -162,60 +204,6 @@ status: new
}
```
=== "C"
```c title="binary_search_insertion.c"
[class]{}-[func]{binarySearchInsertionSimple}
```
=== "C#"
```csharp title="binary_search_insertion.cs"
/* 二分查找插入点(无重复元素) */
int binarySearchInsertionSimple(int[] nums, int target) {
int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
return m; // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i;
}
```
=== "Swift"
```swift title="binary_search_insertion.swift"
/* 二分查找插入点(无重复元素) */
func binarySearchInsertionSimple(nums: [Int], target: Int) -> Int {
var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target {
i = m + 1 // target 在区间 [m+1, j] 中
} else if nums[m] > target {
j = m - 1 // target 在区间 [i, m-1] 中
} else {
return m // 找到 target ,返回插入点 m
}
}
// 未找到 target ,返回插入点 i
return i
}
```
=== "Zig"
```zig title="binary_search_insertion.zig"
[class]{}-[func]{binarySearchInsertionSimple}
```
=== "Dart"
```dart title="binary_search_insertion.dart"
@@ -258,6 +246,18 @@ status: new
}
```
=== "C"
```c title="binary_search_insertion.c"
[class]{}-[func]{binarySearchInsertionSimple}
```
=== "Zig"
```zig title="binary_search_insertion.zig"
[class]{}-[func]{binarySearchInsertionSimple}
```
## 10.2.2 &nbsp; 存在重复元素的情况
!!! question
@@ -314,25 +314,22 @@ status: new
即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。
=== "Java"
=== "Python"
```java title="binary_search_insertion.java"
/* 二分查找插入点(存在重复元素) */
int binarySearchInsertion(int[] nums, int target) {
int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i;
}
```python title="binary_search_insertion.py"
def binary_search_insertion(nums: list[int], target: int) -> int:
"""二分查找插入点(存在重复元素)"""
i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1]
while i <= j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # target 在区间 [i, m-1] 中
else:
j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中
# 返回插入点 i
return i
```
=== "C++"
@@ -356,22 +353,46 @@ status: new
}
```
=== "Python"
=== "Java"
```python title="binary_search_insertion.py"
def binary_search_insertion(nums: list[int], target: int) -> int:
"""二分查找插入点(存在重复元素)"""
i, j = 0, len(nums) - 1 # 初始化双闭区间 [0, n-1]
while i <= j:
m = (i + j) // 2 # 计算中点索引 m
if nums[m] < target:
i = m + 1 # target 在区间 [m+1, j] 中
elif nums[m] > target:
j = m - 1 # target 在区间 [i, m-1] 中
else:
j = m - 1 # 首个小于 target 的元素在区间 [i, m-1] 中
# 返回插入点 i
return i
```java title="binary_search_insertion.java"
/* 二分查找插入点(存在重复元素) */
int binarySearchInsertion(int[] nums, int target) {
int i = 0, j = nums.length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i;
}
```
=== "C#"
```csharp title="binary_search_insertion.cs"
/* 二分查找插入点(存在重复元素) */
int binarySearchInsertion(int[] nums, int target) {
int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i;
}
```
=== "Go"
@@ -400,6 +421,27 @@ status: new
}
```
=== "Swift"
```swift title="binary_search_insertion.swift"
/* 二分查找插入点(存在重复元素) */
func binarySearchInsertion(nums: [Int], target: Int) -> Int {
var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target {
i = m + 1 // target 在区间 [m+1, j] 中
} else if nums[m] > target {
j = m - 1 // target 在区间 [i, m-1] 中
} else {
j = m - 1 // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i
}
```
=== "JS"
```javascript title="binary_search_insertion.js"
@@ -444,60 +486,6 @@ status: new
}
```
=== "C"
```c title="binary_search_insertion.c"
[class]{}-[func]{binarySearchInsertion}
```
=== "C#"
```csharp title="binary_search_insertion.cs"
/* 二分查找插入点(存在重复元素) */
int binarySearchInsertion(int[] nums, int target) {
int i = 0, j = nums.Length - 1; // 初始化双闭区间 [0, n-1]
while (i <= j) {
int m = i + (j - i) / 2; // 计算中点索引 m
if (nums[m] < target) {
i = m + 1; // target 在区间 [m+1, j] 中
} else if (nums[m] > target) {
j = m - 1; // target 在区间 [i, m-1] 中
} else {
j = m - 1; // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i;
}
```
=== "Swift"
```swift title="binary_search_insertion.swift"
/* 二分查找插入点(存在重复元素) */
func binarySearchInsertion(nums: [Int], target: Int) -> Int {
var i = 0, j = nums.count - 1 // 初始化双闭区间 [0, n-1]
while i <= j {
let m = i + (j - i) / 2 // 计算中点索引 m
if nums[m] < target {
i = m + 1 // target 在区间 [m+1, j] 中
} else if nums[m] > target {
j = m - 1 // target 在区间 [i, m-1] 中
} else {
j = m - 1 // 首个小于 target 的元素在区间 [i, m-1] 中
}
}
// 返回插入点 i
return i
}
```
=== "Zig"
```zig title="binary_search_insertion.zig"
[class]{}-[func]{binarySearchInsertion}
```
=== "Dart"
```dart title="binary_search_insertion.dart"
@@ -540,6 +528,18 @@ status: new
}
```
=== "C"
```c title="binary_search_insertion.c"
[class]{}-[func]{binarySearchInsertion}
```
=== "Zig"
```zig title="binary_search_insertion.zig"
[class]{}-[func]{binarySearchInsertion}
```
!!! tip
本节的代码都是“双闭区间”写法。有兴趣的读者可以自行实现“左闭右开”写法。
+200 -200
View File
@@ -18,21 +18,17 @@ comments: true
<p align="center"> 图 10-9 &nbsp; 线性查找求解两数之和 </p>
=== "Java"
=== "Python"
```java title="two_sum.java"
/* 方法一:暴力枚举 */
int[] twoSumBruteForce(int[] nums, int target) {
int size = nums.length;
// 两层循环,时间复杂度 O(n^2)
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
if (nums[i] + nums[j] == target)
return new int[] { i, j };
}
}
return new int[0];
}
```python title="two_sum.py"
def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
"""方法一:暴力枚举"""
# 两层循环,时间复杂度 O(n^2)
for i in range(len(nums) - 1):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return []
```
=== "C++"
@@ -52,17 +48,38 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="two_sum.py"
def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
"""方法一:暴力枚举"""
# 两层循环,时间复杂度 O(n^2)
for i in range(len(nums) - 1):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return []
```java title="two_sum.java"
/* 方法一:暴力枚举 */
int[] twoSumBruteForce(int[] nums, int target) {
int size = nums.length;
// 两层循环,时间复杂度 O(n^2)
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
if (nums[i] + nums[j] == target)
return new int[] { i, j };
}
}
return new int[0];
}
```
=== "C#"
```csharp title="two_sum.cs"
/* 方法一:暴力枚举 */
int[] twoSumBruteForce(int[] nums, int target) {
int size = nums.Length;
// 两层循环,时间复杂度 O(n^2)
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
if (nums[i] + nums[j] == target)
return new int[] { i, j };
}
}
return Array.Empty<int>();
}
```
=== "Go"
@@ -83,6 +100,23 @@ comments: true
}
```
=== "Swift"
```swift title="two_sum.swift"
/* 方法一:暴力枚举 */
func twoSumBruteForce(nums: [Int], target: Int) -> [Int] {
// 两层循环,时间复杂度 O(n^2)
for i in nums.indices.dropLast() {
for j in nums.indices.dropFirst(i + 1) {
if nums[i] + nums[j] == target {
return [i, j]
}
}
}
return [0]
}
```
=== "JS"
```javascript title="two_sum.js"
@@ -119,80 +153,6 @@ comments: true
}
```
=== "C"
```c title="two_sum.c"
/* 方法一:暴力枚举 */
int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) {
for (int i = 0; i < numsSize; ++i) {
for (int j = i + 1; j < numsSize; ++j) {
if (nums[i] + nums[j] == target) {
int *res = malloc(sizeof(int) * 2);
res[0] = i, res[1] = j;
*returnSize = 2;
return res;
}
}
}
*returnSize = 0;
return NULL;
}
```
=== "C#"
```csharp title="two_sum.cs"
/* 方法一:暴力枚举 */
int[] twoSumBruteForce(int[] nums, int target) {
int size = nums.Length;
// 两层循环,时间复杂度 O(n^2)
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
if (nums[i] + nums[j] == target)
return new int[] { i, j };
}
}
return Array.Empty<int>();
}
```
=== "Swift"
```swift title="two_sum.swift"
/* 方法一:暴力枚举 */
func twoSumBruteForce(nums: [Int], target: Int) -> [Int] {
// 两层循环,时间复杂度 O(n^2)
for i in nums.indices.dropLast() {
for j in nums.indices.dropFirst(i + 1) {
if nums[i] + nums[j] == target {
return [i, j]
}
}
}
return [0]
}
```
=== "Zig"
```zig title="two_sum.zig"
// 方法一:暴力枚举
fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
var size: usize = nums.len;
var i: usize = 0;
// 两层循环,时间复杂度 O(n^2)
while (i < size - 1) : (i += 1) {
var j = i + 1;
while (j < size) : (j += 1) {
if (nums[i] + nums[j] == target) {
return [_]i32{@intCast(i), @intCast(j)};
}
}
}
return null;
}
```
=== "Dart"
```dart title="two_sum.dart"
@@ -227,6 +187,46 @@ comments: true
}
```
=== "C"
```c title="two_sum.c"
/* 方法一:暴力枚举 */
int *twoSumBruteForce(int *nums, int numsSize, int target, int *returnSize) {
for (int i = 0; i < numsSize; ++i) {
for (int j = i + 1; j < numsSize; ++j) {
if (nums[i] + nums[j] == target) {
int *res = malloc(sizeof(int) * 2);
res[0] = i, res[1] = j;
*returnSize = 2;
return res;
}
}
}
*returnSize = 0;
return NULL;
}
```
=== "Zig"
```zig title="two_sum.zig"
// 方法一:暴力枚举
fn twoSumBruteForce(nums: []i32, target: i32) ?[2]i32 {
var size: usize = nums.len;
var i: usize = 0;
// 两层循环,时间复杂度 O(n^2)
while (i < size - 1) : (i += 1) {
var j = i + 1;
while (j < size) : (j += 1) {
if (nums[i] + nums[j] == target) {
return [_]i32{@intCast(i), @intCast(j)};
}
}
}
return null;
}
```
此方法的时间复杂度为 $O(n^2)$ ,空间复杂度为 $O(1)$ ,在大数据量下非常耗时。
## 10.4.2 &nbsp; 哈希查找:以空间换时间
@@ -249,23 +249,19 @@ comments: true
实现代码如下所示,仅需单层循环即可。
=== "Java"
=== "Python"
```java title="two_sum.java"
/* 方法二:辅助哈希表 */
int[] twoSumHashTable(int[] nums, int target) {
int size = nums.length;
// 辅助哈希表,空间复杂度 O(n)
Map<Integer, Integer> dic = new HashMap<>();
// 单层循环,时间复杂度 O(n)
for (int i = 0; i < size; i++) {
if (dic.containsKey(target - nums[i])) {
return new int[] { dic.get(target - nums[i]), i };
}
dic.put(nums[i], i);
}
return new int[0];
}
```python title="two_sum.py"
def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
"""方法二:辅助哈希表"""
# 辅助哈希表,空间复杂度 O(n)
dic = {}
# 单层循环,时间复杂度 O(n)
for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i]
dic[nums[i]] = i
return []
```
=== "C++"
@@ -287,19 +283,42 @@ comments: true
}
```
=== "Python"
=== "Java"
```python title="two_sum.py"
def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
"""方法二:辅助哈希表"""
# 辅助哈希表,空间复杂度 O(n)
dic = {}
# 单层循环,时间复杂度 O(n)
for i in range(len(nums)):
if target - nums[i] in dic:
return [dic[target - nums[i]], i]
dic[nums[i]] = i
return []
```java title="two_sum.java"
/* 方法二:辅助哈希表 */
int[] twoSumHashTable(int[] nums, int target) {
int size = nums.length;
// 辅助哈希表,空间复杂度 O(n)
Map<Integer, Integer> dic = new HashMap<>();
// 单层循环,时间复杂度 O(n)
for (int i = 0; i < size; i++) {
if (dic.containsKey(target - nums[i])) {
return new int[] { dic.get(target - nums[i]), i };
}
dic.put(nums[i], i);
}
return new int[0];
}
```
=== "C#"
```csharp title="two_sum.cs"
/* 方法二:辅助哈希表 */
int[] twoSumHashTable(int[] nums, int target) {
int size = nums.Length;
// 辅助哈希表,空间复杂度 O(n)
Dictionary<int, int> dic = new();
// 单层循环,时间复杂度 O(n)
for (int i = 0; i < size; i++) {
if (dic.ContainsKey(target - nums[i])) {
return new int[] { dic[target - nums[i]], i };
}
dic.Add(nums[i], i);
}
return Array.Empty<int>();
}
```
=== "Go"
@@ -320,6 +339,24 @@ comments: true
}
```
=== "Swift"
```swift title="two_sum.swift"
/* 方法二:辅助哈希表 */
func twoSumHashTable(nums: [Int], target: Int) -> [Int] {
// 辅助哈希表,空间复杂度 O(n)
var dic: [Int: Int] = [:]
// 单层循环,时间复杂度 O(n)
for i in nums.indices {
if let j = dic[target - nums[i]] {
return [j, i]
}
dic[nums[i]] = i
}
return [0]
}
```
=== "JS"
```javascript title="two_sum.js"
@@ -359,6 +396,43 @@ comments: true
}
```
=== "Dart"
```dart title="two_sum.dart"
/* 方法二: 辅助哈希表 */
List<int> twoSumHashTable(List<int> nums, int target) {
int size = nums.length;
// 辅助哈希表,空间复杂度 O(n)
Map<int, int> dic = HashMap();
// 单层循环,时间复杂度 O(n)
for (var i = 0; i < size; i++) {
if (dic.containsKey(target - nums[i])) {
return [dic[target - nums[i]]!, i];
}
dic.putIfAbsent(nums[i], () => i);
}
return [0];
}
```
=== "Rust"
```rust title="two_sum.rs"
/* 方法二:辅助哈希表 */
pub fn two_sum_hash_table(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
// 辅助哈希表,空间复杂度 O(n)
let mut dic = HashMap::new();
// 单层循环,时间复杂度 O(n)
for (i, num) in nums.iter().enumerate() {
match dic.get(&(target - num)) {
Some(v) => return Some(vec![*v as i32, i as i32]),
None => dic.insert(num, i as i32)
};
}
None
}
```
=== "C"
```c title="two_sum.c"
@@ -408,43 +482,6 @@ comments: true
}
```
=== "C#"
```csharp title="two_sum.cs"
/* 方法二:辅助哈希表 */
int[] twoSumHashTable(int[] nums, int target) {
int size = nums.Length;
// 辅助哈希表,空间复杂度 O(n)
Dictionary<int, int> dic = new();
// 单层循环,时间复杂度 O(n)
for (int i = 0; i < size; i++) {
if (dic.ContainsKey(target - nums[i])) {
return new int[] { dic[target - nums[i]], i };
}
dic.Add(nums[i], i);
}
return Array.Empty<int>();
}
```
=== "Swift"
```swift title="two_sum.swift"
/* 方法二:辅助哈希表 */
func twoSumHashTable(nums: [Int], target: Int) -> [Int] {
// 辅助哈希表,空间复杂度 O(n)
var dic: [Int: Int] = [:]
// 单层循环,时间复杂度 O(n)
for i in nums.indices {
if let j = dic[target - nums[i]] {
return [j, i]
}
dic[nums[i]] = i
}
return [0]
}
```
=== "Zig"
```zig title="two_sum.zig"
@@ -466,43 +503,6 @@ comments: true
}
```
=== "Dart"
```dart title="two_sum.dart"
/* 方法二: 辅助哈希表 */
List<int> twoSumHashTable(List<int> nums, int target) {
int size = nums.length;
// 辅助哈希表,空间复杂度 O(n)
Map<int, int> dic = HashMap();
// 单层循环,时间复杂度 O(n)
for (var i = 0; i < size; i++) {
if (dic.containsKey(target - nums[i])) {
return [dic[target - nums[i]]!, i];
}
dic.putIfAbsent(nums[i], () => i);
}
return [0];
}
```
=== "Rust"
```rust title="two_sum.rs"
/* 方法二:辅助哈希表 */
pub fn two_sum_hash_table(nums: &Vec<i32>, target: i32) -> Option<Vec<i32>> {
// 辅助哈希表,空间复杂度 O(n)
let mut dic = HashMap::new();
// 单层循环,时间复杂度 O(n)
for (i, num) in nums.iter().enumerate() {
match dic.get(&(target - num)) {
Some(v) => return Some(vec![*v as i32, i as i32]),
None => dic.insert(num, i as i32)
};
}
None
}
```
此方法通过哈希查找将时间复杂度从 $O(n^2)$ 降低至 $O(n)$ ,大幅提升运行效率。
由于需要维护一个额外的哈希表,因此空间复杂度为 $O(n)$ 。**尽管如此,该方法的整体时空效率更为均衡,因此它是本题的最优解法**。