符号表
符号表最主要的目的就是将一个键和一个值联系起来,符号表能够将存储的数据元素是一个键和一个值共同组成的键值对数据,我们可以根据键来查找对应的值。
符号表是利用单链表实现的,原先的链表的结点只有item和next数据,而实现符号表的链表的结点为key、value、next数据
符号表中,键具有唯一性。
符号表在实际生活中的使用场景是非常广泛的,见下表:
应用 | 查找目的 | 键 | 值 |
---|---|---|---|
字典 | 找出单词的释义 | 单词 | 释义 |
图书索引 | 找出某个术语相关的页码 | 术语 | 一串页码 |
网络搜索 | 找出某个关键字对应的网页 | 关键字 | 网页名称 |
符号表的API设计
结点类:
类名 | Node |
---|---|
构造方法 | Node(Key key,Value value,Node next):创建Node对象 |
成员变量 | 1.public Key key:存储键 2.public Value value:存储值 3.public Node next:存储下一个结点 |
符号表:
类名 | SymbolTable<Key,Value> |
---|---|
构造方法 | SymbolTable():创建SymbolTable对象 |
成员方法 | 1.public Value get(Key key):根据键key,找对应的值 2.public void put(Key key,Value val):向符号表中插入一个键值对 3.public void delete(Key key):删除键为key的键值对 4.public int size():获取符号表的大小 |
成员变量 | 1.private Node head:记录首结点 2.private int N:记录符号表中键值对的个数 |
符号表实现
/**
* 符号表--链表实现
*
* @param <Key>
* @param <Value>
*/
public class SymbolTable<Key, Value> {
// 记录首结点
private Node head;
// 记录符号表中键值对的个数(链表中的元素个数)
private int N;
public SymbolTable() {
head = new Node(null, null, null);
N = 0;
}
// 获取符号表中键值对的个数
public int size() {
return N;
}
// 往符号表中插入键值对
public void put(Key key, Value value) {
// 保证键的唯一性,如果键存在就更新值
Node n = head;
while (n.next != null) {
n = n.next;
// 比对key,相等则更换value
if (key.equals(n.key)) {
n.value = value;
return;
}
}
// 键不存在,创建新结点保存键值对
Node newFirst = new Node(key, value, head.next);
head.next = newFirst;
// 元素个数+1
N++;
}
// 删除符号表中键为key的键值对
public void delete(Key key) {
Node n = head;
while (n.next != null) {
// 比对n的下一个结点的key(这样删除时可以省去新建一个结点)
if (key.equals(n.next.key)) {
// 删除元素
n.next = n.next.next;
// 元素个数-1
N--;
return;
}
n = n.next;
}
}
// 从符号表中获取key对应的值
public Value get(Key key) {
Node n = head;
while (n.next != null) {
n = n.next;
if (key.equals(n.key)) {
return n.value;
}
}
return null;
}
private class Node {
private Key key;
private Value value;
private Node next;
public Node(Key key, Value value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}
有序符号表
刚才实现的符号表,可以称之为无序符号表,因为在插入的时候,并没有考虑键值对的顺序,而在实际生活中,有时候我们需要根据键的大小进行排序,插入数据时要考虑顺序
/**
* 有序符号表(从小到大排序)
*/
public class OrderSymbolTable<Key extends Comparable, Value> {
// 记录首结点
private Node head;
// 记录符号表中键值对的个数(链表中的元素个数)
private int N;
public OrderSymbolTable() {
head = new Node(null, null, null);
N = 0;
}
// 获取符号表中键值对的个数
public int size() {
return N;
}
// 往符号表中插入键值对(从小到大排序)
public void put(Key key, Value value) {
// 记录当前结点的上一个结点
Node pre = head;
// 记录当前结点
Node curr = head.next;
// 比对key值,直到找到某一结点的key值 > 传入的key值,然后插入
while (curr != null && key.compareTo(curr.key) > 0) {
pre = curr;
curr = curr.next;
}
// 如果(找的结点的key) = (传入的key),则进行值覆盖
if (curr != null && key.compareTo(curr.key) == 0) {
curr.value = value;
return;
}
// 没有找到相同的key,创建新结点插入到curr前s
Node node = new Node(key, value, curr);
pre.next = node;
// 元素个数+1
N++;
}
// 删除符号表中键为key的键值对
public void delete(Key key) {
Node n = head;
while (n.next != null) {
// 比对n的下一个结点的key(这样删除时可以省去新建一个结点)
if (key.equals(n.next.key)) {
// 删除元素
n.next = n.next.next;
// 元素个数-1
N--;
return;
}
n = n.next;
}
}
// 从符号表中获取key对应的值
public Value get(Key key) {
Node n = head;
while (n.next != null) {
n = n.next;
if (key.equals(n.key)) {
return n.value;
}
}
return null;
}
private class Node {
private Key key;
private Value value;
private Node next;
public Node(Key key, Value value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}