符号表的元素以键值-数值为类型,主要操作是插入和查找。
A.基于无序链表的顺序查找
package com.Page3;
import java.util.LinkedList;
import java.util.Queue;
public class SequentialSearchST { //实现符号表,顺序查找,基于无序链表.不知道为什么如果在后面限定<Integer,Double>后面就会报错。。。
Node first;
private class Node
{
Integer key;
Double val;
Node next;
Node(Integer k,Double v,Node n) //注意咯,Node类的构造函数就要放在Node类的定义里面
{
key=k;
val=v;
next=n;
}
}
public Double get(Integer key)
{
Node x=first;
while(x!=null)
{
if(x.key==key)
{
return x.val;
}
x=x.next;
}
return null;
}
public void put(Integer key,Double val)
{
Node x=first;
while(x!=null)
{
if(x.key==key)
{
x.val=val;
return;
}
x=x.next;
}
//若没有键值为key的结点,则新建结点
Node newNode=new Node(key,val,first); //注意,是在链表首部插入新结点
first=newNode;
}
public int size()
{
int N=0;
Node x=first;
while(x!=null)
{
N++;
x=x.next;
}
return N;
}
public Iterable<Integer> keys()
{
Queue<Integer> keys=new LinkedList<Integer>(); //注意,因为Queue是接口,所以式子右边不能写Queue,应该是具体的类型
Node x=first;
while(x!=null)
{
keys.add(x.key);
x=x.next;
}
return keys;
}
public void delete(Integer key)
{
Node pre;
pre=first;
if(pre.key==key)
{
first=first.next;
return;
}
while(pre.next!=null)
{
if(pre.next.key==key)
{
pre.next=pre.next.next;
}
pre=pre.next;
}
}
public static void main(String[] args)
{
SequentialSearchST st=new SequentialSearchST();
st.put(1, 1.1);
st.put(2, 2.1);
st.put(3, 3.1);
st.put(4, 4.1);
System.out.println("size "+st.size());
System.out.println("get(2) "+st.get(2));
st.delete(2);
System.out.println("get(2) "+st.get(2));
for(Integer key : st.keys()) //注意是for而不是while。。。。气死了因为这个错误找了半小时。。。。哭死
{
System.out.print(st.get(key)+" ");
}
}
}
B.基于有序数组的二分查找
package com.Page3;
import com.Page1.Queue;
public class BinarySearchST<Key extends Comparable<Key>,Value>{ //实现符号表,基于有序数组的二分查找
//维护两个数组
private Key[] keys;
private Value[] vals;
private int N=0;
public BinarySearchST(int capacity)
{
keys=(Key[]) new Comparable[capacity];
vals=(Value[]) new Object[capacity];//注意构造方式
}
public int size()
{
return N;
}
public boolean isEmpty()
{
return N==0;
}
public Value get(Key key)
{
if(isEmpty()) return null;
int i=rank(key);
if(i<N&&keys[i].compareTo(key)==0) return vals[i];
else return null;
}
public int rank(Key key)
{
int lo=0;int hi=N-1;
while(lo<=hi) //注意用的是while循环
{
int mid=lo+(hi-lo)/2;
int cmp=keys[mid].compareTo(key);
if(cmp==0) return mid;
else if(cmp<0) lo=mid+1;
else hi=mid-1;
}
return lo;//注意:未命中时,还是返回小于该键的键数量,即如果要插入该键的话应该插入的下标
}
public void put(Key key,Value val)
{
int i=rank(key);
if(i<N&&keys[i].compareTo(key)==0) //命中时。ps.如果是i==N的情况即为数组为空或是key比所有键都大的情况
{
vals[i]=val;
return;
}
//未命中时,将插入点位置的键与值逐个后移
for(int j=N;j>i;j--)
{
keys[j]=keys[j-1];
vals[j]=vals[j-1];
}
keys[i]=key;
vals[i]=val;
N++;//最后N加1
}
public void delete(Key key)
{
int i=rank(key);
if(i==N||keys[i].compareTo(key)!=0) //未命中
{
return;
}
for(int j=i+1;j<N;j++)
{
keys[j-1]=keys[j];
vals[j-1]=vals[j];
}
N--;
}
public Key min()
{
return keys[0];
}
public Key max()
{
return keys[N-1];
}
public Key select(int k) //排名为k的键,即有k个键小于此键
{
if(k>=N) return null;
return keys[k];
}
public Key ceiling(Key key)
{
int i=rank(key);
if(i<N&&keys[i].compareTo(key)==0) //命中
return keys[i];
else if(i<N&&keys[i].compareTo(key)!=0) //未命中,排名位置在数组中间(不是那种超过所有键的情况)
return keys[i];
else return null;
}
public Key floor(Key key)
{
int i=rank(key);
if(i<N&&keys[i].compareTo(key)==0) //命中
return keys[i];
else if(i<N&&i==0) //未命中,排名位置在数组中间(不是那种超过所有键的情况)
return null;
else return keys[i-1];
}
public Iterable keys()
{
if(isEmpty()) return null;
return keys(keys[0],keys[N-1]);
}
public Iterable keys(Key lo,Key hi) //参数是键
{
Queue<Key> q=new Queue<Key>();
int loRank=rank(lo);
int hiRank=rank(hi);
if(hiRank==N) hiRank=N-1;
else if(hiRank<N&&keys[hiRank].compareTo(hi)!=0) hiRank-=1;
for(int i=loRank;i<=hiRank;i++)
{
q.enqueue(keys[i]);
}
return q;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BinarySearchST st=new BinarySearchST(3); //这里不能用<Key,Object>,那应该用什么???
st.put(1,"aa");
st.put(3,"bb");
st.put(5,"cc");
System.out.println("size: "+st.size());
System.out.println("get(2): "+st.get(2));
st.put(3,"dd");
System.out.println("get(2): "+st.get(2));
// st.delete(2);
System.out.println("get(2): "+st.get(2));
System.out.println("size: "+st.size());
System.out.println("test Iterable");
for(Object k:st.keys(1,3))
{
System.out.print(k+" ");
}
System.out.println();
System.out.println("test max");
System.out.println(st.max());
System.out.println("test select");
System.out.println(st.select(1));
System.out.println("test ceiling");
System.out.println(st.ceiling(4));
System.out.println("test floor");
System.out.println(st.floor(4));
}
}
C.二叉查找树
package com.Page3;
import com.Page1.Queue;
public class BST<Key extends Comparable<Key>,Value> { //实现符号表,基于二叉查树
private Node root;
private class Node
{
Key key;
Value value;
Node left,right;
int N;
public Node(Key key,Value value,int N)
{
this.key=key;
this.value=value;
this.N=N;
}
}
public int size()
{
return size(root);
}
private int size(Node x)
{
if(x==null) return 0;//注意,还要考虑到空结点的情况
return x.N;
}
public Value get(Key key)
{
Node x=root;
while(x!=null)
{
int cmp=x.key.compareTo(key);
if(cmp==0)
return x.value;
else if(cmp<0)
x=x.right;
else
x=x.left;
}
return null;//当key未命中时
}
public void put(Key key,Value value)
{
root=put(root,key,value);
}
private Node put(Node x,Key key,Value value)
{
if(x==null) return new Node(key,value,1);
int cmp=x.key.compareTo(key);
if(cmp==0)
{
x.value=value;
return x;
}
else if(cmp<0)
{
x.right=put(x.right,key,value);
}
else
{
x.left=put(x.left,key,value);
}
x.N=size(x.left)+size(x.right)+1;
return x;
}
public Key min()
{
return min(root).key;
}
private Node min(Node x)
{
if(x==null) return null;
if(x.left!=null)
return min(x.left);
if(x.left==null)
return x;
return null;
}
public Key max()
{
return max(root).key;
}
private Node max(Node x)
{
if(x==null) return null;
if(x.right!=null)
return max(x.right);
if(x.right==null)
return x;
return null;
}
public Key floor(Key key)
{
Node x=floor(root,key);
if(x==null) return null;
return x.key;
}
private Node floor(Node x,Key key)
{
if(x==null) return null;
int cmp=x.key.compareTo(key);
if(cmp==0) return x;
else if(cmp>0)
return floor(x.left,key);
Node t=floor(x.right,key);
if(t==null) return x;
else return t;
}
public Key ceiling(Key key)
{
Node x=ceiling(root,key);
if(x==null) return null;
return x.key;
}
private Node ceiling(Node x,Key key)
{
if(x==null) return null;
int cmp=x.key.compareTo(key);
if(cmp==0) return x;
else if(cmp<0)
return ceiling(x.right,key);
Node t=ceiling(x.left,key);
if(t==null) return x;
else return t;
}
public Key select(int k) //k从0开始
{
Node x=select(root,k);
if(x==null) return null;
return x.key;
}
private Node select(Node x,int k)
{
if(x==null) return null;
if(k==size(x.left)) return x;
if(k<size(x.left))
{
return select(x.left,k);
}
return select(x.right,k-size(x.left)-1);
}
public int rank(Key key) //排名从0开始,注意我们没考虑到如果此key不在树中的情况!!
{
return rank(key,root);
}
public int rank(Key key,Node x)
{
if(x==null) return 0;
if(x.key==key)
{
if(x.left!=null) return x.left.N;
else return 0;
}
int cmp=key.compareTo(x.key);
if(cmp<0) return rank(key,x.left);
else
{
if(x.left!=null) return x.left.N+rank(key,x.right)+1;
else return rank(key,x.right)+1;
//其实这种讨论是否为空结点的步骤可以省略,直接用size()函数即可。
}
}
public void deleteMin()
{
root=deleteMin(root);
}
public Node deleteMin(Node x)
{
if(x.left==null)
{
if(x.right==null) return null;
return x.right;//是用待删元素的右结点来代替待删元素,而不是粗暴地置null
}
else
{
x.left=deleteMin(x.left);
x.N=size(x.left)+size(x.right)+1;
}
return x;
}
public void delete(Key key)
{
root=delete(root,key);
}
private Node delete(Node x,Key key)
{
if(x==null) return null;
int cmp=key.compareTo(x.key);
if(cmp<0) x.left=delete(x.left,key);
else if(cmp>0) x.right=delete(x.right,key);
else
{
if(x.left==null) return x.right;
if(x.right==null) return x.left;
Node t=x;
x=min(x.right);
x.right=deleteMin(t.right);
x.left=t.left;
}
x.N=size(x.left)+size(x.right)+1;
return x;
}
public Iterable keys()
{
return keys(min(),max());
}
public Iterable keys(Key lo,Key hi)
{
Queue<Key> q=new Queue<Key>();
keys(root,q,lo,hi);
return q;
}
private void keys(Node x,Queue<Key> q,Key lo,Key hi)
{
if(x==null) return;
int cmplo=lo.compareTo(x.key);
int cmphi=hi.compareTo(x.key);
if(cmplo<0) keys(x.left,q,lo,hi);
if(cmplo<=0&&cmphi>=0) q.enqueue(x.key);
if(cmphi>0) keys(x.right,q,lo,hi);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
BST bst=new BST();
bst.put(5, 11);
bst.put(1, 22);
bst.put(6, 33);
bst.put(0, 11);
bst.put(3, 22);
bst.put(2, 33);
bst.put(4, 33);
System.out.println(bst.size());
System.out.println(bst.get(3));
System.out.println(bst.ceiling(5));
System.out.println(bst.floor(5));
System.out.println(bst.select(1));
System.out.println(bst.rank(4));
System.out.println(bst.rank(100));
// bst.deleteMin();
// System.out.println(bst.size());
// System.out.println(bst.rank(1));
// bst.deleteMin();
// System.out.println(bst.size());
// System.out.println(bst.rank(2));
//bst.delete(0);
System.out.println("size: "+bst.size());
System.out.println(bst.rank(2));
System.out.println("test keys()");
for(Object key:bst.keys())
{
System.out.println(key);
}
System.out.println("min: "+bst.min()+" max: "+bst.max());
}
}
D.平衡查找树(红黑树)
此部分内容单独写。
E.基于拉链法的散列表
package com.Page3;
import java.util.LinkedList;
import com.Page1.Queue;
public class SeparateChainingHashST<Key,Value> { //基于拉链法的散列表实现
int N;//键值对总数
int M;//散列表大小,即数组的大小
SequentialSearchST[] st; //链表数组
public SeparateChainingHashST()
{
this(997);
}
public SeparateChainingHashST(int M)
{
N=0;
this.M=M;
st=new SequentialSearchST[M];
for(int i=0;i<M;i++) //注意,还要为每一个元素初始化对象
{
st[i]=new SequentialSearchST();
}
}
private int hash(Key key)
{
return (key.hashCode()& 0x7fffffff)% M;
}
public Value get(Key key)
{
return (Value) st[hash(key)].get(key);//注意,这里需要类型转换(Value),不然就是Object类型
}
public void put(Key key,Value value)
{
st[hash(key)].put(key, value);
}
public Iterable keys() //注意方法!!!!
{
Queue<Key> queue = new Queue<Key>();
for (int i = 0; i < M; i++) {
for (Object key : st[i].keys()) {
queue.enqueue((Key)key);;
}
}
return queue;
}
public void delete(Key key)
{
st[hash(key)].delete(key);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SeparateChainingHashST<Integer,String> sc=new SeparateChainingHashST<Integer,String>(5);
sc.put(2, "a");
sc.put(1, "d");
sc.put(4, "c");
sc.put(3, "e");
System.out.println(sc.get(2));
for(Object key:sc.keys())
{
System.out.println(key);
}
sc.delete(3);
System.out.println("after delete");
for(Object key:sc.keys())
{
System.out.println(key);
}
}
}
F.基于线性探测的散列表
package com.Page3;
public class LinearProbingHashST<Key,Value> { //基于线性探测的散列表实现
Key[] keys;//表示键的数组
Value[] values;//表示值的数组
int N;//键值对数量
int M=16;//数组大小,初始设为16
public LinearProbingHashST()
{
keys=(Key[]) new Object[M]; //注意这种构造方法
values=(Value[]) new Object[M];
N=0;
}
private int hash(Key key)
{
return (key.hashCode()&0x7fffffff)%M;
}
public void put1(Key key,Value value) //注意,这是复杂的一种写法!!!!巧妙而简洁版见下面
{
if(N>=M/2) resizeL();
int locate=hash(key);
while(locate<M)
{
if(keys[locate].equals(key)) //已存在该键.注意:不要用=号,因为如果键是string等类型,则无法判断相等
{
values[locate]=value;
break;
}
if(keys[locate]==null)
{
keys[locate]=key;
values[locate]=value;
N++;
break;
}
}
if(locate!=M) //说明在数组末尾前已经成功put,否则就从数组首部重新查找put的位置
return;
locate=0;
while(keys[locate]!=null) //由于数组大小是动态的,所以保证了一定能找到put的位置
{
if(keys[locate].equals(key)) //已存在该键
{
values[locate]=value;
break;
}
locate++;
}
if(keys[locate]!=key)
{
keys[locate]=key;
values[locate]=value;
N++;
}
}
public void put(Key key,Value value) //here we go!这才是简洁优雅的put版本
{
if(N>=M/2) resizeL();
int i;
for(i=hash(key);keys[i]!=null;i=(i+1)%M)
{
if(keys[i].equals(key))
{
values[i]=value;
return;
}
}
keys[i]=key;
values[i]=value;
N++;
}
public Value get1(Key key) //同理,这种写法太复杂。简单版的见下面。
{
int locate=hash(key);
int i=locate;
while(i<M) //在locate之后的数组中查找
{
if(keys[locate].equals(key))
return values[i];
if(keys[i]==null) //表示找不到
return null;
i++;
}
//从数组首端开始继续找
i=0;
while(i<locate)
{
if(keys[locate].equals(key))
return values[i];
if(keys[i]==null) //表示找不到
return null;
i++;
}
return null;
}
public Value get(Key key)
{
int i;
for(i=hash(key);keys[i]!=null&&!keys[i].equals(key);i=(i+1)%M);
if(keys[i]==null) return null;
return values[i];
}
public void delete(Key key)
{
if(!contains(key)) return;
int i=hash(key);
keys[i]=null;
values[i]=null;
i=(i+1)%M;
while(keys[i]!=null)
{
Key oldKey=keys[i];
Value oldValue=values[i];
keys[i]=null;
values[i]=null;
N--;
put(oldKey,oldValue);
i=(i+1)%M;
}
N--;
if(N>0&&N==M/8) resizeS();
}
public int size()
{
return N;
}
//这里我用了两个函数分别指代数组尺寸的变大和变小。。其实不用这样,直接将变化后的尺寸作为输入参数传入,即可随机调整数组大小
private void resizeL() //将数组尺寸大一倍
{
M*=2;
//将原来的数组复制到临时数组中
Key[] oldKeys=keys;
Value[] oldValue=values;
//原来的数组扩大规格
keys=(Key[]) new Object[M];
values=(Value[]) new Object[M];
//重新将两个数组中的元素hash到扩大后的数组里
N=0;//注意,这里需要将N重新设为0.否则之后将N个元素重新hash到新数组后N的数量就变成2N了!!!!!
for(int i=0;i<M/2;i++)
{
if(oldKeys[i]!=null)
{
put(oldKeys[i],oldValue[i]);
}
}
}
private void resizeS() //将数组尺寸小一倍
{
M/=2;
//将原来的数组复制到临时数组中
Key[] oldKeys=keys;
Value[] oldValue=values;
//原来的数组扩大规格
keys=(Key[]) new Object[M];
values=(Value[]) new Object[M];
//重新将两个数组中的元素hash到扩大后的数组里
for(int i=0;i<M*2;i++)
{
if(oldKeys[i]!=null)
{
put(oldKeys[i],oldValue[i]);
}
}
}
public int getM() //返回的是M的值,即目前数组的大小
{
return M;
}
public boolean contains(Key key)
{
return get(key)!=null;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
LinearProbingHashST<Integer,String> lp=new LinearProbingHashST<Integer,String>();
lp.put(2, "a");
lp.put(1, "b");
lp.put(3, "c");
System.out.println("size: "+lp.size());
System.out.println("get(1) "+lp.get(1));
System.out.println("M: "+lp.getM());
lp.put(1, "a");
lp.put(11, "b");
lp.put(31, "c");
lp.put(22, "a");
lp.put(12, "b");
lp.put(32, "c");
lp.put(23, "a");
lp.put(13, "b");
lp.put(33, "c");
System.out.println("get(1) "+lp.get(1));
System.out.println("get(5) "+lp.get(5));
System.out.println("contains key 1 "+lp.contains(1));
System.out.println("contains key 5 "+lp.contains(5));
System.out.println("mark");
System.out.println("M: "+lp.getM());
System.out.println("N: "+lp.size());
lp.delete(31);
System.out.println("N: "+lp.size());
System.out.println("get(31) "+lp.get(31));
System.out.println("contains key 31 "+lp.contains(31));
}
}