数据结构Python版(一)——顺序表

19 篇文章 94 订阅

一、什么是线性表?

线性表就是数据元素的排列像一条线一样的表。线性表严格的定义是具有相同特性的数据元素组成的一个有限序列。其特征有3个:

  • 所有元素的数据类型相同;
  • 线性表是由有限个元素构成的;
  • 线性表中的元素与位置相关,即每个元素都有唯一的序号(或索引)。

不同于集合,线性表中可以出现值相同的元素。

线性表的逻辑结构一般表示为 ( a 0 , a 1 , ⋯   , a n − 1 ) (a_0,a_1,\cdots,a_{n-1}) (a0,a1,,an1),用 n ( n ≥ 0 ) n(n\geq0) n(n0) 表示线性表的长度(即线性表中元素的个数)。当 n = 0 n=0 n=0 时,表示线性表是一个空表,不包含任何数据元素。

在这里插入图片描述

线性表的逻辑特征:每个元素最多只有一个前驱元素,也最多只有一个后继元素。

二、线性表的抽象数据类型描述

数 据 对 象 : D = { a i ∣ 0 ≤ i ≤ n − 1 , n ≥ 0 , a i 为 E 类 型 } 数 据 关 系 : r = { < a i , a i + 1 > ∣ a i , a i + 1 ∈ D , i = 0 , ⋯   , n − 2 } 基 本 运 算 : fromlist(a) : 根 据 数 组 a 的 全 部 元 素 建 立 线 性 表 add(e) : 将 元 素 e 添 加 到 线 性 表 的 末 尾 insert(i,e) : 插 入 元 素 e 作 为 序 号 i 的 元 素 delete(i) : 删 除 序 号 为 i 的 元 素 getelem(i) : 获 取 序 号 为 i 的 元 素 setelem(i,e) : 设 置 序 号 i 的 元 素 值 为 e getno(e) : 求 线 性 表 中 第 一 个 值 为 e 的 元 素 的 序 号 getsize() : 获 取 线 性 表 的 长 度 display() : 输 出 线 性 表 的 所 有 元 素 \begin{aligned} &数据对象:D=\{a_i|0\leq i\leq n-1,n\geq0,a_i为E类型\} \\ &数据关系:r=\{<a_i,a_{i+1}>|a_i,a_{i+1}\in D,i=0,\cdots,n-2\} \\ &基本运算:\\ &\quad \text{fromlist(a)}:根据数组a的全部元素建立线性表 \\ &\quad \text{add(e)}:将元素e添加到线性表的末尾 \\ &\quad \text{insert(i,e)}:插入元素e作为序号i的元素 \\ &\quad \text{delete(i)}:删除序号为i的元素 \\ &\quad \text{getelem(i)}:获取序号为i的元素 \\ &\quad \text{setelem(i,e)}:设置序号i的元素值为e \\ &\quad \text{getno(e)}:求线性表中第一个值为e的元素的序号 \\ &\quad \text{getsize()}:获取线性表的长度 \\ &\quad \text{display()}:输出线性表的所有元素 \\ \end{aligned} D={ai0in1,n0,aiE}r={<ai,ai+1>ai,ai+1D,i=0,,n2}fromlist(a)a线add(e)e线insert(i,e)eidelete(i)igetelem(i)isetelem(i,e)iegetno(e)线egetsize()线display()线

三、顺序表的基本概念

线性表有两种存储结构:顺序存储链式存储,本文将主要讲解顺序存储。

线性表的顺序存储是把线性表中的所有元素按照其逻辑顺序依次存储到存储器中一块连续的存储空间。线性表的顺序存储结构称为顺序表

接下来我们将采用Python中的列表来实现顺序表。顺序表通常会有一个最大容量 capacity,当要存储的元素个数大于 capacity 时,我们可以对顺序表进行扩容(例如将 capacity 扩大两倍)。

class SqList:

    def __init__(self, capacity):
        self.capacity = capacity  # 最大容量
        self.data = [None] * self.capacity  # 用于存储数据
        self.size = 0  # 顺序表的元素个数

我们可以创建一个 resize 函数用来修改线性表的最大容量:

def resize(self, newcapacity):
    assert newcapacity >= 0
    if newcapacity >= self.capacity:
        self.data = self.data + [None] * (newcapacity - self.capacity)
    else:
        self.data = self.data[:newcapacity]
    self.capacity = newcapacity

四、顺序表的实现

4.1 从数组中建立顺序表

给定列表 a,我们需要通过该列表来建立相对应的顺序表。

def fromlist(self, a):
    for i in range(len(a)):
        # 出现上溢出时将容量扩大2倍
        if self.size == self.capacity:
            self.resize(self.size * 2)
        # 因为self.size是从0开始递增的
        self.data[self.size] = a[i]
        self.size += 1

时间复杂度为 O ( n ) \mathcal{O}(n) O(n),其中 n n n 表示顺序表中的元素个数。

4.2 将元素添加到顺序表末尾

def add(self, e):
	# 防止溢出
    if self.size == self.capacity:
        self.resize(self.size * 2)
    self.data[self.size] = e
    self.size += 1

4.3 插入元素

要在位置 i i i 处插入元素 e e e,我们需要将 self.data[i, i+1, ..., n-1] 的每个元素均后移一位。

def insert(self, i, e):
    assert 0 <= i <= self.size
    if self.size == self.capacity:
        self.resize(self.size * 2)
    for j in range(self.size, i, -1):
        self.data[j] = self.data[j - 1]
    self.data[i] = e
    self.size += 1

平均时间复杂度: O ( n ) \mathcal{O}(n) O(n)

4.4 删除元素

要删除位置 i i i 处的元素,我们需要将 self.data[i+1, ..., n-1] 的每个元素均前移一位。

def delete(self, i):
    assert 0 <= i < self.size
    for j in range(i, self.size - 1):
        self.data[j] = self.data[j + 1]
    self.size -= 1
    # 自适应缩小容量以节省空间
    if self.size < self.capacity / 2:
        self.resize(self.capacity / 2)

平均时间复杂度: O ( n ) \mathcal{O}(n) O(n)

4.5 获取元素

def __getitem__(self, i):
    assert 0 <= i < self.size
    return self.data[i]

4.6 设置元素

def __setitem__(self, i, x):
    assert 0 <= i < self.size
    self.data[i] = x

4.7 查找第一个为e的序号

def getno(self, e):
    for i in range(self.size):
        if self.data[i] == e:
            return i
    # 未找到返回-1
    return -1

4.8 获取顺序表长度

def getsize(self):
    return self.size

4.9 打印顺序表

def display(self):
    print(*self.data[:self.size])

五、验证我们的顺序表

sqlist = SqList(10)
sqlist.fromlist(list(range(10)))
sqlist.display()
# 0 1 2 3 4 5 6 7 8 9
sqlist.add(33)
sqlist.insert(3, 19)
sqlist.delete(0)
sqlist.display()
# 1 2 19 3 4 5 6 7 8 9 33
sqlist[10] = 3
print(sqlist[1])
# 2
print(sqlist.getno(3))
# 3
print(sqlist.getsize())
# 11
sqlist.display()
# 1 2 19 3 4 5 6 7 8 9 3

六、顺序表的一些应用

6.1 翻转顺序表

即若原始顺序表为 ( 4 , 3 , 2 , 1 ) (4,3,2,1) (4,3,2,1),翻转后的顺序表应为 ( 1 , 2 , 3 , 4 ) (1,2,3,4) (1,2,3,4)

def reverse(sqlist):
    i, j = 0, sqlist.getsize() - 1
    while i < j:
        sqlist[i], sqlist[j] = sqlist[j], sqlist[i]
        i += 1
        j -= 1


sqlist = SqList(5)
sqlist.fromlist(list(range(5)))
sqlist.display()
# 0 1 2 3 4
reverse(sqlist)
sqlist.display()
# 4 3 2 1 0

时间复杂度 O ( n ) \mathcal{O}(n) O(n),空间复杂度 O ( 1 ) \mathcal{O}(1) O(1)

6.2 删除k个元素

设计一个算法从位置 i i i 开始删除其后的 k k k 个元素(包括 i i i)。例如设 L = ( 1 , 2 , 3 , 4 , 5 ) , i = 1 , k = 2 L=(1,2,3,4,5),i=1,k=2 L=(1,2,3,4,5),i=1,k=2,则执行操作后 L = ( 1 , 4 , 5 ) L=(1,4,5) L=(1,4,5)

显然需要有 i ≥ 0 , k ≥ 1 i\geq0,k\geq1 i0,k1,删除的最后一个元素索引为 i + k − 1 i+k-1 i+k1,需满足 0 ≤ i + k − 1 ≤ n − 1 0\leq i+k-1\leq n-1 0i+k1n1,即 1 ≤ i + k ≤ n 1\leq i+k\leq n 1i+kn

def delete_k(sqlist, i, k):
    assert i >= 0 and k >= 1 and 1 <= i + k <= sqlist.getsize()
    for j in range(i + k, sqlist.getsize()):
        sqlist[j - k] = sqlist[j]
    sqlist.size -= k


sqlist = SqList(5)
sqlist.fromlist(list(range(5)))
sqlist.display()
# 0 1 2 3 4
delete_k(sqlist, 1, 2)
sqlist.display()
# 0 3 4

时间复杂度 O ( n ) \mathcal{O}(n) O(n)

6.3 二路归并

有序表是按元素值或某属性值递增或递减排列的线性表。有序表是线性表的一个子集。有序顺序表是有序表的顺序存储结构。对于有序表,可以利用其元素的有序性提高相关算法的效率,二路归并就是有序表的一个经典算法。

考虑两个递增的有序顺序表 A A A B B B,设计一个算法将 A A A B B B 合并在一起并构成一个新的递增有序顺序表 C C C

我们可以设置两个指针 i , j i,j i,j,用 i i i 遍历 A A A,用 j j j 遍历 B B B。初始时, i = j = 0 i=j=0 i=j=0。每次比较 i i i j j j 所指的元素的大小,将较小者添加到 C C C 中,且向右移动指向较小者的指针,不断重复这个操作。

def merge(A, B):
    C = SqList(A.capacity + B.capacity)
    i = j = 0
    while i < A.getsize() and j < B.getsize():
        if A[i] < B[j]:
            C.add(A[i])
            i += 1
        else:
            C.add(B[j])
            j += 1
    # 若i,j并非同时到达终点,则必会有一个顺序表还残留元素,此时只需将这些元素全部加入C中即可
    while i < A.getsize():
        C.add(A[i])
        i += 1
    while j < B.getsize():
        C.add(B[j])
        j += 1
    return C


A = SqList(5)
A.fromlist(list(range(1, 11, 2)))
B = SqList(5)
B.fromlist(list(range(2, 11, 2)))
A.display()
# 1 3 5 7 9
B.display()
# 2 4 6 8 10
C = merge(A, B)
C.display()
# 1 2 3 4 5 6 7 8 9 10

附录:顺序表完整代码

class SqList:

    def __init__(self, capacity):
        self.capacity = capacity
        self.data = [None] * self.capacity
        self.size = 0

    def resize(self, newcapacity):
        assert newcapacity >= 0
        if newcapacity >= self.capacity:
            self.data = self.data + [None] * (newcapacity - self.capacity)
        else:
            self.data = self.data[:newcapacity]
        self.capacity = newcapacity

    def fromlist(self, a):
        for i in range(len(a)):
            if self.size == self.capacity:
                self.resize(self.size * 2)
            self.data[self.size] = a[i]
            self.size += 1

    def add(self, e):
        if self.size == self.capacity:
            self.resize(self.size * 2)
        self.data[self.size] = e
        self.size += 1

    def insert(self, i, e):
        assert 0 <= i <= self.size
        if self.size == self.capacity:
            self.resize(self.size * 2)
        for j in range(self.size, i, -1):
            self.data[j] = self.data[j - 1]
        self.data[i] = e
        self.size += 1

    def delete(self, i):
        assert 0 <= i < self.size
        for j in range(i, self.size - 1):
            self.data[j] = self.data[j + 1]
        self.size -= 1
        if self.size < self.capacity / 2:
            self.resize(self.capacity / 2)

    def __getitem__(self, i):
        assert 0 <= i < self.size
        return self.data[i]

    def __setitem__(self, i, x):
        assert 0 <= i < self.size
        self.data[i] = x

    def getno(self, e):
        for i in range(self.size):
            if self.data[i] == e:
                return i
        return -1

    def getsize(self):
        return self.size

    def display(self):
        print(*self.data[:self.size])
  • 40
    点赞
  • 193
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
数据结构与算法(Python) 一、引入概念 1-01算法引入 1-02 时间复杂度与大O表示法 1-03-最坏时间复杂度与计算规则 1-04-常见时间复杂度与大小关系 1-05-代码执行时间测量模块 1-06-Python列表类型不同操作的时间效率 1-07-Python列表与字典操作的时间复杂度 1-08-数据结构引入 二、顺序表 2-01 内存、类型本质、连续存储 recv 2-02 基本顺序表与元素外围顺序表 recv 2-03 顺序表的一体式结构与分离式结构 recv 2-04 顺序表数据区替换与扩充 recv 三、栈 3-01 栈与队列的概念 3-02 栈的实现 3-03 队列与双端队列的实现 四、链表 4-01 链表的提出 4-02 单链表的ADT模型 4-03 Python中变量标识的本质 4-04 单链表及结点的定义代码 4-05 单链表的判空、长度、遍历与尾部添加结点的代码实现 4-06 单链表尾部添加和在指定位置添加 4-07 单链表查找和删除元素 4-08 单链表顺序表的对比 4-09 单向循环链表遍历和求长度 4-10 单向循环链表添加元素 4-11 单向循环链表删除元素 4-12 单向循环链表删除元素复习及链表扩展 4-13 双向链表及添加元素 4-14 双向链表删除元素 五、排序与搜索 5-01 排序算法的稳定性 5-02 冒泡排序及实现 5-03 选择排序算法及实现 5-04 插入算法 5-05 插入排序 5-06 插入排序2 5-07 希尔排序 5-08 希尔排序实现 5-09 快速排序 5-10 快速排序实现1 (1) 5-10 快速排序实现1 5-11 快速排序实现2 5-12 归并排序 5-13 归并排序 代码执行流程 5-14 归并排序时间复杂度及排序算法复杂度对比 5-15 二分查找 5-16 二分查找时间复杂度 六、树和树的算法 6-01 树的概念 6-02 二叉树的概念 6-03 二叉树的广度优先遍历 6-04 二叉树的实现 6-05 二叉树的先序、中序、后序遍历 6-06 二叉树由遍历确定一棵树 ———————————————— 权声明:本文为CSDN博主「dwf1354046363」的原创文章,遵循CC 4.0 BY-SA权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/dwf1354046363/article/details/119832814

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Iareges

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值