mirror of
https://github.com/krahets/hello-algo.git
synced 2024-12-24 10:16:28 +08:00
1. Remove Pair class from hash coliision code.
2. Fix the comment in my_list code. 3. Add a Q&A to the summary of sorting.
This commit is contained in:
parent
7876e3e88c
commit
54dc288e61
18 changed files with 81 additions and 152 deletions
|
@ -67,7 +67,7 @@ class MyList {
|
|||
// 元素数量超出容量时,触发扩容机制
|
||||
if (size() == capacity())
|
||||
extendCapacity();
|
||||
// 索引 i 以及之后的元素都向后移动一位
|
||||
// 将索引 index 以及之后的元素都向后移动一位
|
||||
for (int j = size() - 1; j >= index; j--) {
|
||||
nums[j + 1] = nums[j];
|
||||
}
|
||||
|
|
|
@ -5,3 +5,6 @@ add_executable(preorder_traversal_iii_template preorder_traversal_iii_template.c
|
|||
add_executable(permutations_i permutations_i.cpp)
|
||||
add_executable(permutations_ii permutations_ii.cpp)
|
||||
add_executable(n_queens n_queens.cpp)
|
||||
add_executable(subset_sum_i_naive subset_sum_i_naive.cpp)
|
||||
add_executable(subset_sum_i subset_sum_i.cpp)
|
||||
add_executable(subset_sum_ii subset_sum_ii.cpp)
|
||||
|
|
|
@ -87,4 +87,4 @@ class GraphAdjList {
|
|||
}
|
||||
};
|
||||
|
||||
// GraphAdjList 的测试样例在 graph_adjacency_list_test.cpp 文件中
|
||||
// 测试样例请见 graph_adjacency_list_test.cpp
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
add_executable(array_hash_map array_hash_map.cpp)
|
||||
add_executable(hash_map hash_map.cpp)
|
||||
add_executable(array_hash_map_test array_hash_map_test.cpp)
|
||||
add_executable(hash_map_chaining hash_map_chaining.cpp)
|
||||
add_executable(hash_map_open_addressing hash_map_open_addressing.cpp)
|
||||
add_executable(simple_hash simple_hash.cpp)
|
||||
add_executable(built_in_hash built_in_hash.cpp)
|
||||
|
|
|
@ -107,47 +107,4 @@ class ArrayHashMap {
|
|||
}
|
||||
};
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化哈希表 */
|
||||
ArrayHashMap map = ArrayHashMap();
|
||||
|
||||
/* 添加操作 */
|
||||
// 在哈希表中添加键值对 (key, value)
|
||||
map.put(12836, "小哈");
|
||||
map.put(15937, "小啰");
|
||||
map.put(16750, "小算");
|
||||
map.put(13276, "小法");
|
||||
map.put(10583, "小鸭");
|
||||
cout << "\n添加完成后,哈希表为\nKey -> Value" << endl;
|
||||
map.print();
|
||||
|
||||
/* 查询操作 */
|
||||
// 向哈希表输入键 key ,得到值 value
|
||||
string name = map.get(15937);
|
||||
cout << "\n输入学号 15937 ,查询到姓名 " << name << endl;
|
||||
|
||||
/* 删除操作 */
|
||||
// 在哈希表中删除键值对 (key, value)
|
||||
map.remove(10583);
|
||||
cout << "\n删除 10583 后,哈希表为\nKey -> Value" << endl;
|
||||
map.print();
|
||||
|
||||
/* 遍历哈希表 */
|
||||
cout << "\n遍历键值对 Key->Value" << endl;
|
||||
for (auto kv : map.pairSet()) {
|
||||
cout << kv->key << " -> " << kv->val << endl;
|
||||
}
|
||||
|
||||
cout << "\n单独遍历键 Key" << endl;
|
||||
for (auto key : map.keySet()) {
|
||||
cout << key << endl;
|
||||
}
|
||||
|
||||
cout << "\n单独遍历值 Value" << endl;
|
||||
for (auto val : map.valueSet()) {
|
||||
cout << val << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// 测试样例请见 array_hash_map_test.cpp
|
||||
|
|
52
codes/cpp/chapter_hashing/array_hash_map_test.cpp
Normal file
52
codes/cpp/chapter_hashing/array_hash_map_test.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* File: array_hash_map_test.cpp
|
||||
* Created Time: 2022-12-14
|
||||
* Author: msk397 (machangxinq@gmail.com)
|
||||
*/
|
||||
|
||||
#include "./array_hash_map.cpp"
|
||||
|
||||
/* Driver Code */
|
||||
int main() {
|
||||
/* 初始化哈希表 */
|
||||
ArrayHashMap map = ArrayHashMap();
|
||||
|
||||
/* 添加操作 */
|
||||
// 在哈希表中添加键值对 (key, value)
|
||||
map.put(12836, "小哈");
|
||||
map.put(15937, "小啰");
|
||||
map.put(16750, "小算");
|
||||
map.put(13276, "小法");
|
||||
map.put(10583, "小鸭");
|
||||
cout << "\n添加完成后,哈希表为\nKey -> Value" << endl;
|
||||
map.print();
|
||||
|
||||
/* 查询操作 */
|
||||
// 向哈希表输入键 key ,得到值 value
|
||||
string name = map.get(15937);
|
||||
cout << "\n输入学号 15937 ,查询到姓名 " << name << endl;
|
||||
|
||||
/* 删除操作 */
|
||||
// 在哈希表中删除键值对 (key, value)
|
||||
map.remove(10583);
|
||||
cout << "\n删除 10583 后,哈希表为\nKey -> Value" << endl;
|
||||
map.print();
|
||||
|
||||
/* 遍历哈希表 */
|
||||
cout << "\n遍历键值对 Key->Value" << endl;
|
||||
for (auto kv : map.pairSet()) {
|
||||
cout << kv->key << " -> " << kv->val << endl;
|
||||
}
|
||||
|
||||
cout << "\n单独遍历键 Key" << endl;
|
||||
for (auto key : map.keySet()) {
|
||||
cout << key << endl;
|
||||
}
|
||||
|
||||
cout << "\n单独遍历值 Value" << endl;
|
||||
for (auto val : map.valueSet()) {
|
||||
cout << val << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -4,18 +4,7 @@
|
|||
* Author: Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.hpp"
|
||||
|
||||
/* 键值对 */
|
||||
struct Pair {
|
||||
public:
|
||||
int key;
|
||||
string val;
|
||||
Pair(int key, string val) {
|
||||
this->key = key;
|
||||
this->val = val;
|
||||
}
|
||||
};
|
||||
#include "./array_hash_map.cpp"
|
||||
|
||||
/* 链式地址哈希表 */
|
||||
class HashMapChaining {
|
||||
|
|
|
@ -4,16 +4,7 @@
|
|||
* Author: Krahets (krahets@163.com)
|
||||
*/
|
||||
|
||||
#include "../utils/common.hpp"
|
||||
|
||||
/* 键值对 */
|
||||
struct Pair {
|
||||
int key;
|
||||
string val;
|
||||
|
||||
Pair(int k, string v) : key(k), val(v) {
|
||||
}
|
||||
};
|
||||
#include "./array_hash_map.cpp"
|
||||
|
||||
/* 开放寻址哈希表 */
|
||||
class HashMapOpenAddressing {
|
||||
|
|
|
@ -69,7 +69,7 @@ func (l *myList) insert(num, index int) {
|
|||
if l.numsSize == l.numsCapacity {
|
||||
l.extendCapacity()
|
||||
}
|
||||
// 索引 i 以及之后的元素都向后移动一位
|
||||
// 将索引 index 以及之后的元素都向后移动一位
|
||||
for j := l.numsSize - 1; j >= index; j-- {
|
||||
l.nums[j+1] = l.nums[j]
|
||||
}
|
||||
|
|
|
@ -9,17 +9,6 @@ package chapter_hashing;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/* 键值对 */
|
||||
class Pair {
|
||||
public int key;
|
||||
public String val;
|
||||
|
||||
public Pair(int key, String val) {
|
||||
this.key = key;
|
||||
this.val = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* 链式地址哈希表 */
|
||||
class HashMapChaining {
|
||||
int size; // 键值对数量
|
||||
|
|
|
@ -6,17 +6,6 @@
|
|||
|
||||
package chapter_hashing;
|
||||
|
||||
/* 键值对 */
|
||||
class Pair {
|
||||
public int key;
|
||||
public String val;
|
||||
|
||||
public Pair(int key, String val) {
|
||||
this.key = key;
|
||||
this.val = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* 开放寻址哈希表 */
|
||||
class HashMapOpenAddressing {
|
||||
private int size; // 键值对数量
|
||||
|
|
|
@ -51,7 +51,7 @@ class MyList:
|
|||
# 元素数量超出容量时,触发扩容机制
|
||||
if self.__size == self.capacity():
|
||||
self.extend_capacity()
|
||||
# 索引 i 以及之后的元素都向后移动一位
|
||||
# 将索引 index 以及之后的元素都向后移动一位
|
||||
for j in range(self.__size - 1, index - 1, -1):
|
||||
self.__nums[j + 1] = self.__nums[j]
|
||||
self.__nums[index] = num
|
||||
|
|
|
@ -4,12 +4,10 @@ Created Time: 2023-06-13
|
|||
Author: Krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
class Pair:
|
||||
"""键值对"""
|
||||
import sys, os.path as osp
|
||||
|
||||
def __init__(self, key: int, val: str):
|
||||
self.key = key
|
||||
self.val = val
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from chapter_hashing.array_hash_map import Pair
|
||||
|
||||
|
||||
class HashMapChaining:
|
||||
|
|
|
@ -4,13 +4,10 @@ Created Time: 2023-06-13
|
|||
Author: Krahets (krahets@163.com)
|
||||
"""
|
||||
|
||||
import sys, os.path as osp
|
||||
|
||||
class Pair:
|
||||
"""键值对"""
|
||||
|
||||
def __init__(self, key: int, val: str):
|
||||
self.key = key
|
||||
self.val = val
|
||||
sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__))))
|
||||
from chapter_hashing.array_hash_map import Pair
|
||||
|
||||
|
||||
class HashMapOpenAddressing:
|
||||
|
|
|
@ -71,7 +71,7 @@ pub fn MyList(comptime T: type) type {
|
|||
if (index < 0 or index >= self.size()) @panic("索引越界");
|
||||
// 元素数量超出容量时,触发扩容机制
|
||||
if (self.size() == self.capacity()) try self.extendCapacity();
|
||||
// 索引 i 以及之后的元素都向后移动一位
|
||||
// 将索引 index 以及之后的元素都向后移动一位
|
||||
var j = self.size() - 1;
|
||||
while (j >= index) : (j -= 1) {
|
||||
self.nums[j + 1] = self.nums[j];
|
||||
|
|
|
@ -34,88 +34,66 @@
|
|||
=== "Java"
|
||||
|
||||
```java title="hash_map_chaining.java"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="hash_map_chaining.cpp"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="hash_map_chaining.py"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="hash_map_chaining.go"
|
||||
[class]{pair}-[func]{}
|
||||
|
||||
[class]{hashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```javascript title="hash_map_chaining.js"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "TypeScript"
|
||||
|
||||
```typescript title="hash_map_chaining.ts"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="hash_map_chaining.c"
|
||||
[class]{pair}-[func]{}
|
||||
|
||||
[class]{hashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="hash_map_chaining.cs"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="hash_map_chaining.swift"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
||||
```zig title="hash_map_chaining.zig"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map_chaining.dart"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapChaining}-[func]{}
|
||||
```
|
||||
|
||||
|
@ -150,88 +128,66 @@
|
|||
=== "Java"
|
||||
|
||||
```java title="hash_map_open_addressing.java"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="hash_map_open_addressing.cpp"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="hash_map_open_addressing.py"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="hash_map_open_addressing.go"
|
||||
[class]{pair}-[func]{}
|
||||
|
||||
[class]{hashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "JavaScript"
|
||||
|
||||
```javascript title="hash_map_open_addressing.js"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "TypeScript"
|
||||
|
||||
```typescript title="hash_map_open_addressing.ts"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="hash_map_open_addressing.c"
|
||||
[class]{pair}-[func]{}
|
||||
|
||||
[class]{hashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="hash_map_open_addressing.cs"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="hash_map_open_addressing.swift"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
||||
```zig title="hash_map_open_addressing.zig"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map_open_addressing.dart"
|
||||
[class]{Pair}-[func]{}
|
||||
|
||||
[class]{HashMapOpenAddressing}-[func]{}
|
||||
```
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
给定一个长度为 $n$ 的有序数组 `nums` ,数组可能包含重复元素。请查找并返回元素 `target` 在数组中首次出现的索引。若数组中不包含该元素,则返回 $-1$ 。
|
||||
|
||||
## 简单方法
|
||||
## 线性方法
|
||||
|
||||
为了查找数组中最左边的 `target` ,我们可以分为两步:
|
||||
|
||||
|
@ -15,11 +15,11 @@
|
|||
|
||||
![线性查找最左边的元素](binary_search_edge.assets/binary_search_left_edge_naive.png)
|
||||
|
||||
这个方法虽然有效,但由于包含线性查找,**其时间复杂度可能会劣化至 $O(n)$** 。
|
||||
这个方法虽然有效,但由于包含线性查找,时间复杂度为 $O(n)$ ,当存在很多重复的 `target` 时效率较低。
|
||||
|
||||
## 二分方法
|
||||
|
||||
实际上,我们可以仅通过二分查找解决以上问题。整体算法流程不变,先计算中点索引 $m$ ,再判断 `target` 和 `nums[m]` 大小关系:
|
||||
考虑仅使用二分查找解决该问题。整体算法流程不变,先计算中点索引 $m$ ,再判断 `target` 和 `nums[m]` 大小关系:
|
||||
|
||||
- 当 `nums[m] < target` 或 `nums[m] > target` 时,说明还没有找到 `target` ,因此采取与上节代码相同的缩小区间操作,**从而使指针 $i$ 和 $j$ 向 `target` 靠近**。
|
||||
- 当 `nums[m] == target` 时,说明“小于 `target` 的元素”在区间 $[i, m - 1]$ 中,因此采用 $j = m - 1$ 来缩小区间,**从而使指针 $j$ 向小于 `target` 的元素靠近**。
|
||||
|
|
|
@ -38,6 +38,10 @@
|
|||
|
||||
回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 $n, n - 1, n - 2, ..., 2, 1$ ,从而递归深度为 $n$ 。尾递归优化可以避免这种情况的出现。
|
||||
|
||||
!!! question "当数组中所有元素都相等时,快速排序的时间复杂度是 $O(n^2)$ 吗?该如何处理这种退化情况?"
|
||||
|
||||
是的。这种情况可以考虑通过哨兵划分将数组划分为三个部分:小于、等于、大于基准数。仅向下递归小于和大于的两部分。在该方法下,输入元素全部相等的数组,仅一轮哨兵划分即可完成排序。
|
||||
|
||||
!!! question "桶排序的最差时间复杂度为什么是 $O(n^2)$ ?"
|
||||
|
||||
最差情况下,所有元素被分至同一个桶中。如果我们采用一个 $O(n^2)$ 算法来排序这些元素,则时间复杂度为 $O(n^2)$ 。
|
||||
|
|
Loading…
Reference in a new issue