【数据结构】03-线性表

三、线性表

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

3.1.1概念

包括顺序表和链接表,从使用的角度,需要的提供的操作有哪些呢?
1、表的创建以及元素的序列问题
2、表的宏观检查:是否为空、长度
3、表的增删改查
4、多表操作,交集、并集、差集等
5、表元素的遍历操作(apply 函数)
6、表的销毁:这一部分和编程语言有关,C语言需要手动销毁释放资源、python会自动处理。

3.1.2 表的抽象数据类型

在这里插入图片描述
汇总:增删改查与遍历操作。有些操作进一步细化了。

3.1.3 线性表实现的基本考虑

两种基本的实现模型:顺序表和链接表

3.2 顺序表的实现

元素存储在连续的存储区域里面。

3.2.1 基本实现方式

基本的方式是同类型元素的连续等距存储,存取操作在O(1)时间内实现。当然也有可能存储的数据类型不一致,那么就存在两种存储方案:存数据、存地址。如果是存地址的话,在访问的时候需要通过地址间接访问。时间复杂度仍可为O(1)。
在这里插入图片描述
问题1:建立一个表时,应该安排多大的存储?
首先表有两种:固定表(python中的tuple)和变动表,对于变动表,合理的方式是预留一些空位。C语言中的数组需要定义数组的长度,那么为什么python中的List不需要分配空间呢?

3.2.2 顺序表基本操作的实现

顺序表及其操作的性质
着重讨论下定点增删的时间复杂度:设长度为n,在位置i增删,当考虑保序的情况,对于增加需要移动的元素是n-i,对于删除需要移动的元素是n-i-1.现在求平均时间复杂度。(所以时间复杂度为O(n))
在这里插入图片描述

3.2.3 顺序表的结构

按照前面的讨论,一个顺序表有两部分的信息,一是元素的信息,另外是关于整体的信息。所以需要一个对象把两部分封住起来。
两种基本的实现方式
在这里插入图片描述
一体式:优点是整体性强,易于管理,缺点是不同表对应的对象不同。
分离式:优点是表宏观部分等长,缺点是数据部分间接访问,不易管理。

替换存储区
分离式的优点在于可以在对象不变的情况下申请新的内存换一个更大的车箱,其过程如下:
在这里插入图片描述
后端插入和存储区扩展

3.2.4 python中的LIST

Tuple是固定的表,不支持任何非访问操作,下面集中关注list。
List的基本实现技术:
  Python的官方文档中,List是采用分离式技术实现的动态顺序表。
  几个操作
  lst.clear()
  Lst.reverse() # 前后对称的对调
  Lst.sort() # 排序 等价于内嵌函数sorted(Lst)

3.3 链接表

3.3.2 单链表

单链表 = 二元组
在这里插入图片描述
从引用链表的变量可以找到链表的首节点,紧接着就可以访问表中任一节点。
链表的基本操作
1、创建空链表:表头变量设置为空链接
2、删除链表:python中在将表指针赋值为None
3、判断是否为空:检查表头变量是否为空链接
加入元素:无论加入到那个位置,都是简单的链接指向修改
在这里插入图片描述
删除元素
删除表头:head = head.next
删除其他位置:pre.next = pre.next.next
丢弃不用的节点将会被python解释器自动收回。

扫描、定位、遍历
此部分的很多是基础代码的编写,就不做详细的记录了。代码中重点关注下遍历操作,迭代器、生成器和谓词操作。

class LNode():
    def __init__(self, elem, next=None):
        self._elem  = elem
        self._next = next

class LinkedListUnderflow(ValueError):
    pass

class LLIST():
    def __init__(self):
        self._head = None
        self._len = 0

    def is_empty(self):
        return self._head is None

    def prepend(self, elem):
        node = Node(elem, self._head)
        self._head = node
        self._len += 1

    def append(self, elem):
        node = Node(elem)
        p = self._head
        if self._head is None:
            self._head = node  # 以下等价
            # self.prepend(elem)
            return
        while p._next is not None:
            p = p._next
        p._next = node

    def pop(self):  # 弹出头部
        if self._head is None:
            raise LinkedListUnderflow("in pop")
        e = self._head._elem
        self._head = self._head._next
        self._len -= 1
        return e

    def pop_last(self):
        if self._head is None:
            raise LinkedListUnderflow("IN POP_LAST")
        if self._head._next is None:  # 长度为1
            e = self._head._elem
            self._head = None
            return e
        p = self._head
        while p._next._next is not None:
            p = p._next
        e = p._next._elem
        p._next = None
        return e

    def for_each(self, op):
        p = self._head
        while p is not None:
            p._elem = op(p._elem)
            p = p._next

    def elements(self):
        p = self._head
        while p is not None:
            yield p._elem
            p = p._next

    def find_elems(self, op):
        p = self._head
        while p is not None:
            if op(p._elem):
                yield p._elem
            p = p._next

    def print_all(self):
        infos = ''
        for info in self.elements():
            infos += info + '->'
        infos = infos[:-2]
        print(infos)

    def reverse(self):
        p = None  # 临时变量
        while self._head is not None:
            q = self._head
            self._head = q._next
            q._next = p
            p = q
        self._head = p


llist = LLIST()
llist.append("1")
llist.append("加工")
llist.append("2")
llist.append("销售")
llist.print_all()
llist.reverse()
llist.print_all()

输出:
1->加工->2->销售
销售->2->加工->1

3.4 链表的变形和操作

3.4.1略

3.4.2 循环单链表

这种表对象只需一个数据域_real,它在逻辑上始终引着表的尾节点,因为这样支持时间复杂度为O(1)的表头增删和表尾增加。
在这里插入图片描述

class LCList():  # 注意空表和长度为1的表
    def __init__(self):
        """循环单链表"""
        self._rear = None

    def is_empty(self):
        return self._rear is None

    def prepend(self, elem):  # 前端增加
        node = Node(elem)
        if self._rear is None:
            node._next = node
            self._rear = node
        else:
            node._next = self._rear._next
            self._rear._next = node

    def append(self, elem):  # 后端增加
        self.prepend(elem)
        self._rear = self._rear._next

    def pop(self):  # 前端删除
        if self._rear is None:
            raise LinkedListUnderflow
        elif self._rear == self._rear._next:
            self._rear = None
        else:
            self._rear._next = self._rear._next._next

    def pop_last(self):
        if self._rear is None:
            raise LinkedListUnderflow
        elif self._rear == self._rear._next:
            self._rear = None
        else:
            p = self._rear._next
            while p is not self._rear:
                if p._next == self._rear:
                    self._rear = p
                    self.pop()
                    break
                p = p._next

    def elements(self):
        p = self._rear._next
        while p is not self._rear:
            yield p._elem
            p = p._next


lclist = LCList()
for i in range(1, 6):
    lclist.append(i)
for ele in lclist.elements():
    print(ele, end=' ')
print('')

lclist.prepend(0)
lclist.append(5)
for ele in lclist.elements():
    print(ele, end=' ')
print('')

lclist.pop()
lclist.pop_last()
for ele in lclist.elements():
    print(ele, end=' ')

输出:
1 2 3 4 
0 1 2 3 4 5 
1 2 3 4 

3.4.3 双链表

每个节点视为三元组(elem, prev,next).首节点的prev和尾结点的next=None,这也是遍历的终止条件。
节点操作
节点删除:p.prev.next = p.next; p.next.prev = p.prev
在这里插入图片描述

class DLNODE():
    def __init__(self, elem, prev=None, next=None):
        """双链表"""
        self._prev = prev
        self._next = next
        self._elem = elem


class DLnodeFlow(ValueError):
    pass


class DLLIST():
    def __init__(self):
        self._head = None
        self._rear = None

    def prepend(self, elem):  # 头结点的prev是None,尾结点的next是None
        node = DLNODE(elem, next=self._head,)  # 直连头结点
        if self._head is None:  #
            self._rear = node
        else:
            self._head._prev = node
        self._head = node

    def append(self, elem):
        node = DLNODE(elem, prev=self._rear)
        if self._head is None:
            self._head = node
        else:
            self._rear._next = node
        self._rear = node

    def elements(self):
        p = self._head
        while p is not None:
            yield p._elem
            p = p._next

    def re_elements(self):
        p = self._rear
        while p is not None:
            yield p._elem
            p = p._prev

dllist = DLLIST()
for i in range(1, 5):
    dllist.append(i)
for elem in dllist.elements():
    print(elem, end=' ')
print('')
dllist.prepend(0)
for elem in dllist.re_elements():
    print(elem, end=' ')

输出:
1 2 3 4 
4 3 2 1 0 

3.4.4 两个链表的操作

链表反转:双链表可以直接互抛,单链表的话,可以逐个的将A链表的pop值prepend到一个新的链表。
链表排序:后面会有排序的专题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值