《数据结构与算法:Python语言描述》一第3章 线 性 表

本节书摘来自华章出版社《数据结构与算法:Python语言描述》一书中的第3章,第3.1节,作者 裘宗燕,更多章节内容可以访问云栖社区“华章计算机”公众号查看

第3章 线 性 表

在程序里,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化(可以加入或删除元素)。在有些情况下,可能需要把这样一组元素看成一个序列,用元素在序列里的位置和顺序,表示实际应用中的某种有意义的信息,或者表示数据之间的某种关系。线性表(简称表)就是这样一组元素(的序列)的抽象。一个线性表是某类元素的一个集合,还记录着元素之间的一种顺序关系。
线性表是最基本的数据结构之一,在实际程序中应用非常广泛,它还经常被用作更复杂的数据结构的实现基础。在实际应用中可能需要各种各样的线性表,如整数的表、字符串的表、某种复杂结构的表等。Python语言的内置类型list和tuple都以具体的方式支持程序里的这类需要,它们都可以看作线性表的实现。
本章将讨论线性表概念及其两种基本实现方法、若干变形和一些应用实例。

3.1线性表的概念和表抽象数据类型

表可以看作一种抽象的(数学)概念,也可以作为一种抽象数据类型。本节首先介绍表的概念和它的一些抽象性质,而后定义一个表抽象数据类型。

3.1.1表的概念和性质

在抽象地讨论线性表时,首先要考虑一个(有穷或无穷的)基本元素集合 E,集合E中的元素可能都是某个类型的数据对象。
集合E上的一个线性表就是E中一组有穷个元素排成的序列L=(e0, e1, …, en-1),其中ei∈E且n≥0。在一个表里可以包含0个或多个元素,序列中的每个元素在表里有一个确定的位置,称为该元素的下标。在本书的讨论中,下标总是从0开始编号(一些书籍中选择从1开始)。不包含任何元素的表称为空表。
一个表中包含的元素的个数称为这个表的长度。显然,空表的长度为0。
表元素之间存在一个基本关系,称为下一个关系。对于表L=(e0, e1,…, en-1),其下一个关系是二元组的集合{, , …, }。下一个关系是一种顺序关系,即线性关系,线性表是一种线性结构。
在一个非空的线性表里,存在着唯一的一个首元素和唯一的一个尾元素(或称末元素)。除首元素之外,表中的每个元素e都有且仅有一个前驱元素;除了尾元素之外的每个元素都有且仅有一个后继元素。
可以把线性表作为一种数学对象加以研究,为之建立抽象的数学模型,研究其性质。但就本书的目的而言,重要的是把线性表作为程序中使用的一种数据类型和构造数据结构的一种基本方式。下面讨论这方面的问题。

3.1.2表抽象数据类型

从实际编程和应用的角度看,线性表是一种组织数据元素的数据结构,现在考虑如何将其定义为一种抽象数据类型。作为一种抽象描述,有两类人群需要从各自角度考虑这种类型的问题:线性表的实现者和它的使用者:
从实现者角度,对于一种数据结构有两个问题必须考虑:①如何把该结构内部的数据组织好(为它设计一种合适的表示);②如何提供一套有用而且必要的操作,并有效实现这些操作。显然,这两个问题相互有关联。
从使用者角度看,线性表是一种有用的结构。需要考虑该结构提供了哪些操作,如何有效使用以解决自己的问题。实际使用会对表的实现者提出一些要求。
显然,不仅对于线性表存在这样的两个视角,并因为视角的不同带来了一系列相关问题,对于其他数据结构也都如此。在设计、定义和使用抽象数据类型时,都会遇到这两个不同的观察角度及其相互关系,它们既统一又有分工。这种情况与函数的定义与使用类似。因此,下面讨论中的一些考虑具有普遍意义。
在一个数据结构里,通常需要保存一些信息,需要把这些信息以某种形式存储起来。数据结构的具体表示完全是其内部的东西,在数据结构之外看不到也不应该关心。但具体的表示可能对这一数据结构上各种操作的实现和性质产生重要影响。对于比较复杂的数据结构,可能存在多种不同的表示方式(后面会看到更多具体例子),设计时需要考虑的因素很多,其中利弊得失的权衡可能很复杂,不容易做出有理有据的决策。数据结构课程提供的信息就是为了帮助人们理解这些问题,理解常见的各种重要利弊权衡。
线性表的操作
下面首先从使用者的角度,考虑一个线性表数据结构应该提供哪些操作。
首先,作为抽象类型的线性表是一组数据对象的集合,应该提供创建线性表对象的操作。一种简单操作是创建空表对象,这时不需要提供其他信息。如果需要创建包含一些元素的表,就要考虑如何为创建操作提供初始元素序列的问题。
程序中可能需要检查一个表,获取它的各方面信息。例如,可能需要判断一个表是否为空,考察其中的元素个数(求得表的长度),检查一个表里是否存在某个特定数据对象等。为此需要定义一些获取表中信息的解析操作。
需要动态改变表的内容,包括加入新元素或删除已有元素。加入新元素有一些不同方式,如简单加入只要求新元素加入表中,定位加入要求把新元素存放在表中的确定位置。删除元素可以是定位删除,删掉某位置的元素,或是按内容删去特定元素。后一种删除还有删除一个元素或删除等于某指定元素的所有元素的问题。例如,可能要求删除一个整数表里的一个0元素,或者所有等于0的元素。此外,还可以是给定一个条件,要求删除所有满足(或不满足)条件的元素。例如删除一个整数表里所有负数。
一些涉及一个或两个表的操作。例如表的组合操作,希望得到一个表,其中包含了两个表中所有元素的表;或者从已有的表得到一个新表,其中每个元素都是原表中的元素按某种规则(操作)修改后的结果;等等。
涉及对表中每一个元素进行的操作。注意,这是一个操作类,给定任何一个对单个表元素的操作,就有一个与之对应的对表中所有元素进行的操作。这类操作在进行中需要逐个访问表中元素,对每个元素做同样的事情。这是一种操作模式,称为对表元素的遍历。下面也将考虑表的遍历问题。
最后这两类操作都可以实现为变动操作,让它们实际修改被操作的表,在该表上直接实现操作的效果;也可以实现为非变动操作,让操作总是建立一个新表,与原表相比是多了(加入了)或少了(删除了)一个或一些元素,或者每个元素都按特定方式修改。
上面讨论至少可以给读者一个印象:如何设计一类表对象,并没有一个固有正确的操作集合,而是有许多可能,需要根据实际需要考虑问题并做出选择。
上面列出的这些操作中,有些是所有数据结构都需要的“标准操作”。例如结构的创建操作、对组合结构的判空操作。对元素个数可能变化的对象,需要有检查元素个数的操作。不同编程语言或实现方式也可能影响需要实现的操作集合。在一些语言环境里(例如C程序语言),一个数据结构不再使用时,需要显式地执行操作去销毁它,释放该结构占用的存储资源。由于Python系统能自动处理这方面事务,在这里就不必考虑销毁操作的问题。对于数据结构的一些实现方式,其中组合结构的容量有限,已经装满元素的结构将不能再加入新元素;如果采用这样的实现,就必须定义判断结构满的操作。
另外,一种具体的数据结构可能提供一些特殊的专门操作。例如,集合数据结构需要支持各种常用集合运算(求并集、交集、补集等);图数据结构要提供判断结点是否相邻(两点间是否有边)的操作。
表抽象数据类型
现在考虑一个简单的表抽象数据类型,其中定义了一组最基本的操作。由于是计划用Python的类来实现这种类型,定义中的有些设计反映了这方面考虑。但从整体上看,这个抽象数据类型的描述是一般性的,并不依赖于Python语言。
image

在上面定义中,借用了Python语言及其类定义的一些表达方式。各种操作中的self参数表示被操作的List对象,其他参数用于为操作提供信息。其中的elem参数均表示参与操作的表元素,i表示表中元素的位置(下标),最后一个操作的op参数表示对表元素的某个具体操作,应该在实际调用遍历操作时提供。
根据前面的讨论,显然还可以为这个类型增加更多操作,但上面这些基本操作已能反映表操作的各方面特征。下面的讨论将基于这组操作进行。
显而易见,上面定义的抽象数据类型描述的是一类变动数据对象,其中的prepend、append等操作都是修改被操作的表对象本身,其操作效果体现在这个表的修改上。完全可以定义一个非变动的表抽象数据类型,为此只需把上面ADT里修改表的操作都变成构造新表的操作,在新表里体现各种操作的效果。这一工作留给读者自己完成。
3.1.3线性表的实现:基本考虑
下面研究表数据结构的实现问题,这里主要需要考虑两方面情况:
1)计算机内存的特点,以及保存元素和元素顺序信息的需要。
2)各种重要操作的效率。如果程序里需要一个表,其创建操作只执行一次,但在其存在期间,可能反复地多次以各种方式使用,其中使用最频繁的操作通常包括表的性质判断函数is_empty等,还有(定位)访问、加入和删除元素,元素遍历等。在考虑表的实现结构时,需要特别考虑这些操作的实现效率。
另外,元素遍历就是依次访问表里的所有(或一批)元素,操作效率与访问元素的个数有关。由于是遍历所有元素,自然希望完成整个的复杂度不超过O(n)。下面可以看到,加入/删除/访问元素的操作效率与表的实现结构有关。
基于各方面考虑,人们提出了两种基本的实现模型:
1)将表中元素顺序地存放在一大块连续的存储区里,这样实现的表也称为顺序表(或连续表)。在这种实现中,元素间的顺序关系由它们的存储顺序自然表示。
2)将表元素存放在通过链接构造起来的一系列存储块里,这样实现的表称为链接表,简称链表。
参考这两种基本实现模型,还可以开发出一些不同的具体实现技术。不同实现方式在一些方面各有长短,实际中可以根据具体情况和需要选择合适的技术,有关问题下面讨论。3.2节首先讨论顺序表的实现,3.3节讨论链表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 是一种高级语言,其语法简单易懂,非常适合用来实现数据结构和算法。下面介绍几种常用的数据结构和算法的 Python 实现。 1. 数组 在 Python 中,可以用列(list)来实现数组。例如,以下代码实现了一个长度为 5 的整型数组: ``` arr = [0] * 5 ``` 2. 栈 栈是一种后进先出(LIFO)的数据结构,可以用 Python 的列来实现。例如,以下代码实现了一个栈: ``` stack = [] # 初始化一个空栈 # 入栈 stack.append(1) stack.append(2) stack.append(3) # 出栈 top = stack.pop() # 弹出栈顶元素(3) ``` 3. 队列 队列是一种先进先出(FIFO)的数据结构,可以用 Python 的列来实现。例如,以下代码实现了一个队列: ``` queue = [] # 初始化一个空队列 # 入队 queue.append(1) queue.append(2) queue.append(3) # 出队 front = queue.pop(0) # 弹出队首元素(1) ``` 4. 链是一种通过指针链接各个节点的数据结构,可以用 Python 的类来实现。例如,以下代码实现了一个链节点和链: ``` class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next class LinkedList: def __init__(self): self.head = None # 在链末尾插入一个节点 def append(self, val): if not self.head: self.head = ListNode(val) else: curr = self.head while curr.next: curr = curr.next curr.next = ListNode(val) # 删除链中第一个值为val的节点 def delete(self, val): if not self.head: return if self.head.val == val: self.head = self.head.next return curr = self.head while curr.next: if curr.next.val == val: curr.next = curr.next.next return curr = curr.next ``` 5. 递归 递归是一种通过函数调用自身来解决问题的方法,可以用 Python 的函数来实现。例如,以下代码实现了一个递归函数,计算斐波那契数列第 n 项的值: ``` def fib(n): if n == 0 or n == 1: return n return fib(n-1) + fib(n-2) ``` 6. 排序 排序是一种将数据按照指定规则进行排序的算法,可以用 Python 的内置函数来实现。例如,以下代码实现了一个简单的选择排序: ``` def selection_sort(arr): n = len(arr) for i in range(n): min_idx = i for j in range(i+1, n): if arr[j] < arr[min_idx]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] ``` 以上是 Python 实现常用的数据结构和算法的简单介绍,希望对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值