A Dynamic Set is an Abstract Data Type (ADT) that can support the following operations:
(1) search for an item to determine whether the item exists in the set;
(2) insert a new item into the set;
(3) delete a current item from the set.
1. Binary Search Tree
All the operations mentioned above have a time performance of O(h) in a Binary Search Tree (BST), where h is the height of the BST. In order to maintain the balance of such a binary tree and thus gain a relatively good time performance, sophisticated strategies are exploited in AVL Tree, Red-Black Tree and other advanced data structures. Here I would only provide a Java class of a naive binary search tree.
1 class BST<T extends Comparable> { 2 private class Node<T> { 3 // Node of Binary Search Tree 4 public Node<T> left, right; 5 public T data; 6 } 7 8 private Node<T> root; // root of Binary Search Tree 9 private int minIdx; // index aimed to help ithMin 10 11 public boolean search(T item) { 12 // Search for an item starting from the root 13 // return whether such an item exists 14 return srchHelp(root,item)!=null; 15 } 16 private Node<T> srchHelp(Node<T> sub,T item) { 17 // Search a Node that contains item as its data 18 // return such a Node of the minimum depth 19 // if no such Node exists, return null 20 if (sub==null||sub.data.equals(item)) { 21 return sub; 22 } else if (sub.data.compareTo(item)<0) { 23 return srchHelp(sub.right,item); 24 } else { 25 return srchHelp(sub.left,item); 26 } 27 } 28 public void insert(T item) { 29 // Insert an item to BST starting from root 30 root = insHelp(root, item); 31 } 32 private Node<T> insHelp(Node<T> sub,T item) { 33 // Insert an item to a subtree recursively 34 if (sub==null) { 35 sub = new Node<T>(); 36 sub.data = item; 37 } else if (item.compareTo(sub.data)<0) { 38 sub.left = insHelp(sub.left,item); 39 } else { 40 sub.right = insHelp(sub.right,item); 41 } 42 return sub; 43 } 44 public void delete(T item) { 45 // Delete a Node with data item from BST 46 root = delHelp(root,item); 47 } 48 public Node<T> delHelp(Node<T> sub,T item) { 49 // Search an item and ultimately delete it 50 if (sub==null) { 51 return null; 52 } else if (item.compareTo(sub.data)==0) { 53 sub = delNode(sub); 54 } else if (item.compareTo(sub.data)<0) { 55 sub.left = delHelp(sub.left,item); 56 } else { 57 sub.right = delHelp(sub.right,item); 58 } 59 return sub; 60 } 61 private Node<T> delNode(Node<T> sub) { 62 // Delete the Node sub given sub!=null 63 if (sub.right==null) { 64 return sub.left; 65 } else if (sub.left==null) { 66 return sub.right; 67 } else if (sub.right.left==null) { 68 sub.data = sub.right.data; 69 sub.right = sub.right.right; 70 return sub; 71 } else { 72 Node<T> prev = sub.right; 73 while (prev.left.left!=null) { 74 prev = prev.left; 75 } 76 sub.data = prev.left.data; 77 prev.left = prev.left.right; 78 return sub; 79 } 80 } 81 public void delLessThan(T item) { 82 // Delete items less than item starting from root 83 root = delLessThanHelp(root,item); 84 } 85 private Node<T> delLessThanHelp(Node<T> sub,T item) { 86 // Delete items less than item from a subtree 87 if (sub==null) { 88 return null; 89 } else if (sub.data.compareTo(item)<0) { 90 sub.left = delLessThanHelp(sub.left,item); 91 return delLessThanHelp(sub.right,item); 92 } else { 93 sub.left = delLessThanHelp(sub.left,item); 94 return sub; 95 } 96 } 97 public void delGreaterThan(T item) { 98 // Delete items greater than item starting from root 99 root = delGreaterThanHelp(root,item); 100 } 101 private Node<T> delGreaterThanHelp(Node<T> sub,T item) { 102 // Delete items greater than item from a subtree 103 if (sub==null) { 104 return null; 105 } else if (sub.data.compareTo(item)>0) { 106 sub.right = delGreaterThanHelp(sub.right,item); 107 return delGreaterThanHelp(sub.left,item); 108 } else { 109 sub.right = delGreaterThanHelp(sub.right,item); 110 return sub; 111 } 112 } 113 public void delInterval(T a, T b) { 114 // Delete items greater than a && less than b 115 root = delIntervalHelp(root,a,b); 116 } 117 public Node<T> delIntervalHelp(Node<T> sub,T a,T b) { 118 // Delete items belonging to (a,b) from a subtree 119 if (a.compareTo(b)>=0) { 120 return sub; 121 } else if (sub==null) { 122 return null; 123 } else if (sub.data.compareTo(a)<=0) { 124 sub.right = delIntervalHelp(sub.right,a,b); 125 } else if (sub.data.compareTo(b)>=0) { 126 sub.left = delIntervalHelp(sub.left,a,b); 127 } else { 128 sub.left = delIntervalHelp(sub.left,a,b); 129 sub.right = delIntervalHelp(sub.right,a,b); 130 sub = delNode(sub); 131 } 132 return sub; 133 } 134 public T ithMin(int idx) { 135 // Return the idx-th least item in BST 136 if (idx<=0) { 137 return null; 138 } else { 139 minIdx = idx; 140 return ithMinHelp(root); 141 } 142 } 143 private T ithMinHelp(Node<T> sub) { 144 // Return the minIdx-th least item recursively 145 if (sub==null) { 146 return null; 147 } else { 148 T val = ithMinHelp(sub.left); 149 if (minIdx<=0) { 150 // counting has been terminated 151 return val; 152 } else if (--minIdx==0) { 153 // termination of counting 154 return sub.data; 155 } else { 156 // counting is going on 157 return ithMinHelp(sub.right); 158 } 159 } 160 } 161 public void display() { 162 inOrderTraverse(root); 163 System.out.println(); 164 } 165 private void inOrderTraverse(Node<T> sub) { 166 if (sub!=null) { 167 inOrderTraverse(sub.left); 168 System.out.print(" "+sub.data); 169 inOrderTraverse(sub.right); 170 } 171 } 172 }
2. Hash Table
In a Hash Table, a hash function maps an item to its proper slot in the table and thus each set operation can gain a time performance of O(1). To tackle the problem of what is called "conflict", we can resort to either Open Hashing or Chaining. Here, I used a chaining hash table to solve a problem from SJTU ACM Online Judge,
1 import java.util.*; 2 3 class Hash { 4 private class Node { 5 public int data; 6 public int cnt = 1; 7 public Node next; 8 } 9 10 private final int SIZE = 100003; 11 private Node[] tbl; 12 13 public Hash() { 14 tbl = new Node[SIZE]; 15 for (int i=0;i<SIZE;i++) { 16 tbl[i] = new Node(); 17 } 18 } 19 private int index(int item) { 20 // Hash function using division method 21 if (item%SIZE<0) { 22 return item%SIZE+SIZE; 23 } else { 24 return item%SIZE; 25 } 26 } 27 public void insert(int item) { 28 // Insert an item to the hash table 29 Node itr = tbl[index(item)]; 30 while (itr.next!=null) { 31 if (itr.next.data==item) { 32 itr.next.cnt++; 33 return; 34 } else { 35 itr = itr.next; 36 } 37 } 38 itr.next = new Node(); 39 itr.next.data = item; 40 } 41 public int search(int item) { 42 // Search an item in the hash table 43 // return its count value 44 Node itr = tbl[index(item)].next; 45 while (itr!=null) { 46 if (itr.data==item) { 47 return itr.cnt; 48 } else { 49 itr = itr.next; 50 } 51 } 52 return 0; 53 } 54 } 55 56 public class Main { 57 public static void main(String[] args) { 58 // get and store the input data 59 Scanner in = new Scanner(System.in); 60 int num = in.nextInt(); 61 int[][] table = new int [num][4]; 62 for (int i=0;i<num;i++) { 63 table[i][0] = in.nextInt(); 64 table[i][1] = in.nextInt(); 65 table[i][2] = in.nextInt(); 66 table[i][3] = in.nextInt(); 67 } 68 in.close(); 69 // build up the hash table 70 Hash set = new Hash(); 71 for (int i=0;i<num;i++) { 72 for (int j=0;j<num;j++) { 73 set.insert(table[i][0]+table[j][1]); 74 } 75 } 76 // solve the problem and print the result 77 int val = 0; 78 for (int i=0;i<num;i++) { 79 for (int j=0;j<num;j++) { 80 val += set.search(-table[i][2]-table[j][3]); 81 } 82 } 83 System.out.println(val); 84 } 85 86 }
P.S. 关于链表的操作比较易错的是遍历的同时删除结点,应该这样做:
1 private void srchAndDel(int del) { 2 Node itr = tbl[index(item)]; 3 while (itr.next!=null) { 4 if (itr.next.data==item) { 5 itr.next = itr.next.next; 6 continue; // Attention! 7 } 8 itr = itr.next; 9 } 10 }
3. Cantor Expansion
In the example above, I constructed a hash function by Division Method and this may seem more than facile. However, in other cases, the construction of a proper hash function may be a rather tricky work, which involves a lot of mathematical knowledge. Here, I'd like to take Cantor Expansion for example, a bijection that maps a permutation of n distinct integers to a natural number index in a hash table.
1 public static int cantor(int[] perm) { 2 // Encode a 0 through n-1 permutation into an index 3 int val = 0, len = perm.length; 4 int[] cnt = new int[len]; 5 boolean[] vis = new boolean[len]; 6 for (int i=0;i<len;i++) { 7 for (int j=0;j<perm[i];j++) { 8 // cnt[i] is the number of unvisited numbers 9 // that are smaller than perm[i] 10 cnt[i] += (vis[j])? 0:1; 11 } 12 vis[perm[i]] = true; 13 } 14 for (int i=0;i<len;i++) { 15 val = val*(len-i)+cnt[i]; 16 } 17 return val; 18 } 19 public static void cantInv(int code,int[] perm) { 20 // Decode an index into a 0 through n-1 permutation 21 int len = perm.length; 22 int[] fact = new int[len]; 23 fact[0] = 1; 24 for (int i=1;i<len;i++) { 25 // fact[i] stores i factorial 26 fact[i] = fact[i-1]*i; 27 } 28 boolean[] vis = new boolean[len]; 29 for (int i=0;i<len;i++) { 30 int cnt = code/fact[len-i-1]; 31 for (int j=0;j<len;j++) { 32 // perm[i] is the (cnt+1) th smallest one among unvisited items 33 if (!vis[j]&&--cnt<0) { 34 perm[i] = j; 35 break; 36 } 37 } 38 vis[perm[i]] = true; 39 code %= fact[len-i-1]; 40 } 41 }