diff --git a/373. Find K Pairs with Smallest Sums/note.md b/373. Find K Pairs with Smallest Sums/note.md new file mode 100644 index 0000000..ea17d0b --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/note.md @@ -0,0 +1,44 @@ +# 373. Find K Pairs with Smallest Sums + +## step1 +時間計算量: O(klogk), 空間計算量: O(k) +全探索しか思い浮かばず、ACできなかった。 +回答をみて理解してその通りに実装(変数名のリネームはおこなった。) +大まかな思考の流れは以下の通り +1, 単純に全探索→ソートすると計算量がよくないので、改善のためにどのように比較をするかを考える +2, 実際に比較をするのは、最小のペア(nums1[index1], nums2[index2])を基準に考えると、次の最小値は(nums1[index1 + 1], nums2[index2], nums1[index1], nums2[index2 + 1])かもしくは、この以前のステップでのこったもののどれかとなる。 +3, 上記で以前のステップでのこったものを実現すること、常にソートされた状態でいることを考慮してHeapを使うという流れとなる。 +4, あとは追加の際に重複をさけるため、Setを用意して重複のIndexペアは弾くようにする。 + +## step2 +他の人の実装を参考に実装した。 +### step2-1 +参考:https://github.com/seal-azarashi/leetcode/pull/10/files +根本的な解法はstep1と同じだが、PriorityQueueにいれる中身を変えて、変数を少なくしたタイプ。変数宣言での記述量は増えるものの、繰り返しの部分は余計な合計などもなくわかりやすいかなとおもった。 + +### step2-2 +参考:[https://github.com/seal-azarashi/leetcode/pull/10/files](https://github.com/kazukiii/leetcode/pull/11/files) +実装していて、indexまわりをまとめて独自で定義したほうが見やすいなあとおもっていて同様のコメントが上記のPRでもあったので、独自定義する方針でも実装してみた。 + +## メモ +Loopの表現についてfor, whileの議論が他のPRのコメントでもあったが、宣言的か手続的のどちらがいいかというものがあり、今回の問題だと個人的にはどちらでもそこまで変わらないかなと思ったが問題や条件によってはどちらが自然かというのを常に意識したい。 +また問題文の制約次第ではループのなかでエラーとなる可能性があることも意識したい。 +https://github.com/sakupan102/arai60-practice/pull/11#discussion_r1622031840 +https://github.com/fhiyo/leetcode/pull/13/files + +## step3 +いただいたコメントを元に修正した。 +### step3-1 +アルゴリズムの修正を行なった。 +・2つの配列について片方の配列のIndexしか動かさない +・動かす方のindex = 0の時のみもう片方の配列のについてその配列のIndex+1の要素をQueueに追加 +することで重複がないようにおこなうことができる。 + +### step3-2 +独自クラスの定義のし直し、および共通ロジックを関数として外出しした。 +修正を行うと途端にどこが共通化されているのか、どこが無駄な処理になるのかがぼやけるので、全体の処理を頭に置きながら共通化部分をしっかり頭で描いてからコードにうつるように癖づけないといけないと思った。 +また、HashSetに独自クラスのオブジェクトをいれるときに、仕組みとしてどのように一致判定を行なっているかについて気になったので以下にまとめる。 + +・HashSetではAbstractSetをimplementしており、equalsとhashCodeを使ってObjectの一致判定をしている。 +・classは暗黙的にObjectクラスを継承しているのでequalsとhashCodeは必ずもっている。 +・独自クラスでかつ一致判定をカスタムしたい時は上記をOverrideする必要がある。 diff --git a/373. Find K Pairs with Smallest Sums/step1.java b/373. Find K Pairs with Smallest Sums/step1.java new file mode 100644 index 0000000..4eda67a --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/step1.java @@ -0,0 +1,28 @@ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + int length1 = nums1.length; + int length2 = nums2.length; + List> kSmallestPairs = new ArrayList<>(); + Set> visited = new HashSet<>(); + + PriorityQueue minSums = new PriorityQueue<>((a, b) -> a[0] - b[0]); + minSums.add(new int[] {nums1[0] + nums2[0], 0, 0}); + visited.add(new Pair(0, 0)); + + while(kSmallestPairs.size() < k) { + int[] currentMinPair = minSums.poll(); + int index1 = currentMinPair[1]; + int index2 = currentMinPair[2]; + kSmallestPairs.add(List.of(nums1[index1], nums2[index2])); + if (index1 + 1 < length1 && !visited.contains(new Pair(index1 + 1, index2))) { + minSums.add(new int[] {nums1[index1 + 1] + nums2[index2], index1 + 1, index2}); + visited.add(new Pair(index1 + 1, index2)); + } + if (index2 + 1 < length2 && !visited.contains(new Pair(index1, index2 + 1))) { + minSums.add(new int[] {nums1[index1] + nums2[index2 + 1], index1, index2 + 1}); + visited.add(new Pair(index1, index2 + 1)); + } + } + return kSmallestPairs; + } +} diff --git a/373. Find K Pairs with Smallest Sums/step2-1.java b/373. Find K Pairs with Smallest Sums/step2-1.java new file mode 100644 index 0000000..a0c51d9 --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/step2-1.java @@ -0,0 +1,28 @@ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + int length1 = nums1.length; + int length2 = nums2.length; + List> kSmallestPairs = new ArrayList<>(); + Set> visited = new HashSet<>(); + + PriorityQueue minSortedPairs = new PriorityQueue<>((a, b) -> (nums1[a[0]] + nums2[a[1]]) - (nums1[b[0]] + nums2[b[1]])); + minSortedPairs.add(new int[] {0, 0}); + visited.add(new Pair(0, 0)); + + while(kSmallestPairs.size() < k) { + int[] currentMinPair = minSortedPairs.poll(); + int index1 = currentMinPair[0]; + int index2 = currentMinPair[1]; + kSmallestPairs.add(List.of(nums1[index1], nums2[index2])); + if (index1 + 1 < length1 && !visited.contains(new Pair(index1 + 1, index2))) { + minSortedPairs.add(new int[] {index1 + 1, index2}); + visited.add(new Pair(index1 + 1, index2)); + } + if (index2 + 1 < length2 && !visited.contains(new Pair(index1, index2 + 1))) { + minSortedPairs.add(new int[] {index1, index2 + 1}); + visited.add(new Pair(index1, index2 + 1)); + } + } + return kSmallestPairs; + } +} diff --git a/373. Find K Pairs with Smallest Sums/step2-2.java b/373. Find K Pairs with Smallest Sums/step2-2.java new file mode 100644 index 0000000..e64f497 --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/step2-2.java @@ -0,0 +1,40 @@ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + int length1 = nums1.length; + int length2 = nums2.length; + List> kSmallestPairs = new ArrayList<>(); + Set> visited = new HashSet<>(); + + PriorityQueue minSortedPairs = new PriorityQueue<>((a, b) -> (a.sum - b.sum)); + minSortedPairs.add(new SumPair(nums1, 0, nums2, 0)); + visited.add(new Pair(0, 0)); + + for(int i = 0; i < k; i++) { + SumPair currentMinPair = minSortedPairs.poll(); + int index1 = currentMinPair.indice.getKey(); + int index2 = currentMinPair.indice.getValue(); + kSmallestPairs.add(List.of(currentMinPair.elements.getKey(), currentMinPair.elements.getValue())); + if (index1 + 1 < length1 && !visited.contains(new Pair(index1 + 1, index2))) { + minSortedPairs.add(new SumPair(nums1, index1 + 1, nums2, index2)); + visited.add(new Pair(index1 + 1, index2)); + } + if (index2 + 1 < length2 && !visited.contains(new Pair(index1, index2 + 1))) { + minSortedPairs.add(new SumPair(nums1, index1, nums2, index2 + 1)); + visited.add(new Pair(index1, index2 + 1)); + } + } + return kSmallestPairs; + } +} + +class SumPair { + int sum; + Pair elements; + Pair indice; + + SumPair(int[] nums1, int index1, int[] nums2, int index2) { + this.sum = nums1[index1] + nums2[index2]; + this.elements = new Pair(nums1[index1], nums2[index2]); + this.indice = new Pair(index1, index2); + } +} diff --git a/373. Find K Pairs with Smallest Sums/step3-1.java b/373. Find K Pairs with Smallest Sums/step3-1.java new file mode 100644 index 0000000..b4d6bb2 --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/step3-1.java @@ -0,0 +1,24 @@ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + int length1 = nums1.length; + int length2 = nums2.length; + List> kSmallestPairs = new ArrayList<>(); + + PriorityQueue minSums = new PriorityQueue<>((a, b) -> a[0] - b[0]); + minSums.add(new int[] { nums1[0] + nums2[0], 0, 0 }); + + while (kSmallestPairs.size() < k) { + int[] totalAndIndexes = minSums.poll(); + int index1 = totalAndIndexes[1]; + int index2 = totalAndIndexes[2]; + kSmallestPairs.add(List.of(nums1[index1], nums2[index2])); + if (index2 == 0 && index1 + 1 < length1) { + minSums.add(new int[] { nums1[index1 + 1] + nums2[index2], index1 + 1, index2 }); + } + if (index2 + 1 < length2) { + minSums.add(new int[] { nums1[index1] + nums2[index2 + 1], index1, index2 + 1 }); + } + } + return kSmallestPairs; + } +} diff --git a/373. Find K Pairs with Smallest Sums/step3-2.java b/373. Find K Pairs with Smallest Sums/step3-2.java new file mode 100644 index 0000000..ee74b4f --- /dev/null +++ b/373. Find K Pairs with Smallest Sums/step3-2.java @@ -0,0 +1,64 @@ +class Solution { + int[] nums1; + int[] nums2; + + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + this.nums1 = nums1; + this.nums2 = nums2; + List> kSmallestPairs = new ArrayList<>(); + Set visited = new HashSet<>(); + PriorityQueue minSortedPairs = new PriorityQueue<>((a, b) -> (a.sum - b.sum)); + IndexPair firstPair = new IndexPair(0, 0, nums1[0] + nums2[0]); + minSortedPairs.add(firstPair); + visited.add(firstPair); + + for(int i = 0; i < k; i++) { + IndexPair currentMinPair = minSortedPairs.poll(); + int index1 = currentMinPair.index1; + int index2 = currentMinPair.index2; + kSmallestPairs.add(List.of(nums1[index1], nums2[index2])); + insertPairIfNeeded(minSortedPairs, visited, index1 + 1, index2); + insertPairIfNeeded(minSortedPairs, visited, index1, index2 + 1); + } + return kSmallestPairs; + } + + private void insertPairIfNeeded(PriorityQueue queue, Set visited, int pos1, int pos2) { + int len1 = nums1.length; + int len2 = nums2.length; + if (len1 <= pos1 || len2 <= pos2) { + return; + } + IndexPair newPair = new IndexPair(pos1, pos2, nums1[pos1] + nums2[pos2]); + if (visited.contains(newPair)) { + return; + } + queue.add(newPair); + visited.add(newPair); + } +} + +class IndexPair { + int sum; + int index1; + int index2; + + IndexPair(int index1, int index2, int sum) { + this.index1 = index1; + this.index2 = index2; + this.sum = sum; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IndexPair other = (IndexPair) o; + return other.index1 == index1 && other.index2 == index2; + } + + @Override + public int hashCode() { + return Objects.hash(index1, index2); + } +}