顺序表用Python3定义与增删改查

0 阅读6分钟

顺序表用Python3定义与增删改查


一、顺序表的设计

核心要素:

属性含义
data底层数组,存元素
size当前元素个数
capacity当前容量

关键点

  • 元素存在 data[0..size-1],后面的位置空着。
  • 插入/删除需要搬移元素
  • 容量不够时扩容

二、完整代码

class SeqList:
    """顺序表(动态数组实现)"""

    def __init__(self, capacity=10):
        if capacity <= 0:
            raise ValueError("容量必须大于 0")
        self.data = [None] * capacity   # 底层数组
        self.size = 0                   # 当前元素个数
        self.capacity = capacity        # 当前容量

    # ---------- 内部辅助 ----------
    def _resize(self, new_capacity):
        """扩容/缩容:把原数据搬到新数组"""
        new_data = [None] * new_capacity
        for i in range(self.size):
            new_data[i] = self.data[i]
        self.data = new_data
        self.capacity = new_capacity

    def _check_index(self, index):
        """检查访问索引是否合法"""
        if index < 0 or index >= self.size:
            raise IndexError(f"索引 {index} 越界,合法范围 [0, {self.size - 1}]")

    def _check_insert_index(self, index):
        """检查插入索引是否合法(可以等于 size)"""
        if index < 0 or index > self.size:
            raise IndexError(f"插入索引 {index} 越界,合法范围 [0, {self.size}]")

    # ---------- 增 ----------
    def insert(self, index, value):
        """在 index 位置插入 value,O(n)"""
        self._check_insert_index(index)

        # 容量不够,扩容为原来的 2 倍
        if self.size == self.capacity:
            self._resize(self.capacity * 2)

        # 从后往前搬移,给新元素腾位置
        for i in range(self.size, index, -1):
            self.data[i] = self.data[i - 1]

        self.data[index] = value
        self.size += 1

    def append(self, value):
        """尾部追加,均摊 O(1)"""
        self.insert(self.size, value)

    def prepend(self, value):
        """头部插入,O(n)"""
        self.insert(0, value)

    # ---------- 删 ----------
    def delete(self, index):
        """删除 index 位置元素,返回被删的值,O(n)"""
        self._check_index(index)

        value = self.data[index]

        # 从前往后搬移,覆盖被删元素
        for i in range(index, self.size - 1):
            self.data[i] = self.data[i + 1]

        self.data[self.size - 1] = None   # 末尾置空
        self.size -= 1

        # 缩容:元素太少时,容量减半(可选优化)
        if self.size > 0 and self.size <= self.capacity // 4:
            self._resize(max(self.capacity // 2, 10))

        return value

    def remove(self, value):
        """按值删除第一个匹配项,O(n)"""
        index = self.index_of(value)
        if index == -1:
            raise ValueError(f"值 {value} 不存在")
        return self.delete(index)

    def pop(self):
        """删除尾部,返回被删的值,O(1)"""
        if self.size == 0:
            raise IndexError("顺序表为空")
        return self.delete(self.size - 1)

    def clear(self):
        """清空"""
        self.data = [None] * self.capacity
        self.size = 0

    # ---------- 改 ----------
    def update(self, index, value):
        """修改 index 位置的值,O(1)"""
        self._check_index(index)
        self.data[index] = value

    # ---------- 查 ----------
    def get(self, index):
        """按索引获取值,O(1)"""
        self._check_index(index)
        return self.data[index]

    def index_of(self, value):
        """按值查找索引,找不到返回 -1,O(n)"""
        for i in range(self.size):
            if self.data[i] == value:
                return i
        return -1

    def contains(self, value):
        """判断是否包含某值,O(n)"""
        return self.index_of(value) != -1

    # ---------- 其他 ----------
    def is_empty(self):
        return self.size == 0

    def length(self):
        return self.size

    def __len__(self):
        return self.size

    def __getitem__(self, index):
        """支持 lst[index] 访问"""
        return self.get(index)

    def __setitem__(self, index, value):
        """支持 lst[index] = value"""
        self.update(index, value)

    def __contains__(self, value):
        """支持 value in lst"""
        return self.contains(value)

    def __iter__(self):
        """支持 for x in lst"""
        for i in range(self.size):
            yield self.data[i]

    def __str__(self):
        return "[" + ", ".join(str(self.data[i]) for i in range(self.size)) + "]"

    def __repr__(self):
        return f"SeqList({self})"

三、测试代码

if __name__ == "__main__":
    sl = SeqList()

    # ---------- 增 ----------
    sl.append(10)
    sl.append(20)
    sl.append(30)
    print(sl)              # [10, 20, 30]

    sl.insert(1, 15)
    print(sl)              # [10, 15, 20, 30]

    sl.prepend(5)
    print(sl)              # [5, 10, 15, 20, 30]

    # ---------- 查 ----------
    print(sl.get(0))       # 5
    print(sl[2])           # 15
    print(sl.index_of(20)) # 3
    print(20 in sl)        # True
    print(len(sl))         # 5

    # ---------- 改 ----------
    sl.update(0, 100)
    print(sl)              # [100, 10, 15, 20, 30]

    sl[1] = 200
    print(sl)              # [100, 200, 15, 20, 30]

    # ---------- 删 ----------
    sl.delete(0)
    print(sl)              # [200, 15, 20, 30]

    sl.remove(15)
    print(sl)              # [200, 20, 30]

    sl.pop()
    print(sl)              # [200, 20]

    # ---------- 遍历 ----------
    for x in sl:
        print(x, end=" ")  # 200 20
    print()

    # ---------- 清空 ----------
    sl.clear()
    print(sl)              # []
    print(sl.is_empty())   # True

    # ---------- 扩容测试 ----------
    sl2 = SeqList(capacity=2)
    for i in range(10):
        sl2.append(i)
    print(sl2)             # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(sl2.capacity)    # 16(扩容过程:2→4→8→16)

四、关键操作图解

1. 插入 insert(2, 99)

原数组: [10, 20, 30, 40, _]
size=4

从后往前搬移:
[10, 20, 30, 40, _]
[10, 20, 30, 40, 40]   ← data[4] = data[3]
[10, 20, 30, 30, 40]   ← data[3] = data[2]
[10, 20, 99, 30, 40]   ← data[2] = 99

结果: [10, 20, 99, 30, 40]
size=5

2. 删除 delete(1)

原数组: [10, 20, 30, 40, 50]
size=5

从前往后搬移:
[10, 20, 30, 40, 50]
[10, 30, 30, 40, 50]   ← data[1] = data[2]
[10, 30, 40, 40, 50]   ← data[2] = data[3]
[10, 30, 40, 50, 50]   ← data[3] = data[4]

末尾置空:
[10, 30, 40, 50, None]

结果: [10, 30, 40, 50]
size=4

3. 扩容

容量满: [10, 20, 30, 40]  capacity=4

申请新数组(2 倍): [None] * 8
搬移: [10, 20, 30, 40, None, None, None, None]
插入: [10, 20, 30, 40, 50, None, None, None]
capacity=8

五、复杂度总结

操作时间复杂度说明
按索引访问 getO(1)连续内存,直接定位
按索引修改 updateO(1)直接定位
尾部追加 appendO(1) 均摊偶尔扩容,均摊下来 O(1)
头部插入 prependO(n)所有元素后移
中间插入 insertO(n)部分元素后移
删除 deleteO(n)部分元素前移
按值查找 index_ofO(n)遍历
按值删除 removeO(n)查找 + 删除

六、顺序表 vs 链表

对比项顺序表链表
内存连续分散
随机访问O(1)O(n)
头部插入/删除O(n)O(1)
尾部插入O(1) 均摊O(n)(无尾指针)
中间插入/删除O(n)O(n)(定位)+ O(1)(改指针)
按值查找O(n)O(n)
扩容需要搬移不需要
内存开销可能浪费额外指针开销
缓存友好

选择原则

  • 频繁随机访问 → 顺序表。
  • 频繁头部/中间增删 → 链表。
  • 大多数场景 → Python 的 list 就够用。

七、常见坑

说明正确做法
插入位置越界插入可以等于 size判断 index > size
访问位置越界访问必须小于 size判断 index >= size
删除后忘记 size -= 1长度错误删完减 1
删除后忘记置空内存泄漏data[size-1] = None
扩容忘记搬移数据丢失逐个复制
缩容太频繁性能抖动用 1/4 阈值,不要用 1/2
list 模拟时混淆 size 和 len概念混淆size 是逻辑长度,capacity 是物理容量

为什么缩容阈值用 1/4 而不是 1/2?

  • 如果用 1/2,在临界点反复插入删除会导致频繁扩容缩容(抖动)。
  • 用 1/4,留出缓冲,避免抖动。

八、一句话总结

问题答案
顺序表是什么?用连续内存存储的线性表
核心属性?data(数组)、size(元素个数)、capacity(容量)
增?insertappendprepend,容量不够扩容
删?deleteremovepop,搬移元素
改?update,O(1)
查?get(O(1))、index_of(O(n))
最大优势?随机访问 O(1)
最大劣势?中间插入/删除 O(n)
和 list 关系?Python 的 list 就是动态顺序表
和链表区别?顺序表连续、随机访问快;链表分散、增删快

核心记住:顺序表 = 连续内存 + size + capacity。随机访问 O(1),中间增删 O(n),容量不够要扩容。手写一遍能彻底理解 Python list 的底层原理。