【杂乱算法】哈希

哈希

基础定义

哈希函数(Hash Function)

哈希函数是将任意大小的数据映射到固定大小的数据(通常是一个整数)的函数。这个固定大小的数据称为哈希值或哈希码。一个好的哈希函数应该满足以下特性:

  1. 一致性:相同的输入总是产生相同的哈希值。
  2. 高效性:计算哈希值的过程应该快速。
  3. 均匀分布:哈希值应该尽可能均匀地分布在可能的哈希值范围内,以减少冲突。

哈希表(Hash Table)

哈希表是一种数据结构,它使用哈希函数将键(key)映射到数组中的一个位置,以便快速访问记录。哈希表通常支持以下操作:

  • 插入(Insert):将一个键值对插入到哈希表中。
  • 查找(Search):根据键查找对应的值。
  • 删除(Delete):根据键删除对应的键值对。

哈希冲突(Hash Collision)

由于哈希函数的输出范围通常小于输入范围,不同的键可能会映射到同一个位置,这种现象称为哈希冲突。解决哈希冲突的常见方法有:

  1. 链地址法(Chaining):每个数组位置存储一个链表,所有哈希到该位置的键值对都存储在这个链表中。

    #include <iostream>
    #include <list>
    #include <vector>
    
    using namespace std;
    
    class HashTable {
    private:
        vector<list<int>> table;
        int size;
    
        int hashFunction(int key) {
            return key % size;
        }
    
    public:
        HashTable(int s) : size(s) {
            table.resize(size);
        }
    
        void insert(int key) {
            int index = hashFunction(key);
            table[index].push_back(key);
        }
    
        void remove(int key) {
            int index = hashFunction(key);
            table[index].remove(key);
        }
    
        bool search(int key) {
            int index = hashFunction(key);
            for (int val : table[index]) {
                if (val == key) {
                    return true;
                }
            }
            return false;
        }
    
        void display() {
            for (int i = 0; i < size; ++i) {
                cout << "Bucket " << i << ": ";
                for (int val : table[i]) {
                    cout << val << " ";
                }
                cout << endl;
            }
        }
    };
    
    int main() {
        HashTable ht(7);
    
        ht.insert(10);
        ht.insert(20);
        ht.insert(15);
        ht.insert(7);
    
        ht.display();
    
        cout << "Search 15: " << (ht.search(15) ? "Found" : "Not Found") << endl;
        cout << "Search 5: " << (ht.search(5) ? "Found" : "Not Found") << endl;
    
        ht.remove(15);
        ht.display();
    
        return 0;
    }
    
  2. 开放地址法(Open Addressing):当发生冲突时,寻找哈希表中的下一个空闲位置来存储冲突的键值对。常见的开放地址法包括线性探测、二次探测和双重哈希。

    • 线性探测法:

      如果遇到冲突即向后移动,直至能插入为止。

      #include <iostream>
      #include <vector>
      
      using namespace std;
      
      class HashTable {
      private:
          vector<int> table;
          int size;
          int emptyValue;
      
          int hashFunction(int key) {
              return key % size;
          }
      
      public:
          HashTable(int s, int emptyVal = -1) : size(s), emptyValue(emptyVal) {
              table.resize(size, emptyValue);
          }
      
          void insert(int key) {
              int index = hashFunction(key);
              while (table[index] != emptyValue) {
                  index = (index + 1) % size;
              }
              table[index] = key;
          }
      
          void remove(int key) {
              int index = hashFunction(key);
              while (table[index] != emptyValue) {
                  if (table[index] == key) {
                      table[index] = emptyValue;
                      return;
                  }
                  index = (index + 1) % size;
              }
          }
      
          bool search(int key) {
              int index = hashFunction(key);
              while (table[index] != emptyValue) {
                  if (table[index] == key) {
                      return true;
                  }
                  index = (index + 1) % size;
              }
              return false;
          }
      
          void display() {
              for (int i = 0; i < size; ++i) {
                  if (table[i] != emptyValue) {
                      cout << "Index " << i << ": " << table[i] << endl;
                  } else {
                      cout << "Index " << i << ": " << "Empty" << endl;
                  }
              }
          }
      };
      
      int main() {
          HashTable ht(7);
      
          ht.insert(10);
          ht.insert(20);
          ht.insert(15);
          ht.insert(7);
      
          ht.display();
      
          cout << "Search 15: " << (ht.search(15) ? "Found" : "Not Found") << endl;
          cout << "Search 5: " << (ht.search(5) ? "Found" : "Not Found") << endl;
      
          ht.remove(15);
          ht.display();
      
          return 0;
      }
      
    • 二次探测法:

      遇到冲突即采取平方的方式进行移动。

      #include <iostream>
      #include <vector>
      
      using namespace std;
      
      class HashTable {
      private:
          vector<int> table;
          int size;
          int emptyValue;
      
          int hashFunction(int key) {
              return key % size;
          }
      
      public:
          HashTable(int s, int emptyVal = -1) : size(s), emptyValue(emptyVal) {
              table.resize(size, emptyValue);
          }
      
          void insert(int key) {
              int index = hashFunction(key);
              int i = 0;
              while (table[(index + i * i) % size] != emptyValue) {
                  i++;
              }
              table[(index + i * i) % size] = key;
          }
      
          void remove(int key) {
              int index = hashFunction(key);
              int i = 0;
              while (table[(index + i * i) % size] != emptyValue) {
                  if (table[(index + i * i) % size] == key) {
                      table[(index + i * i) % size] = emptyValue;
                      return;
                  }
                  i++;
              }
          }
      
          bool search(int key) {
              int index = hashFunction(key);
              int i = 0;
              while (table[(index + i * i) % size] != emptyValue) {
                  if (table[(index + i * i) % size] == key) {
                      return true;
                  }
                  i++;
              }
              return false;
          }
      
          void display() {
              for (int i = 0; i < size; ++i) {
                  if (table[i] != emptyValue) {
                      cout << "Index " << i << ": " << table[i] << endl;
                  } else {
                      cout << "Index " << i << ": " << "Empty" << endl;
                  }
              }
          }
      };
      
      int main() {
          HashTable ht(7);
      
          ht.insert(10);
          ht.insert(20);
          ht.insert(15);
          ht.insert(7);
      
          ht.display();
      
          cout << "Search 15: " << (ht.search(15) ? "Found" : "Not Found") << endl;
          cout << "Search 5: " << (ht.search(5) ? "Found" : "Not Found") << endl;
      
          ht.remove(15);
          ht.display();
      
          return 0;
      }
      
    • 双重哈希:

      采用两个哈希函数,一个函数计算初始哈希值,另外一个函数计算哈希步长,遇到冲突就在初始值上加哈希步长。

      #include <iostream>
      #include <vector>
      
      using namespace std;
      
      class HashTable {
      private:
          vector<int> table;
          int size;
          int emptyValue;
      
          int hashFunction1(int key) {
              return key % size;
          }
      
          int hashFunction2(int key) {
              return 1 + (key % (size - 1));
          }
      
      public:
          HashTable(int s, int emptyVal = -1) : size(s), emptyValue(emptyVal) {
              table.resize(size, emptyValue);
          }
      
          void insert(int key) {
              int index = hashFunction1(key);
              int stepSize = hashFunction2(key);
              while (table[index] != emptyValue) {
                  index = (index + stepSize) % size;
              }
              table[index] = key;
          }
      
          void remove(int key) {
              int index = hashFunction1(key);
              int stepSize = hashFunction2(key);
              while (table[index] != emptyValue) {
                  if (table[index] == key) {
                      table[index] = emptyValue;
                      return;
                  }
                  index = (index + stepSize) % size;
              }
          }
      
          bool search(int key) {
              int index = hashFunction1(key);
              int stepSize = hashFunction2(key);
              while (table[index] != emptyValue) {
                  if (table[index] == key) {
                      return true;
                  }
                  index = (index + stepSize) % size;
              }
              return false;
          }
      
          void display() {
              for (int i = 0; i < size; ++i) {
                  if (table[i] != emptyValue) {
                      cout << "Index " << i << ": " << table[i] << endl;
                  } else {
                      cout << "Index " << i << ": " << "Empty" << endl;
                  }
              }
          }
      };
      
      int main() {
          HashTable ht(7);
      
          ht.insert(10);
          ht.insert(20);
          ht.insert(15);
          ht.insert(7);
      
          ht.display();
      
          cout << "Search 15: " << (ht.search(15) ? "Found" : "Not Found") << endl;
          cout << "Search 5: " << (ht.search(5) ? "Found" : "Not Found") << endl;
      
          ht.remove(15);
          ht.display();
      
          return 0;
      }
      

[!NOTE]

哈希只是一种算法,能实践哈希算法的载体有很多,但不变的都是数据映射,例如我们可以将数据映射在数组、map等等。

哈希的应用

1、重复查询(哈希集合)

  • 哈希集合集合 数据结构的实现之一,用于存储 非重复值
#include <iostream>
#include <unordered_set>

using namespace std;

int main() {
    // 创建一个无序集合
    unordered_set<int> elements;

    // 插入一些元素
    elements.insert(10);
    elements.insert(20);
    elements.insert(30);

    // 查询元素是否在集合中
    int queryElement = 20;
    if (elements.find(queryElement) != elements.end()) {
        cout << queryElement << " 在集合中。" << endl;
    } else {
        cout << queryElement << " 不在集合中。" << endl;
    }

    queryElement = 40;
    if (elements.find(queryElement) != elements.end()) {
        cout << queryElement << " 在集合中。" << endl;
    } else {
        cout << queryElement << " 不在集合中。" << endl;
    }

    return 0;
}
映射底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
map红黑树key有序key不可重复key不可修改O(log n)O(log n)
multimap红黑树key有序key可重复key不可修改O(log n)O(log n)
unordered_map哈希表key无序key不可重复key不可修改O(1)O(1)
  • 数独中的哈希判断是否符合

    重要公式:idx=(i/3)*3+(j/3);判断是位于哪个九宫格(下标从0开始)

    #include <iostream>
    #include <vector>
    #include <unordered_set>
    
    using namespace std;
    
    class Solution {
    public:
        bool isValidSudoku(vector<vector<char>>& board) {
            vector<unordered_set<int>> row(9), col(9), area(9);
    
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    char c = board[i][j];
                    if (c == '.') continue;
                    int u = c - '0';
                    int idx = (i / 3) * 3 + (j / 3);
    
                    if (row[i].count(u) || col[j].count(u) || area[idx].count(u)) 
                        return false;
    
                    row[i].insert(u);
                    col[j].insert(u);
                    area[idx].insert(u);
                }
            }
            return true;
        }
    };
    
    int main() {
        Solution solution;
        vector<vector<char>> board = {
            {'5', '3', '.', '.', '7', '.', '.', '.', '.'},
            {'6', '.', '.', '1', '9', '5', '.', '.', '.'},
            {'.', '9', '8', '.', '.', '.', '.', '6', '.'},
            {'8', '.', '.', '.', '6', '.', '.', '.', '3'},
            {'4', '.', '.', '8', '.', '3', '.', '.', '1'},
            {'7', '.', '.', '.', '2', '.', '.', '.', '6'},
            {'.', '6', '.', '.', '.', '.', '2', '8', '.'},
            {'.', '.', '.', '4', '1', '9', '.', '.', '5'},
            {'.', '.', '.', '.', '8', '.', '.', '7', '9'}
        };
    
        if (solution.isValidSudoku(board)) {
            cout << "The Sudoku board is valid." << endl;
        } else {
            cout << "The Sudoku board is invalid." << endl;
        }
    
        return 0;
    }
    
    

2、字符串哈希

采用两个大质数对哈希值进行取模能有效降低哈希碰撞。

typedef unsigned long long ull;
ull base = 131;
ull mod1 = 212370440130137957, mod2 = 1e9 + 7;

ull get_hash1(std::string s) {
  int len = s.size();
  ull ans = 0;
  for (int i = 0; i < len; i++) ans = (ans * base + (ull)s[i]) % mod1;
  return ans;
}

ull get_hash2(std::string s) {
  int len = s.size();
  ull ans = 0;
  for (int i = 0; i < len; i++) ans = (ans * base + (ull)s[i]) % mod2;
  return ans;
}

bool cmp(const std::string s, const std::string t) {
  bool f1 = get_hash1(s) != get_hash1(t);
  bool f2 = get_hash2(s) != get_hash2(t);
  return f1 || f2;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值