mirror of
https://github.com/krahets/hello-algo.git
synced 2026-07-13 23:56:07 +00:00
Re-translate the Japanese version (#1871)
* Retranslate Japanese docs with GPT-5.4 * Retranslate Japanese code with GPT-5.4
This commit is contained in:
+259
-185
@@ -1,238 +1,238 @@
|
||||
# ハッシュ表
|
||||
# ハッシュテーブル
|
||||
|
||||
<u>ハッシュ表</u>は<u>ハッシュマップ</u>とも呼ばれ、キーと値の間のマッピングを確立し、効率的な要素の取得を可能にするデータ構造です。具体的には、ハッシュ表に`key`を入力すると、$O(1)$の時間計算量で対応する`value`を取得できます。
|
||||
<u>ハッシュテーブル(hash table)</u>は、<u>散列表</u>とも呼ばれ、キー `key` と値 `value` の対応関係を構築することで、高効率な要素検索を実現します。具体的には、ハッシュテーブルにキー `key` を入力すると、対応する値 `value` を $O(1)$ 時間で取得できます。
|
||||
|
||||
下図に示すように、$n$人の学生がいて、各学生には「名前」と「学籍番号」の2つのデータフィールドがあるとします。学籍番号を入力として対応する名前を返すクエリ機能を実装したい場合、下図に示すハッシュ表を使用できます。
|
||||
以下の図に示すように、$n$ 人の学生がいるとし、各学生は「名前」と「学籍番号」の 2 つの情報を持っています。もし「学籍番号を入力すると対応する名前を返す」という検索機能を実現したいなら、下図のようなハッシュテーブルを用いることができます。
|
||||
|
||||

|
||||

|
||||
|
||||
ハッシュ表に加えて、配列や連結リストもクエリ機能の実装に使用できますが、時間計算量が異なります。効率は以下の表で比較されています:
|
||||
ハッシュテーブルのほかに、配列や連結リストでも検索機能を実現できます。それらの効率比較を次の表に示します。
|
||||
|
||||
- **要素の挿入**: 配列(または連結リスト)の末尾に要素を追加するだけです。この操作の時間計算量は$O(1)$です。
|
||||
- **要素の検索**: 配列(または連結リスト)がソートされていないため、要素を検索するにはすべての要素を走査する必要があります。この操作の時間計算量は$O(n)$です。
|
||||
- **要素の削除**: 要素を削除するには、まずその要素を見つけてから、配列(または連結リスト)から削除します。この操作の時間計算量は$O(n)$です。
|
||||
- **要素の追加**:要素を配列(連結リスト)の末尾に追加するだけでよく、$O(1)$ 時間です。
|
||||
- **要素の検索**:配列(連結リスト)は無秩序なので、すべての要素を走査する必要があり、$O(n)$ 時間かかります。
|
||||
- **要素の削除**:先に要素を検索してから配列(連結リスト)から削除する必要があり、$O(n)$ 時間かかります。
|
||||
|
||||
<p align="center"> 表 <id> 一般的な操作の時間効率の比較 </p>
|
||||
<p align="center"> 表 <id> 要素検索効率の比較 </p>
|
||||
|
||||
| | 配列 | 連結リスト | ハッシュ表 |
|
||||
| -------------- | ------ | ----------- | ---------- |
|
||||
| 要素の検索 | $O(n)$ | $O(n)$ | $O(1)$ |
|
||||
| 要素の挿入 | $O(1)$ | $O(1)$ | $O(1)$ |
|
||||
| 要素の削除 | $O(n)$ | $O(n)$ | $O(1)$ |
|
||||
| | 配列 | 連結リスト | ハッシュテーブル |
|
||||
| -------- | ------ | ------ | ------ |
|
||||
| 要素の検索 | $O(n)$ | $O(n)$ | $O(1)$ |
|
||||
| 要素の追加 | $O(1)$ | $O(1)$ | $O(1)$ |
|
||||
| 要素の削除 | $O(n)$ | $O(n)$ | $O(1)$ |
|
||||
|
||||
観察されるように、**ハッシュ表における操作(挿入、削除、検索、変更)の時間計算量は$O(1)$**で、非常に効率的です。
|
||||
以上から分かるように、**ハッシュテーブルにおける追加・削除・検索・更新の時間計算量はいずれも $O(1)$** であり、非常に高効率です。
|
||||
|
||||
## ハッシュ表の一般的な操作
|
||||
## ハッシュテーブルの基本操作
|
||||
|
||||
ハッシュ表の一般的な操作には、初期化、クエリ、キー値ペアの追加、キー値ペアの削除があります。以下はコード例です:
|
||||
ハッシュテーブルの一般的な操作には、初期化、検索、キーと値のペアの追加、キーと値のペアの削除などがあります。コード例は以下のとおりです:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="hash_map.py"
|
||||
# ハッシュ表を初期化
|
||||
# ハッシュテーブルを初期化
|
||||
hmap: dict = {}
|
||||
|
||||
# 追加操作
|
||||
# ハッシュ表にキー値ペア (key, value) を追加
|
||||
hmap[12836] = "小哈"
|
||||
hmap[15937] = "小啰"
|
||||
hmap[16750] = "小算"
|
||||
hmap[13276] = "小法"
|
||||
hmap[10583] = "小鸭"
|
||||
# ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
hmap[12836] = "シャオハ"
|
||||
hmap[15937] = "シャオルオ"
|
||||
hmap[16750] = "シャオスワン"
|
||||
hmap[13276] = "シャオファ"
|
||||
hmap[10583] = "シャオヤー"
|
||||
|
||||
# クエリ操作
|
||||
# ハッシュ表にキーを入力し、値を取得
|
||||
# 検索操作
|
||||
# ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
name: str = hmap[15937]
|
||||
|
||||
# 削除操作
|
||||
# ハッシュ表からキー値ペア (key, value) を削除
|
||||
# ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
hmap.pop(10583)
|
||||
```
|
||||
|
||||
=== "C++"
|
||||
|
||||
```cpp title="hash_map.cpp"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
unordered_map<int, string> map;
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map[12836] = "小哈";
|
||||
map[15937] = "小啰";
|
||||
map[16750] = "小算";
|
||||
map[13276] = "小法";
|
||||
map[10583] = "小鸭";
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハ";
|
||||
map[15937] = "シャオルオ";
|
||||
map[16750] = "シャオスワン";
|
||||
map[13276] = "シャオファ";
|
||||
map[10583] = "シャオヤー";
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
string name = map[15937];
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.erase(10583);
|
||||
```
|
||||
|
||||
=== "Java"
|
||||
|
||||
```java title="hash_map.java"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map.put(12836, "小哈");
|
||||
map.put(15937, "小啰");
|
||||
map.put(16750, "小算");
|
||||
map.put(13276, "小法");
|
||||
map.put(10583, "小鸭");
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.put(12836, "シャオハ");
|
||||
map.put(15937, "シャオルオ");
|
||||
map.put(16750, "シャオスワン");
|
||||
map.put(13276, "シャオファ");
|
||||
map.put(10583, "シャオヤー");
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
String name = map.get(15937);
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(10583);
|
||||
```
|
||||
|
||||
=== "C#"
|
||||
|
||||
```csharp title="hash_map.cs"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
Dictionary<int, string> map = new() {
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
{ 12836, "小哈" },
|
||||
{ 15937, "小啰" },
|
||||
{ 16750, "小算" },
|
||||
{ 13276, "小法" },
|
||||
{ 10583, "小鸭" }
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
{ 12836, "シャオハ" },
|
||||
{ 15937, "シャオルオ" },
|
||||
{ 16750, "シャオスワン" },
|
||||
{ 13276, "シャオファ" },
|
||||
{ 10583, "シャオヤー" }
|
||||
};
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
string name = map[15937];
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.Remove(10583);
|
||||
```
|
||||
|
||||
=== "Go"
|
||||
|
||||
```go title="hash_map_test.go"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
hmap := make(map[int]string)
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
hmap[12836] = "小哈"
|
||||
hmap[15937] = "小啰"
|
||||
hmap[16750] = "小算"
|
||||
hmap[13276] = "小法"
|
||||
hmap[10583] = "小鸭"
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
hmap[12836] = "シャオハ"
|
||||
hmap[15937] = "シャオルオ"
|
||||
hmap[16750] = "シャオスワン"
|
||||
hmap[13276] = "シャオファ"
|
||||
hmap[10583] = "シャオヤー"
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
name := hmap[15937]
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
delete(hmap, 10583)
|
||||
```
|
||||
|
||||
=== "Swift"
|
||||
|
||||
```swift title="hash_map.swift"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
var map: [Int: String] = [:]
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map[12836] = "小哈"
|
||||
map[15937] = "小啰"
|
||||
map[16750] = "小算"
|
||||
map[13276] = "小法"
|
||||
map[10583] = "小鸭"
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハ"
|
||||
map[15937] = "シャオルオ"
|
||||
map[16750] = "シャオスワン"
|
||||
map[13276] = "シャオファ"
|
||||
map[10583] = "シャオヤー"
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
let name = map[15937]!
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.removeValue(forKey: 10583)
|
||||
```
|
||||
|
||||
=== "JS"
|
||||
|
||||
```javascript title="hash_map.js"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
const map = new Map();
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map.set(12836, '小哈');
|
||||
map.set(15937, '小啰');
|
||||
map.set(16750, '小算');
|
||||
map.set(13276, '小法');
|
||||
map.set(10583, '小鸭');
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.set(12836, 'シャオハ');
|
||||
map.set(15937, 'シャオルオ');
|
||||
map.set(16750, 'シャオスワン');
|
||||
map.set(13276, 'シャオファ');
|
||||
map.set(10583, 'シャオヤー');
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
let name = map.get(15937);
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.delete(10583);
|
||||
```
|
||||
|
||||
=== "TS"
|
||||
|
||||
```typescript title="hash_map.ts"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
const map = new Map<number, string>();
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map.set(12836, '小哈');
|
||||
map.set(15937, '小啰');
|
||||
map.set(16750, '小算');
|
||||
map.set(13276, '小法');
|
||||
map.set(10583, '小鸭');
|
||||
console.info('\n追加後、ハッシュ表は\nKey -> Value');
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.set(12836, 'シャオハ');
|
||||
map.set(15937, 'シャオルオ');
|
||||
map.set(16750, 'シャオスワン');
|
||||
map.set(13276, 'シャオファ');
|
||||
map.set(10583, 'シャオヤー');
|
||||
console.info('\n追加後のハッシュテーブルは次のとおりです\nKey -> Value');
|
||||
console.info(map);
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
let name = map.get(15937);
|
||||
console.info('\n学籍番号15937を入力、名前を問い合わせ ' + name);
|
||||
console.info('\n学籍番号 15937 を入力し、名前を検索: ' + name);
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.delete(10583);
|
||||
console.info('\n10583を削除後、ハッシュ表は\nKey -> Value');
|
||||
console.info('\n10583 を削除した後のハッシュテーブル\nKey -> Value');
|
||||
console.info(map);
|
||||
```
|
||||
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map.dart"
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
Map<int, String> map = {};
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map[12836] = "小哈";
|
||||
map[15937] = "小啰";
|
||||
map[16750] = "小算";
|
||||
map[13276] = "小法";
|
||||
map[10583] = "小鸭";
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハ";
|
||||
map[15937] = "シャオルオ";
|
||||
map[16750] = "シャオスワン";
|
||||
map[13276] = "シャオファ";
|
||||
map[10583] = "シャオヤー";
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
String name = map[15937];
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(10583);
|
||||
```
|
||||
|
||||
@@ -241,51 +241,95 @@
|
||||
```rust title="hash_map.rs"
|
||||
use std::collections::HashMap;
|
||||
|
||||
/* ハッシュ表を初期化 */
|
||||
/* ハッシュテーブルを初期化 */
|
||||
let mut map: HashMap<i32, String> = HashMap::new();
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュ表にキー値ペア (key, value) を追加
|
||||
map.insert(12836, "小哈".to_string());
|
||||
map.insert(15937, "小啰".to_string());
|
||||
map.insert(16750, "小算".to_string());
|
||||
map.insert(13279, "小法".to_string());
|
||||
map.insert(10583, "小鸭".to_string());
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map.insert(12836, "シャオハ".to_string());
|
||||
map.insert(15937, "シャオルオ".to_string());
|
||||
map.insert(16750, "シャオスワン".to_string());
|
||||
map.insert(13279, "シャオファ".to_string());
|
||||
map.insert(10583, "シャオヤー".to_string());
|
||||
|
||||
/* クエリ操作 */
|
||||
// ハッシュ表にキーを入力し、値を取得
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
let _name: Option<&String> = map.get(&15937);
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュ表からキー値ペア (key, value) を削除
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
let _removed_value: Option<String> = map.remove(&10583);
|
||||
```
|
||||
|
||||
=== "C"
|
||||
|
||||
```c title="hash_map.c"
|
||||
// Cには組み込みのハッシュ表が提供されていません
|
||||
// C には組み込みのハッシュテーブルはありません
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="hash_map.kt"
|
||||
/* ハッシュテーブルを初期化 */
|
||||
val map = HashMap<Int,String>()
|
||||
|
||||
/* 追加操作 */
|
||||
// ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
map[12836] = "シャオハ"
|
||||
map[15937] = "シャオルオ"
|
||||
map[16750] = "シャオスワン"
|
||||
map[13276] = "シャオファ"
|
||||
map[10583] = "シャオヤー"
|
||||
|
||||
/* 検索操作 */
|
||||
// ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
val name = map[15937]
|
||||
|
||||
/* 削除操作 */
|
||||
// ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
map.remove(10583)
|
||||
```
|
||||
|
||||
ハッシュ表を走査する一般的な方法は3つあります:キー値ペアの走査、キーの走査、値の走査。以下はコード例です:
|
||||
=== "Ruby"
|
||||
|
||||
```ruby title="hash_map.rb"
|
||||
# ハッシュテーブルを初期化
|
||||
hmap = {}
|
||||
|
||||
# 追加操作
|
||||
# ハッシュテーブルにキーと値のペア (key, value) を追加
|
||||
hmap[12836] = "シャオハ"
|
||||
hmap[15937] = "シャオルオ"
|
||||
hmap[16750] = "シャオスワン"
|
||||
hmap[13276] = "シャオファ"
|
||||
hmap[10583] = "シャオヤー"
|
||||
|
||||
# 検索操作
|
||||
# ハッシュテーブルにキー key を入力し、値 value を取得
|
||||
name = hmap[15937]
|
||||
|
||||
# 削除操作
|
||||
# ハッシュテーブルからキーと値のペア (key, value) を削除
|
||||
hmap.delete(10583)
|
||||
```
|
||||
|
||||
??? pythontutor "可視化実行"
|
||||
|
||||
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%9F%A5%E8%AF%A2%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%90%91%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E8%BE%93%E5%85%A5%E9%94%AE%20key%20%EF%BC%8C%E5%BE%97%E5%88%B0%E5%80%BC%20value%0A%20%20%20%20name%20%3D%20hmap%5B15937%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E5%88%A0%E9%99%A4%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap.pop%2810583%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
ハッシュテーブルには、キーと値のペア、キー、値を走査する 3 つの一般的な方法があります。コード例は以下のとおりです:
|
||||
|
||||
=== "Python"
|
||||
|
||||
```python title="hash_map.py"
|
||||
# ハッシュ表を走査
|
||||
# キー値ペア key->value を走査
|
||||
# ハッシュテーブルを走査
|
||||
# キーと値のペア key->value を走査
|
||||
for key, value in hmap.items():
|
||||
print(key, "->", value)
|
||||
# キーのみを走査
|
||||
# キー key のみを走査
|
||||
for key in hmap.keys():
|
||||
print(key)
|
||||
# 値のみを走査
|
||||
# 値 value のみを走査
|
||||
for value in hmap.values():
|
||||
print(value)
|
||||
```
|
||||
@@ -293,12 +337,12 @@
|
||||
=== "C++"
|
||||
|
||||
```cpp title="hash_map.cpp"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア key->value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア key->value を走査
|
||||
for (auto kv: map) {
|
||||
cout << kv.first << " -> " << kv.second << endl;
|
||||
}
|
||||
// イテレータを使用してキー値ペア key->value を走査
|
||||
// イテレータを使って key->value を走査
|
||||
for (auto iter = map.begin(); iter != map.end(); iter++) {
|
||||
cout << iter->first << "->" << iter->second << endl;
|
||||
}
|
||||
@@ -307,16 +351,16 @@
|
||||
=== "Java"
|
||||
|
||||
```java title="hash_map.java"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア key->value を走査
|
||||
for (Map.Entry<Integer, String> kv: map.entrySet()) {
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア key->value を走査
|
||||
for (Map.Entry <Integer, String> kv: map.entrySet()) {
|
||||
System.out.println(kv.getKey() + " -> " + kv.getValue());
|
||||
}
|
||||
// キーのみを走査
|
||||
// キー key のみを走査
|
||||
for (int key: map.keySet()) {
|
||||
System.out.println(key);
|
||||
}
|
||||
// 値のみを走査
|
||||
// 値 value のみを走査
|
||||
for (String val: map.values()) {
|
||||
System.out.println(val);
|
||||
}
|
||||
@@ -325,16 +369,16 @@
|
||||
=== "C#"
|
||||
|
||||
```csharp title="hash_map.cs"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア Key->Value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア Key->Value を走査
|
||||
foreach (var kv in map) {
|
||||
Console.WriteLine(kv.Key + " -> " + kv.Value);
|
||||
}
|
||||
// キーのみを走査
|
||||
// キー key のみを走査
|
||||
foreach (int key in map.Keys) {
|
||||
Console.WriteLine(key);
|
||||
}
|
||||
// 値のみを走査
|
||||
// 値 value のみを走査
|
||||
foreach (string val in map.Values) {
|
||||
Console.WriteLine(val);
|
||||
}
|
||||
@@ -343,16 +387,16 @@
|
||||
=== "Go"
|
||||
|
||||
```go title="hash_map_test.go"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア key->value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア key->value を走査
|
||||
for key, value := range hmap {
|
||||
fmt.Println(key, "->", value)
|
||||
}
|
||||
// キーのみを走査
|
||||
// キー key のみを走査
|
||||
for key := range hmap {
|
||||
fmt.Println(key)
|
||||
}
|
||||
// 値のみを走査
|
||||
// 値 value のみを走査
|
||||
for _, value := range hmap {
|
||||
fmt.Println(value)
|
||||
}
|
||||
@@ -361,16 +405,16 @@
|
||||
=== "Swift"
|
||||
|
||||
```swift title="hash_map.swift"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア Key->Value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア Key->Value を走査
|
||||
for (key, value) in map {
|
||||
print("\(key) -> \(value)")
|
||||
}
|
||||
// キーのみを走査
|
||||
// キー Key のみを走査
|
||||
for key in map.keys {
|
||||
print(key)
|
||||
}
|
||||
// 値のみを走査
|
||||
// 値 Value のみを走査
|
||||
for value in map.values {
|
||||
print(value)
|
||||
}
|
||||
@@ -379,16 +423,16 @@
|
||||
=== "JS"
|
||||
|
||||
```javascript title="hash_map.js"
|
||||
/* ハッシュ表を走査 */
|
||||
console.info('\nキー値ペア Key->Value を走査');
|
||||
/* ハッシュテーブルを走査 */
|
||||
console.info('\nキーと値のペア Key->Value を走査');
|
||||
for (const [k, v] of map.entries()) {
|
||||
console.info(k + ' -> ' + v);
|
||||
}
|
||||
console.info('\nキーのみを走査 Key');
|
||||
console.info('\nキー Key のみを走査');
|
||||
for (const k of map.keys()) {
|
||||
console.info(k);
|
||||
}
|
||||
console.info('\n値のみを走査 Value');
|
||||
console.info('\n値 Value のみを走査');
|
||||
for (const v of map.values()) {
|
||||
console.info(v);
|
||||
}
|
||||
@@ -397,16 +441,16 @@
|
||||
=== "TS"
|
||||
|
||||
```typescript title="hash_map.ts"
|
||||
/* ハッシュ表を走査 */
|
||||
console.info('\nキー値ペア Key->Value を走査');
|
||||
/* ハッシュテーブルを走査 */
|
||||
console.info('\nキーと値のペア Key->Value を走査');
|
||||
for (const [k, v] of map.entries()) {
|
||||
console.info(k + ' -> ' + v);
|
||||
}
|
||||
console.info('\nキーのみを走査 Key');
|
||||
console.info('\nキー Key のみを走査');
|
||||
for (const k of map.keys()) {
|
||||
console.info(k);
|
||||
}
|
||||
console.info('\n値のみを走査 Value');
|
||||
console.info('\n値 Value のみを走査');
|
||||
for (const v of map.values()) {
|
||||
console.info(v);
|
||||
}
|
||||
@@ -415,38 +459,38 @@
|
||||
=== "Dart"
|
||||
|
||||
```dart title="hash_map.dart"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア Key->Value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア Key->Value を走査
|
||||
map.forEach((key, value) {
|
||||
print('$key -> $value');
|
||||
print('$key -> $value');
|
||||
});
|
||||
|
||||
// キーのみを走査 Key
|
||||
// キー Key のみを走査
|
||||
map.keys.forEach((key) {
|
||||
print(key);
|
||||
print(key);
|
||||
});
|
||||
|
||||
// 値のみを走査 Value
|
||||
// 値 Value のみを走査
|
||||
map.values.forEach((value) {
|
||||
print(value);
|
||||
print(value);
|
||||
});
|
||||
```
|
||||
|
||||
=== "Rust"
|
||||
|
||||
```rust title="hash_map.rs"
|
||||
/* ハッシュ表を走査 */
|
||||
// キー値ペア Key->Value を走査
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア Key->Value を走査
|
||||
for (key, value) in &map {
|
||||
println!("{key} -> {value}");
|
||||
}
|
||||
|
||||
// キーのみを走査 Key
|
||||
// キー Key のみを走査
|
||||
for key in map.keys() {
|
||||
println!("{key}");
|
||||
}
|
||||
|
||||
// 値のみを走査 Value
|
||||
// 値 Value のみを走査
|
||||
for value in map.values() {
|
||||
println!("{value}");
|
||||
}
|
||||
@@ -455,63 +499,93 @@
|
||||
=== "C"
|
||||
|
||||
```c title="hash_map.c"
|
||||
// Cには組み込みのハッシュ表が提供されていません
|
||||
// C には組み込みのハッシュテーブルはありません
|
||||
```
|
||||
|
||||
=== "Kotlin"
|
||||
|
||||
```kotlin title="hash_map.kt"
|
||||
|
||||
/* ハッシュテーブルを走査 */
|
||||
// キーと値のペア key->value を走査
|
||||
for ((key, value) in map) {
|
||||
println("$key -> $value")
|
||||
}
|
||||
// キー key のみを走査
|
||||
for (key in map.keys) {
|
||||
println(key)
|
||||
}
|
||||
// 値 value のみを走査
|
||||
for (_val in map.values) {
|
||||
println(_val)
|
||||
}
|
||||
```
|
||||
|
||||
## ハッシュ表の簡単な実装
|
||||
=== "Ruby"
|
||||
|
||||
まず、最も簡単なケースを考えてみましょう:**配列のみを使ってハッシュ表を実装すること**。ハッシュ表において、配列の各空きスロットは<u>バケット</u>と呼ばれ、各バケットはキー値ペアを格納できます。したがって、クエリ操作は`key`に対応するバケットを見つけ、そこから`value`を取得することになります。
|
||||
```ruby title="hash_map.rb"
|
||||
# ハッシュテーブルを走査
|
||||
# キーと値のペア key->value を走査
|
||||
hmap.entries.each { |key, value| puts "#{key} -> #{value}" }
|
||||
|
||||
では、`key`に基づいて対応するバケットをどのように特定するのでしょうか?これは<u>ハッシュ関数</u>によって実現されます。ハッシュ関数の役割は、より大きな入力空間をより小さな出力空間にマッピングすることです。ハッシュ表では、入力空間はすべてのキーで構成され、出力空間はすべてのバケット(配列インデックス)で構成されます。つまり、`key`が与えられた場合、**ハッシュ関数を使用して対応するキー値ペアの配列内の格納位置を決定できます**。
|
||||
# キー key のみを走査
|
||||
hmap.keys.each { |key| puts key }
|
||||
|
||||
与えられた`key`に対して、ハッシュ関数の計算は2つのステップで構成されます:
|
||||
# 値 value のみを走査
|
||||
hmap.values.each { |val| puts val }
|
||||
```
|
||||
|
||||
1. 特定のハッシュアルゴリズム`hash()`を使用してハッシュ値を計算します。
|
||||
2. ハッシュ値をバケット数(配列長)`capacity`で剰余を取り、キーに対応する配列`index`を取得します。
|
||||
??? pythontutor "可視化実行"
|
||||
|
||||
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E9%94%AE%E5%80%BC%E5%AF%B9%20key-%3Evalue%0A%20%20%20%20for%20key,%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key,%20%22-%3E%22,%20value%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E9%94%AE%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E5%80%BC%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
||||
|
||||
## ハッシュテーブルの簡単な実装
|
||||
|
||||
まずは最も単純なケースとして、**1 つの配列だけでハッシュテーブルを実装する**ことを考えます。ハッシュテーブルでは、配列中の各空き位置を<u>バケット(bucket)</u>と呼び、各バケットには 1 つのキーと値のペアを格納できます。したがって、検索操作とは `key` に対応するバケットを見つけ、そのバケットから `value` を取得することです。
|
||||
|
||||
では、`key` に基づいて対応するバケットをどのように特定するのでしょうか。これは<u>ハッシュ関数(hash function)</u>によって実現されます。ハッシュ関数の役割は、大きな入力空間をより小さな出力空間に写像することです。ハッシュテーブルでは、入力空間はすべての `key` 、出力空間はすべてのバケット(配列インデックス)です。言い換えると、`key` を入力すると、**ハッシュ関数によってその `key` に対応するキーと値のペアの配列内での格納位置を求められます**。
|
||||
|
||||
`key` を入力したとき、ハッシュ関数の計算過程は次の 2 段階に分かれます。
|
||||
|
||||
1. あるハッシュアルゴリズム `hash()` を用いてハッシュ値を計算します。
|
||||
2. ハッシュ値をバケット数(配列長)`capacity` で剰余し、その `key` に対応するバケット(配列インデックス)`index` を求めます。
|
||||
|
||||
```shell
|
||||
index = hash(key) % capacity
|
||||
```
|
||||
|
||||
その後、`index`を使用してハッシュ表内の対応するバケットにアクセスし、`value`を取得できます。
|
||||
その後、`index` を使ってハッシュテーブル内の対応するバケットにアクセスし、`value` を取得できます。
|
||||
|
||||
配列長が`capacity = 100`で、ハッシュアルゴリズムが`hash(key) = key`として定義されているとします。したがって、ハッシュ関数は`key % 100`として表現できます。以下の図は、`key`を学籍番号、`value`を名前として、ハッシュ関数の動作原理を示しています。
|
||||
配列長を `capacity = 100` 、ハッシュアルゴリズムを `hash(key) = key` とすると、ハッシュ関数は `key % 100` となります。次の図では、`key` を学籍番号、`value` を名前の例として、ハッシュ関数の動作原理を示します。
|
||||
|
||||

|
||||
|
||||
以下のコードは簡単なハッシュ表を実装しています。ここでは、`key`と`value`を`Pair`クラスにカプセル化してキー値ペアを表現しています。
|
||||
以下のコードは、単純なハッシュテーブルを実装したものです。ここでは、キーと値のペアを表すために `key` と `value` をクラス `Pair` にまとめています。
|
||||
|
||||
```src
|
||||
[file]{array_hash_map}-[class]{array_hash_map}-[func]{}
|
||||
```
|
||||
|
||||
## ハッシュ衝突とリサイズ
|
||||
## ハッシュ衝突と拡張
|
||||
|
||||
本質的に、ハッシュ関数の役割は、すべてのキーの入力空間全体を、すべての配列インデックスの出力空間にマッピングすることです。しかし、入力空間は出力空間よりもはるかに大きいことがよくあります。したがって、**理論的には、「複数の入力が同じ出力に対応する」ケースが常に存在します**。
|
||||
本質的には、ハッシュ関数の役割は、すべての `key` からなる入力空間を、配列のすべてのインデックスからなる出力空間に写像することです。しかし、入力空間は多くの場合、出力空間よりはるかに大きいため、**理論上は必ず「複数の入力が同じ出力に対応する」状況が存在します**。
|
||||
|
||||
上記の例では、与えられたハッシュ関数で、入力`key`の下二桁が同じ場合、ハッシュ関数は同じ出力を生成します。例えば、学籍番号12836と20336の2人の学生をクエリすると、以下のことがわかります:
|
||||
上の例のハッシュ関数では、入力 `key` の下 2 桁が同じであれば、出力結果も同じになります。たとえば、学籍番号 12836 と 20336 の 2 人の学生を検索すると、次の結果を得ます:
|
||||
|
||||
```shell
|
||||
12836 % 100 = 36
|
||||
20336 % 100 = 36
|
||||
```
|
||||
|
||||
下図に示すように、両方の学籍番号が同じ名前を指しており、これは明らかに間違っています。この複数の入力が同じ出力に対応する状況を<u>ハッシュ衝突</u>と呼びます。
|
||||
次の図に示すように、2 つの学籍番号が同じ名前を指してしまっており、これは明らかに誤りです。このような、複数の入力が同じ出力に対応する状況を<u>ハッシュ衝突(hash collision)</u>と呼びます。
|
||||
|
||||

|
||||
|
||||
ハッシュ表の容量$n$が増加するにつれて、複数のキーが同じバケットに割り当てられる確率が減少し、衝突が少なくなることは理解しやすいです。したがって、**ハッシュ表をリサイズすることでハッシュ衝突を減らすことができます**。
|
||||
容易に分かるように、ハッシュテーブルの容量 $n$ が大きいほど、複数の `key` が同じバケットに割り当てられる確率は低くなり、衝突も少なくなります。したがって、**ハッシュテーブルを拡張することでハッシュ衝突を減らせます**。
|
||||
|
||||
下図に示すように、リサイズ前は、キー値ペア`(136, A)`と`(236, D)`が衝突していました。しかし、リサイズ後は衝突が解決されています。
|
||||
次の図に示すように、拡張前はキーと値のペア `(136, A)` と `(236, D)` が衝突していますが、拡張後は衝突が解消されます。
|
||||
|
||||

|
||||

|
||||
|
||||
配列の拡張と同様に、ハッシュ表のリサイズにはすべてのキー値ペアを元のハッシュ表から新しいものに移行する必要があり、時間がかかります。さらに、ハッシュ表の`capacity`が変更されるため、ハッシュ関数を使用してすべてのキー値ペアの格納位置を再計算する必要があり、リサイズプロセスの計算オーバーヘッドがさらに増加します。したがって、プログラミング言語は頻繁なリサイズを防ぐために、ハッシュ表に十分大きな容量を割り当てることがよくあります。
|
||||
配列の拡張と同様に、ハッシュテーブルの拡張ではすべてのキーと値のペアを元のハッシュテーブルから新しいハッシュテーブルへ移し替える必要があり、非常に時間がかかります。また、ハッシュテーブルの容量 `capacity` が変わるため、ハッシュ関数を使ってすべてのキーと値のペアの格納位置を再計算しなければならず、これによって拡張過程の計算コストがさらに増加します。そのため、プログラミング言語では通常、頻繁な拡張を防ぐために十分大きなハッシュテーブル容量をあらかじめ確保します。
|
||||
|
||||
<u>負荷率</u>はハッシュ表の重要な概念です。ハッシュ表内の要素数とバケット数の比率として定義されます。ハッシュ衝突の深刻度を測定するために使用され、**しばしばハッシュ表のリサイズのトリガーとしても機能します**。例えば、Javaでは、負荷率が$0.75$を超えると、システムはハッシュ表を元のサイズの2倍にリサイズします。
|
||||
<u>負荷率(load factor)</u>はハッシュテーブルにおける重要な概念であり、ハッシュテーブル内の要素数をバケット数で割ったものとして定義され、ハッシュ衝突の深刻さを測るために用いられます。**また、ハッシュテーブル拡張の発動条件としてもよく使われます**。例えば Java では、負荷率が $0.75$ を超えると、システムはハッシュテーブルを元の $2$ 倍に拡張します。
|
||||
|
||||
Reference in New Issue
Block a user