《数据结构与算法python语言描述》3.线性表


在程序里,经常需要将一组(通常是同为某个类型的)数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。在有些情况下,可能需要把这样一组元素看成一个序列,用元素在序列里的位置和顺序,表示实际应用中的某种有意义的信息,或者表示数据之间的某种关系。
线性表(简称表)就是这样一组元素(的序列)的抽象。一个线性表是某类元素的一个集合,还记录着元素之间的一种顺序关系。

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

表的概念和性质

考虑一个(有穷或无穷的)基本元素集合 E E E,集合 E E E中的元素可能都是某个类型的数据对象。
集合 E E E上的一个线性表就是 E E E中一组有穷个元素排成的序列 L = ( e 0 , e 1 , . . . , e n − 1 ) L=(e_{0},e_{1},...,e_{n-1}) L=(e0,e1,...,en1).
一个表中包含的元素的个数称为这个表的长度。显然,空表的长度为0。
表元素之间存在一个基本关系,称为下一个关系。下一个关系是一种顺序关系,即线性关系,线性表是一种线性结构。

表抽象数据类型

一个线性表数据结构应该提供哪些操作。
1.作为抽象类型的线性表是一组数据对象的集合,应该提供创建线性表对象的操作。
2.程序中可能需要检查一个表,获取它的各方面信息。例如,可能需要判断一个表是否为空,考察其中的元素个数(求得表的长度),检查一个表里是否存在某个特定数据对象等。
3.需要动态改变表的内容,包括加入新元素或删除已有元素。
4.涉及对表中每一个元素进行的操作。

表抽象数据类型:一个表中一些有用的操作:
ADT List:     # 一个表抽象数据类型
 List(self)    # 表构造操作,创建一个新表
 is_empty(self)   # 判断是否为一个空表
 len(self)   # 获得self的长度
 prepend(self, elem)   # 将元素elem加入表中作为第一个元素
 append(self, elem)   # 将元素elem加入表中作为最后一个元素
 insert(self, elem, i)   # 将元素elem加入表中作为第i个元素,其他元素的顺序不变

线性表的实现:基本考虑

计算机内存
各种重要操作的效率
两种:顺序表和连接表

顺序表的实现

python中的list和tuple就采用了顺序表的实现技术。
顺序结构(技术)是组织一组元素的最重要方式。在顺序表结构中,直接采用顺序结构实现线性表,这种结构(技术)也是许多其他数据结构的实现基础。
采用顺序表结构实现线性表:
1.最重要特点(优势)是O(1)时间的定位元素访问。很多简单操作的效率也比较高。
2.这里最重要的麻烦是加入/删除等操作的效率问题。这类操作改变表中元素序列的结构,是典型的变动操作。由于元素在顺序表的存储区里连续排列,加入/删除操作有可能要移动很多元素,操作代价高。
3.只有特殊的尾端插人/删除操作具有O(1)时间复杂度。但插人操作复杂度还受到元素存储区固定大小的限制。通过适当的(加倍)存储区扩充策略,一系列尾端插人可以达到O(1)的平均复杂度。
顺序表的优点和缺点都在于其元素存储的集中方式和连续性。从缺点看,这样的表结构不够灵活,不容易调整和变化。如果在一个表的使用中需要经常修改结构,用顺序表去实现就不太方便,反复操作的代价可能很高。
还有一个问题也值得提出:如果程序里需要巨大的线性表,采用顺序表实现就需要巨大块的连续存储空间,这也可能造成存储管理方面的困难。

链接表

线性表的基本需要和链接表

线性表的基本需要:
1.能够找到表中的首元素;
2.从表里的任一元素出发,可以找到它之后的下一个元素。
采用链接技术实现的线性表称为链接表或者链表。
基本思想:
1.把表中的元素分别存储在一批独立的存储块(称为表的结点)里;
2.保证从组成表结构中的任一个结点可找到与其相关的下一个结点;
3.在前一个结点里用链接的方式显式地记录与下一个结点之间的关联。

单链表

单链表的结点是一个二元组,元素域和链接域
元素域elem保存着作为表元素的数据项,链接域next里保存同下一个表里的下一个结点的标识。
掌握一个单链表,只需要用一个变量保存着这个表的首结点的标识。这个变量称为表头变量或表头指针。
总结:
一个单链表由一些具体的表结点构成;
每个结点是一个对象,有自己的标识或链接;
结点之间通过结点链接建立起单向的顺序联系。

基本链表操作

创建空链表
删除链表
判断表是否为空
判断表是否满

加入元素

表首端插入
一般情况的元素插入

删除元素

删除表首元素
一般情况的元素删除

扫描、定位和遍历
求表的长度
# 定义一个简单的表结点类
class LNode:
    def __init__(self, elem, next_=None):
        """初始化,给两个域赋值"""
        self.elem = elem
        self.next = next_
    def __repr__(self):
        '''
        用来定义Node的字符输出,
        print为输出elem
        '''
        return str(self.elem)
llist1 = LNode(1)  # 表头

# 创建链表
p = llist1
for i in range(2, 11):
    p.next = LNode(i)
    p = p.next
# 打印元素
p = llist1
while p is not None:
    print(p.__repr__())
    p = p.next
1
2
3
4
5
6
7
8
9
10
自定义异常
class LinkedListUnderflow(ValueError):
    pass
LList类的定义,初始化函数和简单操作
# 定义一个简单的表结点类
class LNode:
    def __init__(self, elem, next_=None):
        """初始化,给两个域赋值"""
        self.elem = elem
        self.next = next_
    def __repr__(self):
        '''
        用来定义Node的字符输出,
        print为输出elem
        '''
        return str(self.elem)

class LinkedListUnderflow(ValueError):
    pass

class LList:
    def __init__(self):
        """初始化,建立的是一个空表"""
        self._head = None

    def is_empty(self):
        """判断是一个空表"""
        return self._head is None

    def prepend(self, elem):
        """表头插入数据"""
        self._head = LNode(elem, self._head)
    
    def pop(self):
        """删除表头结点,并返回这个结点里的数据"""
        if self._head is None: # 无结点,引发异常
            raise LinkedListUnderflow("in pop")
        e = self._head.elem
        self._head = self._head.next
        return e

    def append(self, elem):
        """链表最后插入元素"""
        if self._head is None:
            self._head = LNode(elem)
            return
        p = self._head
        while p.next is not None:
            p = p.next
        p.next = LNode(elem)

    def pop_last(self):
        """删除链表最后一个元素"""
        if self._head is None: # 空表
            raise LinkedListUnderflow("in pop_last")
        p = self._head
        if p.next is None: # 只有一个元素
            e = p.elem
            self._head = None
            return e
        while p.next.next is not None:
            p = p.next
        e = p.next.elem
        p.next = None
        return e

    def find(self, pred):
        """找到满足给定条件的表元素,参数提供一个判断谓词"""
        p = self._head
        while p is not None:
            if pred(p.elem):
                return p.elem
            p = p.next
    
    def printall(self):
        p = self._head
        while p is not None:
            print(p.elem, end='')
            if p.next is not None:
                print('   ', end='')
            p = p.next
        print('')
    
    def for_each(self, proc):
        """遍历,参数:给定的操作"""
        p = self._head
        while p is not None:
            proc(p.elem)
            p = p.next

    def elements(self):
        """定义迭代器"""
        p = self._head
        while p is not None:
            yield p.elem
            p = p.next
            
    def filter(self, pred):
        """筛选生成器,参数pred是谓词参数"""
        p = self._head
        while p is not None:
            if pred(p.elem):
                yield p.elem
            p = p.next
mlist1 = LList()
for i in range(10):
    mlist1.prepend(i)
for i in range(11, 20):
    mlist1.append(i)
mlist1.printall()
mlist1.for_each(print)

for x in mlist1.elements():
    print(x)
9   8   7   6   5   4   3   2   1   0   11   12   13   14   15   16   17   18   19
9
8
7
6
5
4
3
2
1
0
11
12
13
14
15
16
17
18
19
9
8
7
6
5
4
3
2
1
0
11
12
13
14
15
16
17
18
19

链表的变形和操作

单链表的简单变形
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值