符号表
符号表是一种通过把一个键(key)和一个值(value)联系起来,在调用时通过查找键来对键对应的值进行操作的数据结构(如c++中的map)。
符号表的主要操作有增,删,改,查四种,也可以对其进行扩展操作。下面,就对几种符号表的实现及部分扩展操作进行简要的介绍。
符号表的双数组实现
顾名思义,通过两个数组,一个存放key,一个存放value,来实现符号表。为了便于数据查找,要保证数组内数据的有序性。
这里给出完整代码:
//符号表(双数组实现版)
template<typename K,typename V>
class signTable{
vector<K> keyV;
vector<V> valueV;
size_t n = 0;
public:
signTable() = default;
size_t size() { return n; }
bool is_empty() { return n == 0; }
K max() { return is_empty() ? NULL : keyV[n - 1]; }
K min() { return is_empty() ? NULL : keyV[0]; }
K select(size_t r) { return (r < n) ? keyV[r] : NULL; }
size_t rank(K key);
size_t rank(K lo, K hi);
void insert(K key, V value);
V get(K key);
void dele(K key);
K ceiling(K key);
K floor(K key);
vector<K> cut_key(K lo, K hi);
};
template<typename K, typename V>
size_t signTable<K, V>::rank(K key) {
if (is_empty()) { return 0; }
int lo = 0, hi = n - 1, mid;
while (lo <= hi) {
mid = lo + (hi - lo) / 2;
if (key < keyV[mid]) { hi = mid - 1; }
else if (key > keyV[mid]) { lo = mid + 1; }
else { return mid; }
}
return lo;
}
template<typename K, typename V>
size_t signTable<K, V>::rank(K lo,K hi) {
if (hi <= lo) { return 0; }
else { return rank(hi) - rank(lo); }
}
template<typename K, typename V>
void signTable<K, V>::insert(K key, V value) {
size_t temp = rank(key);
if (temp == n) {
keyV.push_back(key);
valueV.push_back(value);
++n;
}
else if (keyV[temp] == key) { valueV[temp] = value; }
else {
keyV.push_back(keyV[n - 1]);
valueV.push_back(valueV[n - 1]);
for (int i = n - 1; i > temp; --i) {
keyV[i] = keyV[i - 1];
valueV[i] = valueV[i - 1];
}
keyV[temp] = key;
valueV[temp] = value;
++n;
}
}
template<typename K, typename V>
V signTable<K, V>::get(K key) {
size_t temp = rank(key);
if (temp == n || keyV[temp] != key) { return NULL; }
return valueV[temp];
}
template<typename K, typename V>
void signTable<K, V>::dele(K key) {
size_t temp = rank(key);
if (temp == n || keyV[temp] != key) { return; }
while (temp < n - 1) {
valueV[temp] = valueV[temp + 1];
keyV[temp] = keyV[++temp];
}
valueV.pop_back();
keyV.pop_back();
--n;
}
template<typename K, typename V>
K signTable<K, V>::ceiling(K key) {
size_t temp = rank(key);
if (temp == n) { return NULL; }
return keyV[temp];
}
template<typename K, typename V>
K signTable<K, V>::floor(K key) {
size_t temp = rank(key);
if (temp == 0 && keyV[temp] != key) { return NULL; }
if (temp == n || keyV[temp] != key) { return keyV[temp - 1]; }
return key;
}
template<typename K, typename V>
vector<K> signTable<K, V>::cut_key(K lo, K hi) {
vector<K> temp;
K temp1 = ceiling(lo);
if (!temp1) { return temp; }
size_t temp2 = rank(floor(hi));
for (size_t i = rank(temp1); i <= temp2; ++i) {
temp.push_back(keyV[i]);
}
return temp;
}
函数详细解释:
- rank(K key) :使用二分查找,找出所提供的key在表中的秩,如果表中没有key,则返回大于key的最小值(如果不存在则是表尾)的秩。
- rank(K lo, K hi) :两个key的秩的差值。
- insert(K key, V value) :插入和修改操作,如果key已存在,则修改value(表中key具有唯一性);key不存在则插入,在插入时仍保证表的有序性。
- get(K key) :查找操作,返回key对应的value,如果key不存在则返回NULL。
- dele(K key) :删除操作,删除后仍保证表的有序性。
- ceiling(K key) :天花板,返回大于等于key的最小值的秩(不存在返回Null)。
- floor(K key) :地板,返回小于等于key的最大值的秩(不存在返回Null)。
- cut_key(K lo, K hi) :切片,返回一个由大于等于lo小于等于hi的秩组成的vector。
rank通过二分查找来获取秩,而几乎所有操作都是通过rank提供的秩来实现的,这也保证了程序的高效性。双数组符号表的查找性能可以达到O(lgN)级别,但它的插入操作仍是O(N)级别。
符号表的二叉搜索树实现
用二叉树实现的符号表,每个节点存放key,value,n(以它为根节点的树的节点数)和指向左右树的智能指针(防止内存泄漏)。通过递归,自顶向下地进行查找,保证较低的时间复杂度。
这里给出完整代码:
//符号表(二叉搜索树实现版)
class BST {
struct Node {
int key;
int value;
shared_ptr<Node> left, right;
int n;
Node(int key, int value, int n) :key(key), value(value), n(n) {}
};
shared_ptr<Node>root;
int size(shared_ptr<Node> node) { return node ? node->n : 0; }
int get(shared_ptr<Node> node, int key) {
if (node == nullptr) { return 0; }
if (key < node->key) { return get(node->left, key); }
else if (key > node->key) { return get(node->right, key); }
else { return node->value; }
}
shared_ptr<Node> put(int key, int value, shared_ptr<Node> node) {
if (node == nullptr) { node = shared_ptr<Node>(new Node(key, value, 1)); }
else if (key < node->key) { node->left = put(key, value, node->left); }
else if (key > node->key) { node->right = put(key, value, node->right); }
else { node->value = value; }
node->n = size(node->left) + size(node->right) + 1;
return node;
}
shared_ptr<Node> max(shared_ptr<Node> node) {
if (node->right == nullptr) { return node; }
else { return max(node->right); }
}
shared_ptr<Node> min(shared_ptr<Node> node) {
if (node->left == nullptr) { return node; }
else { return min(node->left); }
}
shared_ptr<Node> floor(int key, shared_ptr<Node> node) {
if (node == nullptr) { return node; };
if (key < node->key) { return floor(key, node->left); }
else if (key == node->key) { return node; }
shared_ptr<Node> tem = floor(key,node->right);
if (tem) { return tem; }
return node;
}
shared_ptr<Node> ceiling(int key, shared_ptr<Node> node) {
if (node == nullptr) { return node; };
if (key > node->key) { return floor(key, node->right); }
else if (key == node->key) { return node; }
shared_ptr<Node> tem = floor(key, node->left);
if (tem) { return tem; }
return node;
}
shared_ptr<Node> select(int ran, shared_ptr<Node> node) {
if (node == nullptr) { return NULL; }
int t = size(node->left);
if (ran < t) { return select(ran, node->left); }
else if (ran > t) { return select(ran - t - 1, node->left); }
else { return node; }
}
int rank(int key, shared_ptr<Node> node) {
if (node == nullptr) { return NULL; }
if (key < node->key) { return rank(key, node->left); }
else if (key > node->key) { return size(node->left) + 1 + rank(key, node->right); }
else { return size(node->left); }
}
shared_ptr<Node> deleteMax(shared_ptr<Node> node) {
if (node->right == nullptr) { return node->left; }
node->right = deleteMax(node->right);
node->n = size(node->left) + 1 + size(node->right);
return node;
}
shared_ptr<Node> deleteMin(shared_ptr<Node> node) {
if (node->left == nullptr) { return node->right; }
node->left = deleteMin(node->left);
node->n = size(node->left) + 1 + size(node->right);
return node;
}
shared_ptr<Node> deleteKey(shared_ptr<Node> node,int key) {
if (node == nullptr) { return nullptr; }
if (key < node->key) { node->left = deleteKey(node->left, key); }
else if (key > node->key) { node->right = deleteKey(node->right, key); }
else {
if (node->left == nullptr) { return node->right; }
if (node->right == nullptr) { return node->left; }
shared_ptr<Node>tem = min(node->right);
node->key = tem->key;
node->value = tem->value;
node->right = deleteMin(node->right);
}
node->n = size(node->left) + 1 + size(node->right);
return node;
}
vector<int>& keys(shared_ptr<Node>node, vector<int>& vk, int l, int r) {
if (node == nullptr) { return vk; }
if (l < node->key) { vk = keys(node->left, vk, l, r); }
if (l <= node->key && r >= node->key) { vk.push_back(node->key); }
if (r > node->key) { vk = keys(node->right, vk, l, r); }
return vk;
}
public:
int size() { return size(root); }
int get(int key) { return get(root, key); }
void put(int key, int value) { root = put(key, value, root); }
int max() { return max(root)->key; }
int min() { return min(root)->key; }