第三章 符号表

符号表的元素以键值-数值为类型,主要操作是插入和查找。

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));

	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值