理解常用算法和数据结构是编程的重要基础,动态数组和哈希表是其中两个非常重要的容器。以下详细解释每一块内容,包括其设计原理、涉及的知识点以及代码示例。
一、动态数组
动态数组是一种可以动态调整大小的数组,它克服了普通数组固定长度的缺点。
1. 动态数组的设计原理
- 存储结构:
- 动态数组使用连续的内存块存储数据,与普通数组相似。
- 扩容机制:
- 当存储空间不足时,动态数组会分配更大的内存空间(通常为当前大小的 2 倍),并将旧数据复制到新内存中。
- 时间复杂度:
- 插入元素:平均时间复杂度为 O(1)O(1),最坏情况(触发扩容)为 O(n)O(n)。
- 查找元素:时间复杂度为 O(1)O(1)(通过索引访问)。
- 删除元素:时间复杂度为 O(n)O(n)(可能需要移动后续元素)。
2. 代码实现动态数组
C++ 示例
实现一个简单的动态数组类。
#include <iostream>
#include <stdexcept>
class DynamicArray {
private:
int* data;
int capacity;
int size;
void resize(int newCapacity) {
int* newData = new int[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
capacity = newCapacity;
}
public:
DynamicArray() : data(new int[2]), capacity(2), size(0) {}
~DynamicArray() {
delete[] data;
}
void add(int value) {
if (size == capacity) {
resize(capacity * 2);
}
data[size++] = value;
}
void removeAt(int index) {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
for (int i = index; i < size - 1; i++) {
data[i] = data[i + 1];
}
size--;
}
int get(int index) const {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
int getSize() const {
return size;
}
};
二、哈希表
哈希表是一种基于哈希函数实现的键值对存储的数据结构,具有快速插入、删除和查找的特性。
1. 哈希表的设计原理
- 哈希函数:
- 将键映射到数组索引。哈希函数应具有良好的分布性,以减少冲突。
- 冲突处理:
- 开放地址法:当冲突发生时,寻找下一个空闲位置。
- 链地址法:使用链表或其他结构存储冲突的元素。
- 动态扩容:
- 当负载因子(填充率)达到一定阈值时,哈希表会重新分配更大的存储空间并重新哈希所有键值对。
2. 哈希表的优缺点
- 优点:
- 插入、删除和查找操作的平均时间复杂度为 O(1)O(1)。
- 缺点:
- 最坏情况下(哈希冲突严重),时间复杂度退化为 O(n)O(n)。
- 需要更多的内存以避免冲突。
3. 代码实现哈希表
C++ 示例
实现一个简单的链地址法哈希表。
#include <iostream>
#include <list>
#include <vector>
#include <string>
class HashTable {
private:
std::vector<std::list<std::pair<std::string, int>>> table;
int capacity;
int hashFunction(const std::string& key) const {
int hash = 0;
for (char ch : key) {
hash = (hash * 31 + ch) % capacity;
}
return hash;
}
public:
HashTable(int cap) : capacity(cap), table(cap) {}
void insert(const std::string& key, int value) {
int index = hashFunction(key);
for (auto& pair : table[index]) {
if (pair.first == key) {
pair.second = value;
return;
}
}
table[index].emplace_back(key, value);
}
bool find(const std::string& key, int& value) const {
int index = hashFunction(key);
for (const auto& pair : table[index]) {
if (pair.first == key) {
value = pair.second;
return true;
}
}
return false;
}
void remove(const std::string& key) {
int index = hashFunction(key);
table[index].remove_if([&](const std::pair<std::string, int>& pair) {
return pair.first == key;
});
}
};
三、动态数组与哈希表的对比
特性 | 动态数组 | 哈希表 |
---|---|---|
数据存储 | 连续内存块 | 哈希函数映射位置 |
时间复杂度 | 插入 O(1)O(1) | 插入/查找 O(1)O(1) |
扩容 | 按固定倍率扩容 | 按负载因子扩容 |
空间利用率 | 高 | 低(存在一定冗余) |
查找效率 | 慢(线性查找) | 快 |
四、应用场景
- 动态数组:
- 常用于实现列表、栈和队列。
- 适合需要频繁按索引访问的场景。
- 哈希表:
- 用于实现键值对存储,如字典、缓存、符号表。
- 适合需要快速查找和插入的场景。
通过掌握动态数组和哈希表的原理和实现,可以应对大部分数据存储与检索需求。学习中建议亲手实现并优化这些数据结构,以加深理解和实践能力。