web-dev-qa-db-ja.com

HashSet <T>にハッシュを実装する方法は?

インタビュー中にRustを使用して上手くいくために、リートコードのいくつかの問題に取り組んでいます。解決する最初の試みとして この問題 として、トリプレットソリューションを表すことを考えました_a + b + c = 0_ abcを_solution: HashSet<i32>_に格納し、その_solution: HashSet<i32>_を別のコレクションに格納する_solution_set: HashSet<HashSet<i32>>_。クレイジーですね。

演習では、冗長なトリプレットは適格ではないことが明示されているため、トリプレットを_solution: Vec<i32>_ sに格納するのではなく、順序によってVecのハッシュ値が変更される可能性があるため、トリプレットを_solution: HashSet<i32>_に格納すると、 ab、およびcのうち、同じsolutionに解決されます。さらに、トリプレットが_solution_set: HashSet<HashSet<i32>>_に存在するかどうかをチェックするのではなく、O(1)ではなく_solution_set: Vec<HashSet<i32>>_にトリプレットがすでに存在することを確認するのがO(n)の時間になります。最後に、戻り値が_Vec<Vec<i32>>_であることはわかっていますが、_solution: HashSet<i32>_を_Vec<i32>_にdrain()し、結果の_Iter<Vec<i32>>_を_Vec<Vec<i32>>_。

_HashSet<T>_はHashを実装していないことを認識しているので、自分で試してみることにしましたが、今はクリークのないパドルになっています。 ????構造体にHashを実装する方法を学ぶために here を調べ、所有していない構造体に特性を実装する方法を学ぶために here を調べましたが、今はHashSetHashSetWrapperから必要なすべての関数ハンドルを実装する(new()drain()insert()など)。コンパイラーはまた、PartialEqのような他の特性についてもコンパイルしているので、私は実際にこれについてPandoraのボックスを開きました。これは、これを行うための最も「さびた」方法ではないように感じます。

また、ハッシュを正しく実装することは簡単ではないことも知っています。これはベストプラクティスの取り組みであるため、ソリューションを実装するための最も「錆ついた」方法を理解するための手助けが欲しいのです。実際にはまだ動作していませんが、ここまでのコードは次のとおりです。

_use std::collections::HashSet;
use std::hash::{Hash, Hasher};

#[derive(PartialEq)]
struct HashSetWrapper<T>(HashSet<T>);

impl<T: Hash> HashSetWrapper<T> {
    fn new() -> Self {
        HashSetWrapper(HashSet::<T>::new())
    }

    fn insert(&self, value: T) {
        self.0.insert(value);
    }
}

impl<T: Hash> Hash for HashSetWrapper<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for value in &self.0 {
            value.hash(state);
        }
    }
}

impl Solution {
    pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {

        let mut solution_set: HashSetWrapper<HashSet<i32>> = HashSetWrapper::new();

        for (i, a) in nums[0..(nums.len() - 2)].iter().enumerate() {
            for (j, b) in nums[i..(nums.len() - 1)].iter().enumerate() {
                for c in nums[j..].iter() {
                    if a + b + c == 0 { 
                        let mut temp = HashSet::<i32>::new();
                        temp.insert(*a);
                        temp.insert(*b);
                        temp.insert(*c);
                        solution_set.insert(temp); }
                }
            }
        }
        solution_set.drain().map(|inner_set| inner_set.drain().collect::<Vec<_>>()).collect::<Vec<_>>()
    }
}
_

それでもラッパークラスのdrain()を実装する必要がありますが、正しい方向に進んでいるかどうかもわかりません。この問題をどのように解決しますか? HashHashSetをどのように実装しますか?知りたい!

以下はコンパイラが私に与えているエラーです:

_Line 5, Char 26: binary operation `==` cannot be applied to type `std::collections::HashSet<T>` (solution.rs)
  |
5 | struct HashSetWrapper<T>(HashSet<T>);
  |                          ^^^^^^^^^^
  |
  = note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashSet<T>`
Line 5, Char 26: binary operation `!=` cannot be applied to type `std::collections::HashSet<T>` (solution.rs)
  |
5 | struct HashSetWrapper<T>(HashSet<T>);
  |                          ^^^^^^^^^^
  |
  = note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashSet<T>`
Line 9, Char 38: no function or associated item named `new` found for type `std::collections::HashSet<T>` in the current scope (solution.rs)
   |
9 |         HashSetWrapper(HashSet::<T>::new())
   |                                      ^^^ function or associated item not found in `std::collections::HashSet<T>`
   |
   = note: the method `new` exists but the following trait bounds were not satisfied:
           `T : std::cmp::Eq`
Line 13, Char 16: no method named `insert` found for type `std::collections::HashSet<T>` in the current scope (solution.rs)
   |
13 |         self.0.insert(value);
   |                ^^^^^^ method not found in `std::collections::HashSet<T>`
   |
   = note: the method `insert` exists but the following trait bounds were not satisfied:
           `T : std::cmp::Eq`
Line 28, Char 62: the trait bound `std::collections::HashSet<i32>: std::hash::Hash` is not satisfied (solution.rs)
   |
8  |     fn new() -> Self {
   |     ---------------- required by `HashSetWrapper::<T>::new`
...
28 |         let mut solution_set: HashSetWrapper<HashSet<i32>> = HashSetWrapper::new();
   |                                                              ^^^^^^^^^^^^^^^^^^^ the trait `std::hash::Hash` is not implemented for `std::collections::HashSet<i32>`
Line 38, Char 38: no method named `insert` found for type `HashSetWrapper<std::collections::HashSet<i32>>` in the current scope (solution.rs)
   |
5  | struct HashSetWrapper<T>(HashSet<T>);
   | ------------------------------------- method `insert` not found for this
...
38 |                         solution_set.insert(temp); }
   |                                      ^^^^^^ method not found in `HashSetWrapper<std::collections::HashSet<i32>>`
   |
   = note: the method `insert` exists but the following trait bounds were not satisfied:
           `std::collections::HashSet<i32> : std::hash::Hash`
Line 42, Char 22: no method named `drain` found for type `HashSetWrapper<std::collections::HashSet<i32>>` in the current scope (solution.rs)
   |
5  | struct HashSetWrapper<T>(HashSet<T>);
   | ------------------------------------- method `drain` not found for this
...
42 |         solution_set.drain().map(|inner_set| inner_set.drain().collect::<Vec<_>>()).collect::<Vec<_>>()
   |                      ^^^^^ method not found in `HashSetWrapper<std::collections::HashSet<i32>>`
error: aborting due to 7 previous errors
_
3
Makenbaccon

私はあなたのコードと人々のコメントを調べました。 HashSet<i32>を使用すると、複雑すぎて、HashSetWrapperの特性関数をすべて実装する必要があると思います。より単純なバージョンは、トリプレットを保持する単純な構造体を持ち、マクロを使用してHashEqおよびPartialEqから派生させることです。重複除外を自動的に機能させるために、トリプレットを以前のコメントとしてソートできます。

以下は、あなたのthree_sum実装のロジックに大まかに従う私のコードです(バグがあります)。

#[derive(Hash, Eq, PartialEq, Debug)]
pub struct Triplet {
    x: i32,
    y: i32,
    z: i32,
}

impl Triplet {
    pub fn new(x: i32, y: i32, z: i32) -> Triplet {
        let mut v = vec![x, y, z];
        v.sort();
        Triplet {
            x: v[0],
            y: v[1],
            z: v[2],
        }
    }

    pub fn to_vec(&self) -> Vec<i32> {
        vec![self.x, self.y, self.z]
    }
}

pub fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
    let mut res: HashSet<Triplet> = HashSet::new();
    for (i, a) in nums[0..(nums.len() - 2)].iter().enumerate() {
        for (j, b) in nums[i+1..(nums.len() - 1)].iter().enumerate() {
            for c in nums[j+1..].iter() {
                if a + b + c == 0 {
                    let triplet = Triplet::new(*a, *b, *c);
                    res.insert(triplet);
                }
            }
        }
    }
    res.into_iter().map(|t| t.to_vec()).collect()
}

テストコード:

    #[test]
    fn test_three_sum() {
        let result = vec![vec![-1, -1, 2], vec![-1, 0, 1]];
        assert_eq!(three_sum(vec![-1, 0, 1, 2, -1, -4]), result)
    }

結果:

running 1 test
test tests::test_three_sum ... ok
1
Dat Nguyen