二分查找
設數組a,其元素個數是n
①查找區間的下界用low表示,上界用high表示。初始時,查找區間是low=0,high=n-1
②設區間中點下標爲mid,其中mid=(high+low)/2;將元素a[mid]的值與key進行比較,若key=a[mid],則表明查找成功,返回該元素的下標mid的值。
③若key<a[mid],則表明待查找元素key只可能落在該中點元素的左邊區間a[low:mid-1]中,接着只需在a[low:mid-1]中繼續進行二分查找即可,即新區間的下界不變,上界 high=mid-1.
④若key>a[mid],則表明待查找元素key只可能落在該中點元素的右邊區間a[mid+1:n-1]中,接着只需在a[mid+1:n-1]中繼續進行二分查找即可,即新區間的上界不變,下界low-mid+1
⑤這樣經過一次比較後就使得查找區間縮小一半,如此進行下去,直到查找到對應的元素,返回其下標值,或者查找區間變爲空(即區間下界low大於區間上界high),表明查找失敗返回-1爲止。
public static int binSearch(int a[], int key){
int n = a.length;
int low = 0, high = n-1, mid;
while(low <= high){
mid = (low + high)/2;
if(key == a[mid]) return mid;
if(key < a[mid]) high = mid - 1;
else low = mid + 1;
}
return -1;//查找失敗返回-1
}
哈希查找
哈希技術是在記錄的存儲地址和他的關鍵字間建立一個確定的對應關係(哈希函數),不經過比較,一次存取就能得到所需查找元素的查找方法。
一個很常用的哈希函數的構造方法是除留餘數法,即選某個合適的正整數,以關鍵字除以該正整數所得餘數作爲哈希地址
處理地址衝突的方法
1,開放地址法
線性探查 d = [h(key) + i]%m, i = 1, 2, 3, ...m-1,如果一個地址衝突就探尋下一個地址,直到找到空閒地址爲止
平方探查 d = [h(key) + i]%m, i = 1^2, 2^2, 3^2, ...
隨機探查 i爲隨機數
2,鏈地址法
將哈希地址相同的數據存儲到同一個單鏈表中
//基於鏈地址的哈希表程序
public class HashTable<T>{
LinkList<T> table[];//鏈表數組
public HashTable(int len){
//len是哈希表長度,如果len不是素數,取大於len的最小素數作爲哈希表的長度(取素數是爲了減小衝突)
int np;//大於len的最小素數
if(HashTable.isPrime(len)) np=len;
else{
if(len%2==0) len=len+1;
for(np=len;;np++)
if(HashTable.isPrime(np))
break;
}
table=new LinkList[np];
for(int i=0;i<table.length;i++){
table[i]=new LinkList<T>();
}
}
//哈希函數
public int hashCode( T key){
int hc=Math.abs(key.hashCode());
return hc%table.length;
}
public void add(T key){
int ha=hashCode(key);
table[ha].add(key);
}
public void remove(T key){
int ha=hashCode(key);
table[ha].remove(key);
}
public T search(T key){
int ha=hashCode(key);
return table[ha].search(key);
}
//判斷n是否爲素數
public static boolean isPrime(int n){
int m = (int) Math.sqrt(n);
if(n < 2) return false;
for(int i = 2; i < m; i++)
if(n%i == 0) return false;
return true;
}
public String toString(){
String str = "\n";
for(int i = 0; i < table.length; i++){
str = str + i + "|->" + table[i].toString() + "\n";
}
return str;
}
}
哈希映射
映射又稱字典,由鍵(key)值(value)對構成
哈希映射首先定義一個表示鍵值對的類,然後對剛剛的HashTable進行封裝
//表示(key,value)對的Pair類
class Pair<K,V>{
K key; //類型爲K關鍵字
V value;//類型爲V的值
public Pair(K k1,V v1){
key=k1; value=v1;
}
//重載Object的equals方法,注意真正比較的只是key,而忽略了value
public boolean equals(Object o){
Pair<K, V> pair = (Pair<K,V>)o;
return key.equals(pair.key);
}
//重載object的hashCode方法,注意我們只是簡單地返回key的hashCode值
public int hashCode(){
return key.hashCode();
}
public String toString(){
return "("+key+","+value+")";
}
}
public class HashMap<K,V> {
HashTable<Pair<K,V>> ht;//定義pair<K,V>類型的哈希表,則是關鍵點
public HashMap(int len) {
ht=new HashTable<Pair<K,V>>(len);
}
//向哈希表中添加一個(key,value)對,同put,只爲方便記憶
public void add(K key,V value){
ht.add(new Pair<K,V>(key,value));
}
//向哈希表中添加一個(key,value)對,Map的標準方法
public void put(K key,V value){
ht.add(new Pair<K, V>(key,value));
}
//根據關鍵字key,求的對應的value
public V get(K key){
Pair<K,V> p=ht.search(new Pair<K,V>(key,null));
if(p==null) return null;
else return p.value;
}
//判別Map中是否存在關鍵字key
boolean containsKey(K key){
Pair<K,V> p=ht.search(new Pair<K,V>(key,null));
if(p==null) return false;
else return true;
}
//刪除關鍵字爲key的(key,value) 對
public V remove(K key){
Pair<K,V> p=ht.remove(new Pair<K, V>(key,null));
if(p==null) return null;
else return p.value;
}
}
HashMap的使用
public static void main(String[] args) {
int n=6; //請試着修改這個值,觀察結果
HashMap<Integer,String> hm=new HashMap<Integer,String>(n);
hm.put(1, "北京");
hm.put(2, "上海");
hm.put(3, "古老歷史");
hm.put(4, "現代風貌");
hm.put(5, "我喜歡");
System.out.print("輸出哈希表表示的Map");
System.out.print(hm.ht);
System.out.println("==============");
System.out.print(hm.get(5));
System.out.print(hm.get(1));
System.out.println(hm.get(3));
System.out.print(hm.get(5));
System.out.print(hm.get(2));
System.out.println(hm.get(4));
}
運行結果:
輸出哈希表表示的Map
0|->( )
1|->((1,北京))
2|->((2,上海))
3|->((3,古老歷史))
4|->((4,現代風貌))
5|->((5,我喜歡))
6|->( )
==============
我喜歡北京古老歷史
我喜歡上海現代風貌