Skip to main content

rust_algorithms/data_structures/
hash_table.rs

1//! Hash table (xesh-jadval) — kalit → qiymat, o'rtacha O(1).
2
3use std::collections::hash_map::DefaultHasher;
4use std::hash::{Hash, Hasher};
5
6/// Zanjirlash (separate chaining) usulidagi xesh-jadval.
7///
8/// **G'oya:** kalitni xesh funksiyasi orqali songa aylantiramiz, uni chelaklar
9/// soniga qoldiqli bo'lamiz va o'sha chelakka yozamiz. Ikki kalit bitta chelakka
10/// tushsa ("to'qnashuv"), ular bitta ro'yxatda yonma-yon yashaydi.
11///
12/// ```text
13///  "olma" ─hash─> 1743 ─% 8─> chelak 7 ─> [("olma", 5)]
14///  "anor" ─hash─>  911 ─% 8─> chelak 7 ─> [("olma", 5), ("anor", 3)]  ← to'qnashuv
15/// ```
16///
17/// **Yuklama koeffitsienti (load factor)** 0.75 dan oshsa, chelaklar soni ikki
18/// barobar oshiriladi va hamma element qayta joylashtiriladi (rehash).
19///
20/// | Amal | O'rtacha | Eng yomon |
21/// |---|---|---|
22/// | `insert` / `get` / `remove` | O(1) | O(n) (hamma kalit bitta chelakda) |
23///
24/// > Amalda `std::collections::HashMap` dan foydalaning — u DoS hujumlaridan
25/// > himoyalangan (SipHash) va ancha optimallashtirilgan.
26///
27/// # Misol
28/// ```
29/// use rust_algorithms::data_structures::HashTable;
30///
31/// let mut m = HashTable::new();
32/// m.insert("olma", 5);
33/// m.insert("anor", 3);
34/// assert_eq!(m.get(&"olma"), Some(&5));
35///
36/// m.insert("olma", 10); // ustiga yozadi
37/// assert_eq!(m.get(&"olma"), Some(&10));
38/// assert_eq!(m.len(), 2);
39///
40/// assert_eq!(m.remove(&"anor"), Some(3));
41/// assert_eq!(m.get(&"anor"), None);
42/// ```
43#[derive(Debug, Clone)]
44pub struct HashTable<K, V> {
45    buckets: Vec<Vec<(K, V)>>,
46    len: usize,
47}
48
49impl<K: Hash + Eq, V> Default for HashTable<K, V> {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl<K: Hash + Eq, V> HashTable<K, V> {
56    const BOSHLANGICH: usize = 8;
57    const MAX_YUKLAMA: f64 = 0.75;
58
59    /// Bo'sh jadval yaratadi.
60    pub fn new() -> Self {
61        Self {
62            buckets: (0..Self::BOSHLANGICH).map(|_| Vec::new()).collect(),
63            len: 0,
64        }
65    }
66
67    /// Kalit-qiymat qo'shadi. Kalit avval bo'lgan bo'lsa, eski qiymat qaytadi.
68    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
69        if (self.len + 1) as f64 > self.buckets.len() as f64 * Self::MAX_YUKLAMA {
70            self.rehash();
71        }
72        let idx = self.bucket_index(&key);
73        for (k, v) in self.buckets[idx].iter_mut() {
74            if *k == key {
75                return Some(std::mem::replace(v, value));
76            }
77        }
78        self.buckets[idx].push((key, value));
79        self.len += 1;
80        None
81    }
82
83    /// Kalit bo'yicha qiymatni oladi.
84    pub fn get(&self, key: &K) -> Option<&V> {
85        let idx = self.bucket_index(key);
86        self.buckets[idx]
87            .iter()
88            .find(|(k, _)| k == key)
89            .map(|(_, v)| v)
90    }
91
92    /// Qiymatni o'zgartirish uchun havola.
93    pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
94        let idx = self.bucket_index(key);
95        self.buckets[idx]
96            .iter_mut()
97            .find(|(k, _)| k == key)
98            .map(|(_, v)| v)
99    }
100
101    /// Kalit mavjudmi?
102    pub fn contains_key(&self, key: &K) -> bool {
103        self.get(key).is_some()
104    }
105
106    /// Kalitni o'chiradi va qiymatini qaytaradi.
107    pub fn remove(&mut self, key: &K) -> Option<V> {
108        let idx = self.bucket_index(key);
109        let pos = self.buckets[idx].iter().position(|(k, _)| k == key)?;
110        self.len -= 1;
111        Some(self.buckets[idx].swap_remove(pos).1)
112    }
113
114    /// Juftliklar soni.
115    pub fn len(&self) -> usize {
116        self.len
117    }
118
119    /// Bo'shmi?
120    pub fn is_empty(&self) -> bool {
121        self.len == 0
122    }
123
124    /// Barcha juftliklar bo'ylab iterator (tartib kafolatlanmaydi).
125    pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
126        self.buckets
127            .iter()
128            .flat_map(|b| b.iter().map(|(k, v)| (k, v)))
129    }
130
131    fn bucket_index(&self, key: &K) -> usize {
132        let mut hasher = DefaultHasher::new();
133        key.hash(&mut hasher);
134        (hasher.finish() % self.buckets.len() as u64) as usize
135    }
136
137    /// Chelaklar sonini ikki barobar oshirib, hammani qayta joylashtiradi.
138    fn rehash(&mut self) {
139        let yangi_hajm = self.buckets.len() * 2;
140        let eski = std::mem::replace(
141            &mut self.buckets,
142            (0..yangi_hajm).map(|_| Vec::new()).collect(),
143        );
144        for chelak in eski {
145            for (k, v) in chelak {
146                let idx = self.bucket_index(&k);
147                self.buckets[idx].push((k, v));
148            }
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156    use std::collections::HashMap;
157
158    #[test]
159    fn asosiy_amallar() {
160        let mut m = HashTable::new();
161        assert!(m.is_empty());
162        assert_eq!(m.insert("a", 1), None);
163        assert_eq!(m.insert("a", 2), Some(1));
164        assert_eq!(m.len(), 1);
165        assert_eq!(m.get(&"a"), Some(&2));
166        assert!(m.contains_key(&"a"));
167        assert_eq!(m.remove(&"a"), Some(2));
168        assert_eq!(m.remove(&"a"), None);
169        assert!(m.is_empty());
170    }
171
172    #[test]
173    fn get_mut_ishlaydi() {
174        let mut m = HashTable::new();
175        m.insert(1, String::from("bir"));
176        m.get_mut(&1).unwrap().push_str("inchi");
177        assert_eq!(m.get(&1).map(String::as_str), Some("birinchi"));
178    }
179
180    #[test]
181    fn std_hashmap_bilan_bir_xil_ishlaydi() {
182        let mut ours = HashTable::new();
183        let mut theirs = HashMap::new();
184
185        for i in 0..1000i32 {
186            let kalit = format!("kalit-{}", i * 7 % 337);
187            assert_eq!(ours.insert(kalit.clone(), i), theirs.insert(kalit, i));
188        }
189        assert_eq!(ours.len(), theirs.len());
190
191        for (k, v) in theirs.iter() {
192            assert_eq!(ours.get(k), Some(v));
193        }
194
195        for k in theirs.keys() {
196            assert!(ours.contains_key(k));
197        }
198    }
199
200    #[test]
201    fn rehashdan_keyin_hamma_kalit_joyida() {
202        let mut m = HashTable::new();
203        for i in 0..500 {
204            m.insert(i, i * i);
205        }
206        assert_eq!(m.len(), 500);
207        for i in 0..500 {
208            assert_eq!(m.get(&i), Some(&(i * i)));
209        }
210        assert_eq!(m.iter().count(), 500);
211    }
212}