C ++研究ノート(2)————ハッシュテーブル(ハッシュ)

1.ハッシュテーブル(ハッシュテーブル)

検索フィールドには、線形テーブル検索、ツリー検索(BSTと同様)の3つの主要な検索方法があり、もう1つはハッシュテーブル検索とも呼ばれるハッシュテーブル検索です。

  • ハッシュ機能
  • ハッシュ表
  • データレコード

ハッシュテーブルは私たちの機能です。入力キー値ごとに、一意のストレージアドレスがマップされ、アドレスは最終的にデータを保存します。

  • ハッシュ関数はマッピングであり、設計の中核は、競合を最小限に抑えるための完全なハッシュ関数を設計することです。
  • ハッシュ関数:データを挿入、削除、保存するための任意のキー値に対応する一意のメモリアドレスを返します
  • 競合:周期関数が関数に表示されるのと同じように、異なるキーワードに対して同じメモリアドレスを取得する可能性がある状況も発生します。これは競合です。対応する2つのキーワードが呼び出されます。同義。
  • ハッシュテーブル:ハッシュテーブルは、設定したハッシュ関数と、キーワードのセットを連続アドレスセット(間隔)にマップし、アドレスでキーワードを使用するために設定した競合解決方法に基づいています。テーブルに記録されている保存場所と同様に、これはハッシュテーブルと呼ばれます。このマッピングプロセスは、ハッシュテーブルまたはハッシュの確立とも呼ばれ、取得した保存場所はハッシュアドレスまたはハッシュアドレスと呼ばれます。

要約すると、ハッシュテーブルの2つのコアが見つかります。

1.ハッシュを作成します:(ハッシュ関数を作成します)ハッシュ関数に基づいてマッピングを作成します。

2.競合への対処:競合のないハッシュ関数を設計することは困難であるため、競合に対処することが非常に必要です。

第二に、ハッシュテーブルの構築方法

ハッシュ技術は、レコードの保存場所とそのキーワードの間に特定の対応する関係fを確立することです。各キーワードキーは、保存場所f(キー)に対応します。検索するときは、この対応する関係に従って特定の値を見つけます。キーマッピングf(キー)が検索セットに存在する場合は、f(キー)の位置にある必要があります。この対応をハッシュ関数と呼びます。これはハッシュ(ハッシュ)関数とも呼ばれます。ハッシュテクノロジーは、レコードをストレージスペースに格納するために使用されます。この連続したスペースは、ハッシュテーブルまたはハッシュテーブル(Hash-Table)と呼ばれます。

2.1直接アドレス指定方法

直接アドレス指定方法は、次の式を使用します

f(key)= a * key + b、aおよびbは定数です

たとえば、生年月日を数える場合、f(key)= key-1990を使用してハッシュアドレスを計算できます。

アドレスh(キー) 生年月日(鍵) 番号(属性)
0 1990年 1,285万
1 1991 1,281万
2 1992年 1,280万
\ cdots \ cdots \ cdots
10 2000年 1250万
\ cdots \ cdots \ cdots
21 2011 1180万

2.2分割して残りを残す

この方法は、最も一般的に使用されるハッシュ関数の構築方法です。テーブルの長さがmの場合のハッシュ式は、次のとおりです
。f(key)= key mod p(p <= m)

  住所

h(キー)

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

キーワード

   キー

34 18 2 20     23 7 42   27 11   30   15  
  • ここで:p = Tablesize = 17
  • 一般的に、pは素数です 

2.3デジタル分析法

各ビットのデジタルキーワードの変化を分析し、よりランダムなビットをハッシュアドレスとして使用します。ここでは例として携帯電話番号を使用します。携帯電話番号は文字列です。一般的に、最後の4桁は実際のユーザー番号です。

例:11桁の携帯電話番号キーの最後の4桁をアドレスとして使用します。
ハッシュ関数は次のとおりです。h(key)= atoi(key + 7)(char * key)

2.4折り方

キーワードを同じビット数のいくつかの部分に分割し、次に重ね合わせます。

例:56793542

2.5  平方取中法

 例:56793542

3つの競合解決方法

競合に対処する一般的な方法:
1。場所の変更:オープンアドレス方式

2.同じ場所にある競合するオブジェクトは一緒に編成されます:チェーンアドレス方式

3.1オープンアドレス方式

競合が発生すると(アドレスにすでに他の要素が含まれている場合)、特定のルールに従って別の空のアドレスが検出されます。i番目の競合が発生すると、次にプローブされるアドレスが増加しd_ {i}ます。基本的な式は次のとおりです
h_ {i} \ left(key \ right)-(h \ left(key \ right)+ d_ {i})  。modTableSize(1 < = i <TableSize)

これd_ {i}により、線形検出、二乗検出、ダブルハッシュなどのさまざまな競合解決スキームが決まります。さまざまな方法を順番に示します。

3.1.1線形検出方法

線形検出方法は、1、2、...、(TableSize-1)の増分シーケンスで次のストレージアドレスを周期的にプローブします。

【例1】キーワードシーケンスを47、7、29、11、9、84、54、20、30、ハッシュテーブル長とする

TableSize = 11(充填率\ alpha = \ frac {9} {13} \約0.69)、ハッシュ関数は次のとおりです。h(key)= key mod11。線形検出方法を使用して競合を処理し、連続して挿入した後にハッシュテーブルを一覧表示し、検索パフォーマンスを推定します。

【解決策】予備ハッシュアドレスを次の表に示します。

关键词(key)      47 7 29 11 9 84 54 20 30
散列地址h(key)    3 7  7  0 9  7 10  9  8

複数のキーワードのハッシュアドレスに競合があることがわかります。詳細については、以下の表を参照してください。

关键词(key)      47 7 29 11 9 84 54 20 30
散列地址h(key)    3 7  7  0 9  7 10  9  8
冲突次数            0 0  1  0 0  3  1  3  6

特定のハッシュテーブルの構築プロセスは、次の図で表すことができます。

オペレーティング 住所 0 1 2 3 4 5 6 7 8 9 10 11 12 説明
インサート47       47                   競合なし
インサート7       47       7           競合なし
インサート29       47       7 29         d_ {1} = 1
インサート11 11     47       7 29         競合なし
インサート9 11     47       7 29 9       競合なし
84を挿入 11     47       7 29 9 84     d_ {3} = 3
インサート54 11     47       7 29 9 84 54   d_ {1} = 1
20を挿入 11     47       7 29 9 84 54 20 d_ {3} = 3
30を挿入 11 30   47       7 29 9 84 54 20 d_ {6} = 6

 ハッシュテーブルの検索パフォーマンスの分析は次のとおりです。ハッシュテーブルの検索パフォーマンスには、一般に2つの方法があります。

1.成功した平均検索長(ASL)

2.失敗した平均検索長(ASLu)

上記の質問の場合、ハッシュアドレスの競合の数は次のとおりです。

关键词(key)      0  1  2  3  4  5  6  7  8   9  10  11  12
散列地址h(key)   11 30    47          7  28  9  84  54  20
冲突次数           0  6     0           0  1   0  3   1   3

ASL:ルックアップテーブル内のキーワードの検索と比較の平均数(競合の数に1を加えた数)

ASL =(1 + 7 + 1 + 1 + 2 + 1 + 4 + 2 + 4)/ 9 = \ approx23/9 2.56

ASLu:ハッシュテーブルにないキーワードの平均検索数(失敗)

一般的な方法:ハッシュテーブルにないキーワードをいくつかのカテゴリに分割します。

例:h(キー)値の分類による

ASLu =(3 + 2 + 1 + 2 + 1 + 1 + 1 + 9 + 8 + 7 + 6)/ 11 = \ approx41/11 3.73

3.1.2正方形の検出方法

二乗検出方式では、インクリメンタルシーケンス1 ^ {2}、-1 ^ {2}、2 ^ {2}、-2 ^ {2}、\ cdots \ cdots、q ^ {2}q <= [TableSize / 2]ループを使用して、次のストレージアドレスをプローブします。[例1]を引き続き使用すると、得られる競合は次のようになります。

キーワード 47 7 29 11 9 84 54 20 30
ハッシュアドレスh(キー) 3 7 7 0 9 7 10 9 8
競合の数 0 0 1 0 0 2 0 3 3

 ASL =(1 + 1 + 2 + 1 + 1 + 3 + 1 + 4 + 4)/ 9 = 18/9 = 2

オペレーティング 住所 0 1 2 3 4 5 6 7 8 9 10 説明
インサート47       47               競合なし
インサート7       47       7       競合なし
インサート29       47       7 29     d_ {1} = 1
インサート11 11     47       7 29     競合なし
インサート9 11     47       7 29 9   競合なし
84を挿入 11     47     84 7 29 9   d_ {2} = -1
インサート54 11     47     84 7 29 9 54 競合なし
20を挿入 11   20 47     84 7 29 9 54 d_ {3} = 4
30を挿入 11 30 20 47     84 7 29 9 54 d_ {3} = 4

3.2チェーンアドレス方式 

チェーンアドレス方式では、競合するすべてのキーワードを、同じ単一リンクリストの対応する位置に格納します。 

【例2】キーシーケンスを47、7、29、11、16、92、22、8、3、50、37、89、94、21とし、ハッシュ関数はh(key)= key mod 11 、競合を処理するには、分離リンク方式を使用します。

[1日1回の質問]

2つの数値の合計

整数配列numsとターゲット値targetが与えられた場合、合計が配列内のターゲット値である2つの整数を見つけて、それらの配列添え字を返します。

各入力は1つの回答にのみ対応すると想定できます。ただし、配列内の同じ要素を2回使用することはできません。

例:

给定 nums = [2,7,11,15],target = 9
因为 nums[0] + nums[1] = 2 + 7 =9
所以返回[0,1]

方法1:暴力的な列挙

【回答】

アイデアと方法:

考える最も簡単な方法は、配列内のすべての数値xを列挙して、target-xが配列内に存在するかどうかを確認することです。

配列全体をトラバースしてtarget-xを見つける方法を使用する場合、xより前のすべての要素がxと一致しているため、一致する必要がないことに注意する必要があります。各要素を2回使用することはできないため、xの後の要素でtarget-xを探すだけで済みます。

コード:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[0];
    }
}

方法2:ハッシュテーブル

アイデアとアルゴリズム:

方法1の時間の複雑さが高い理由は、target-xを見つける時間の複雑さが高すぎるためであることに注意してください。したがって、ターゲット要素が配列に存在するかどうかをすばやく見つけることができる、より優れた方法が必要です。存在する場合は、そのインデックスを見つける必要があります。

ハッシュテーブルを使用すると、target-xを見つける時間の複雑さをO(N)からO(1)に減らすことができます。

このようにして、ハッシュテーブルを作成します。xごとに、最初にtarget-xがハッシュテーブルに存在するかどうかを照会し、次にxをハッシュテーブルに挿入して、xがそれ自体と一致しないようにします。

コード:

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; ++i) {
            if (hashtable.containsKey(target - nums[i])) {
                return new int[]{hashtable.get(target - nums[i]), i};
            }
            hashtable.put(nums[i], i);
        }
        return new int[0];
    }
}

 

おすすめ

転載: blog.csdn.net/weixin_38452841/article/details/109079580