关联式容器和序列容器都是C++ STL中的容器,它们主要的区别在于其存储内部元素的方式不同。
序列容器是按照元素插入的顺序来存储元素的,也就是说,元素在容器中的位置与其被插入的先后顺序是一致的。例如,vector、list 和deque都是序列容器。
而关联式容器不同,它们是通过比较关键字来对元素进行有序存储的。关键字可以是一个简单数据类型,例如整数或字符串,也可以是一个自定义的结构体或类,元素是按照关键字大小的顺序进行存储的。例如,set、map、multiset和multimap都是关联式容器。
因此,序列容器和关联式容器的最大区别在于内部元素的顺序。此外,在实现上,关联式容器一般采用平衡查找树或哈希表进行元素的存储和访问,而序列容器只需要链表或数组即可实现。两种容器的插入、删除和查找操作的时间复杂度也有所不同,因此需要根据实际问题的需求来选择使用何种容器。
C++ STL中主要有以下几种容器:
1. vector
vector是一个动态数组,可以随时改变大小,通过指针连续存储其元素。vector可以随机存取元素,但在插入或删除元素时效率较低,因为需要移动其后续元素。
2. list
list是一个双向链表,可以高效地插入和删除元素,但随机访问效率较低。
3. deque
deque也是一个双向队列,可以高效地插入和删除元素,并支持在两端进行随机存取,但效率不如vector。
4. set/multiset
set是关键字集合,其中的元素按照关键字自动排序,并且不允许重复。multiset在set的基础上,允许重复元素。
5. map/multimap
map是一个关键字-值集合,其中的元素按照关键字自动排序,并且不允许重复。multimap在map的基础上,允许重复键值对。
6. stack
stack是一个后进先出的容器,只支持在栈顶进行插入删除操作。
7. queue
queue是一个先进先出的容器,只支持在队列头尾进行插入删除操作。
以上这些容器都是通过模板类实现的。在实现中,STL使用了许多高效的算法和数据结构,例如基于红黑树的set/multiset和map/multimap,以及基于堆的优先队列等等。因此,使用STL提供的容器可以大大提高程序的效率和可维护性。
STL中的容器使用了不同的数据结构来实现不同的功能。
以下是几种常见的数据结构和对应的容器:
1. 动态数组(vector):使用普通数组实现,支持动态扩展大小和随机访问元素。
2. 双向链表(list):使用链表实现,在插入和删除元素时具有高效率,但是访问元素时效率较低。
3. 双端队列(deque):内部使用多个固定大小的数组实现,在两端进行操作时具有高效率。
4. 集合(set)、映射(map):基于红黑树实现,具有自动排序和二分查找的优势。set使用红黑树存储关键字,map使用红黑树存储键值对。
5. 散列表(unordered_set)、无序映射(unordered_map):基于散列表实现,通过将关键字或键哈希到不同的桶中,实现高效查找和插入。区别是unordered_map存储键值对,unordered_set存储关键字。
6. 栈(stack)和队列(queue):分别使用vector和deque来实现,提供了后进先出和先进先出的操作。
以上都是STL容器中常见的数据结构实现方式。在实现上,STL会利用C++的多态特性和迭代器等功能来实现通用的容器,使得容器的使用变得方便和简单。
//下面是非 STL 的 C++ 实现的散列表,使用了开放地址法解决冲突,哈希函数选取的是简单的取模法:
#include <iostream>
#include <string>
class HashTable {
private:
static const int table_size = 100;
struct Item {
std::string key;
std::string value;
};
Item* hash_table[table_size];
public:
HashTable();
~HashTable();
int hash_func(std::string);
void add_item(std::string, std::string);
void remove_item(std::string);
void find_item(std::string);
void print_table();
};
HashTable::HashTable() {
//初始化散列表,把指针设为 NULL
for(int i = 0; i < table_size; i++) {
hash_table[i] = NULL;
}
}
HashTable::~HashTable() {
// 清除散列表中所有指针指向的内存
for(int i = 0; i < table_size; i++) {
if(hash_table[i] != NULL) {
delete hash_table[i];
}
}
}
int HashTable::hash_func(std::string key) {
// 简单的取模哈希函数
int hash = 0;
for(int i = 0; i < key.length(); i++) {
hash = (hash + (int)key[i]) * 17;
}
return hash % table_size;
}
void HashTable::add_item(std::string key, std::string value) {
int index = hash_func(key);
//进行线性探测,知道找到一个空的位置为止
while(hash_table[index] != NULL) {
index++;
index %= table_size;
}
hash_table[index] = new Item();
hash_table[index]->key = key;
hash_table[index]->value = value;
}
void HashTable::remove_item(std::string key) {
int index = hash_func(key);
while(hash_table[index] != NULL && hash_table[index]->key != key) {
index++;
index %= table_size;
}
if(hash_table[index] == NULL) {
std::cout << "Key not found" << std::endl;
return;
} else {
delete hash_table[index];
hash_table[index] = NULL;
std::cout << "Key removed" << std::endl;
}
}
void HashTable::find_item(std::string key) {
int index = hash_func(key);
while(hash_table[index] != NULL && hash_table[index]->key != key) {
index++;
index %= table_size;
}
if(hash_table[index] == NULL) {
std::cout << "Key not found" << std::endl;
return;
} else {
std::cout << "Key: " << hash_table[index]->key << ", Value: " << hash_table[index]->value << std::endl;
}
}
void HashTable::print_table() {
for(int i = 0; i < table_size; i++)
{
if(hash_table[i] != NULL) {
std::cout << "Index " << i << ": Key: " << hash_table[i]->key << ", Value: " << hash_table[i]->value << std::endl;
}
}
}
int main()
{
HashTable my_table;
my_table.add_item("123", "abc");
my_table.add_item("456", "def");
my_table.add_item("789", "ghi");
my_table.print_table();
my_table.remove_item("123");
my_table.print_table();
my_table.find_item("789");
my_table.find_item("123");
return 0;
}
/*
以上代码实现了添加、删除、查找和打印散列表的功能。
当添加元素时,先通过哈希函数计算键的索引,然后在散列表中进行线性探测,直到找到一个空的位置为止。
当删除元素时,同样也需要通过哈希函数找到键在散列表中的位置,然后删除该位置上的元素。
查找元素的方法与删除元素类似,但是需要在找到元素以后返回它的值。
*/