动态数组 哈希表

理解常用算法和数据结构是编程的重要基础,动态数组和哈希表是其中两个非常重要的容器。以下详细解释每一块内容,包括其设计原理、涉及的知识点以及代码示例。


一、动态数组

动态数组是一种可以动态调整大小的数组,它克服了普通数组固定长度的缺点。

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)
扩容 按固定倍率扩容 按负载因子扩容
空间利用率 低(存在一定冗余)
查找效率 慢(线性查找)

四、应用场景

  • 动态数组
    • 常用于实现列表、栈和队列。
    • 适合需要频繁按索引访问的场景。
  • 哈希表
    • 用于实现键值对存储,如字典、缓存、符号表。
    • 适合需要快速查找和插入的场景。

通过掌握动态数组和哈希表的原理和实现,可以应对大部分数据存储与检索需求。学习中建议亲手实现并优化这些数据结构,以加深理解和实践能力。

猜你喜欢

转载自blog.csdn.net/skx13613650153/article/details/144791191