[英]Counting Inversions using TreeSet Java
我正在使用treeSet
來解決計數反轉問題。 我正在使用以下方法,它使用在 O(logn) 時間內工作的gnu_pbds
。
Ordered_Set
中插入數組的第一個元素。arr[]
中的所有剩余元素,請執行以下操作:Ordered_Set
中插入當前元素。order_of_key
(arr[i]+1) 在Ordered_Set
中找到嚴格小於當前元素 + 1 的元素數。Ordered_Set
和order_of_key
(current_element + 1) 的大小之間的差異將給出當前元素的反轉計數。對於
order_of_key
方法,我使用 TreeSet 的耳機(k)方法進行計算。
public class Solution {
static int order_of_key(TreeSet<Integer> s, int k)
{
return s.headSet(k,true).size();
}
public int solve(ArrayList<Integer> a) {
TreeSet<Integer> s = new TreeSet<>();
s.add(a.get(0) );
int invcount = 0;
for(int i=1;i<a.size();i++)
{
s.add(a.get(i) );
int key = order_of_key(s, a.get(i)+1);
// if(i+1 == a.size() ) key--;
invcount+= s.size() - key;
// System.out.println(s+" "+(a.get(i)+1)+" "+ key+" "+invcount);
}
return invcount;
}
}
// Ordered set in GNU C++ based
// approach for inversion count
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
// Ordered Set Tree
typedef tree<int, null_type, less_equal<int>,
rb_tree_tag,
tree_order_statistics_node_update>
ordered_set;
// Returns inversion count in
// arr[0..n-1]
void print(ordered_set s,int n){
for(int i=0;i<n;i++)
{
// printf("%d ",s[i]);// <<endl;
cout << *(s.find_by_order(i))
<< " ";
}
//cout<<endl;
}
int getInvCount(int arr[], int n)
{
int key;
// Intialise the ordered_set
ordered_set set1;
// Insert the first
// element in set
set1.insert(arr[0]);
// Intialise inversion
// count to zero
int invcount = 0;
// Finding the inversion
// count for current element
for (int i = 1; i < n; i++) {
set1.insert(arr[i]);
// Number of elements strictly
// less than arr[i]+1
key = set1.order_of_key(arr[i] + 1);
// Difference between set size
// and key will give the
// inversion count
invcount += set1.size() - key;
print(set1,n);
cout<<arr[i]+1<<" ";
cout<<key<<" ";
cout<<" "<<invcount<<endl;
}
return invcount;
}
// Driver's Code
int main()
{
int arr[] = { 32, 35, 43, 1, 38, 39, 42 };
int n = sizeof(arr) / sizeof(int);
cout<<n<<endl;
// Function call to count
// inversion
cout << getInvCount(arr, n);
return 0;
}
1- inversion count
。 如果我將order_of_key
的工作更改為以下內容,它會破壞其他一些測試用例。int order_of_key(TreeSet<Integer> s, int k)
{
if(s.contains(k))
return s.headSet(k).size();
else
return s.headSet(k,true).size();
}
gnu_pbds
有效。 請幫助我使用TreeSet
修復此代碼。 另外,如果我遺漏了關於pbds
order_of_key 方法的其他幾點,請告訴我。
order_of_key
function 返回小於提供的鍵的元素數。 在您的 C++ 代碼中,您將元素添加到集合中,然后調用order_of_key
元素加一,以包含您剛剛添加的元素。
在 Java 代碼中保留這種安排,使用 Java TreeSet 忠實地實現類似order_of_key
的方法是調用
return s.headSet(k, false).size();
或者干脆
return s.headSet(k).size();
因為其中任何一個都返回包含嚴格小於k
元素的頭集的大小。 無需先進行包含檢查。 當我使用 C++ 代碼中的輸入數組運行它時,我得到相同的中間結果和最終結果的反轉次數:6。
請注意,Java 的 TreeSet 還包括創建尾集的能力,即大於提供的元素的元素。 事實證明(在我看來)這是一種更簡單的計算反轉次數的方法。 在添加元素之前,大於當前元素的尾集的大小是相對於該元素的反轉次數。 由於我們希望尾集嚴格大於當前元素,因此我們使用tailSet(k, false)
。 然后我們可以對輸入列表中的每個元素執行此操作:
int inversions(List<Integer> a) {
var s = new TreeSet<Integer>();
int invcount = 0;
for (int k : a) {
invcount += s.tailSet(k, false).size();
s.add(k);
}
return invcount;
}
inversions(List.of(32, 35, 43, 1, 38, 39, 42)) // result is 6
更新 2020-06-24
上面的代碼僅適用於具有唯一值的輸入。 如果輸入具有重復值,則它不起作用。 我注意到在 C++ 代碼中,使用less_equal<int>
比較的樹 function 保留重復項,而使用less<int>
壓縮重復項。
保持重復很重要的原因是每個元素——即使它是重復的——都可以算作倒置。 因此,[2, 2, 1] 的輸入被認為有兩個反轉。 Java TreeSet 壓縮了重復項,因此我們必須做一些額外的工作來保存它們。
允許“重復” int 值的一種方法是以某種方式使它們唯一。 這可以通過創建一個新的 object 來包含與始終遞增的計數器配對的 int 值來完成。 重復值是唯一的,因為它們將具有不同的計數器值。 這是執行此操作的 class:
static class UniqInt implements Comparable<UniqInt> {
static int count = 0;
final int value;
final int uniq;
UniqInt(int value) { this.value = value; uniq = count++; }
public int compareTo(UniqInt other) {
int c = Integer.compare(this.value, other.value);
return c != 0 ? c : Integer.compare(this.uniq, other.uniq);
}
}
請注意,這里的compareTo
方法比較值和“uniq”計數器值,因此創建多個 UniqInt 實例將彼此不同,並且將完全有序。 一旦我們有了這個 class,我們基本上做同樣的事情,除了我們在 TreeSet 中跟蹤 UniqInt 而不是 Integer 對象。
int inversions1(List<Integer> a) {
var s = new TreeSet<UniqInt>();
int invcount = 0;
for (int k : a) {
var u = new UniqInt(k);
invcount += s.tailSet(u, false).size();
s.add(u);
}
return invcount;
}
對於評論中提供的輸入(包括重復值),
84, 2, 37, 3, 67, 82, 19, 97, 91, 63, 27, 6, 13, 90, 63, 89, 100, 60, 47, 96, 54, 26, 64, 50, 71, 16、6、40、84、93、67、85、16、22、60
這給出了 290 的預期結果。
然而,這有點脆弱。 請注意,創建具有相同值的新 UniqInt 始終會創建一個大於任何現有實例的實例,因為計數器始終遞增。 (直到你創建了 2^31 個。)當新實例被創建時,它的尾部將永遠不會包含任何已經在 TreeSet 中的重復值。 這對於這個小例子來說可能是合理的,但如果這是一個更大系統的一部分,我會更仔細地考慮如何獲得相對於某個值的正確的 head-set 或 tail-set,而不必依賴大多數最近創建的 UniqInt 比所有以前的都大。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.