数据结构与算法之线性表的概念(python3)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:


提示:以下是本篇文章正文内容,下面案例可供参考

3、线性表概念和抽象数据类型?

线性表有两种存储方式:顺序表和链表

1定义:
线性表是相同数据类型的n个元素的有限序列.数据元素类型相同
2结构特点:
第一个元素只有后继没有前驱;
最后一个元素只有前驱没有后继,
其他元素只有唯一一个前驱和唯一一个后继

3.2顺序表

定义:线性表的顺序存储

1 什么是内存
存储器: 外存,内存
外存: c盘,d盘 u盘 长期保持,存取速度慢
内存: 存取速度快
cpu: 只能直接访问内存,在内存中取数据进行该操作

注意:顺序表不适合用来存储频繁用来插入和删除的数据

2 python 中顺序表的实现

python中list和tuple两种类型采用了顺序表的实现技术;python中list和tuple底层是基于数组的;

3.2.1元素外置顺序表

不同类型的元素不能存放在基本的顺序表中;基本的顺序表要求元素的类型必须是相同的;

若我们非要将不同类型的数据放到顺序表,我们可以使用顺序表来存放各个元素的地址,这种叫元素外置的顺序表,对于元素外置的顺序表使用插入,删除等是通过对元素地址操作来实现的

3.2.2顺序表的结构定义

一个顺序表的完整信息包括两部分:
1 数据区:表中的元素集合
2 表头: 记录表的整体情况的信息,元素存储区的容量;已有元素个数两项;

3.2.3顺序表的两种形式

a 一体式结构,存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象

b 分离式结构,表对象只保存整个表有关的信息(容量和元素个数),实际数据元素存放在另一个独立元素存储区里,通过链接与基本表对象关联

这两种结构如何选取?

若我们的数据经常扩容,我们就使用分离式动态表;若我们数据不经常改变就使用一体式顺序表,便于管理;

3.2.4 动态顺序表

动态顺序表: 当你执行添加数据操作时,python中的列表自动的去扩容,即换一块更大的存储区域,这就是所谓的动态顺序表
列表就是动态顺序表,采用的是分离式结构.
而且列表是元素外置的顺序表

3.2.5 顺序表的特点

优点:
逻辑相邻,物理相邻: 存储空间使用紧凑,不必为节点间的逻辑关系而增加额外的存储开销
可随机存储任意元素 O(1)
python 列表也引用的是数组结构

缺点:
插入,删除操作需要移动大约表中一半的元素,对n大的顺序表效率低

3.3链表

1 特点:

用一组任意的存储单元(可连续,可不连续)存储线性表的数据元素

顺序表呢,是物理上先后关系体现逻辑上先后关系;
链表是不连续的如何体现数据之间的线性关系呢?
答:每个数据元素ai,除了存储本省信息外,还需要存储其直接后继或者直接前驱的信息,即利用地址(指针)实现了用不相邻的存储单元存放逻辑上相邻的元素

2 节点结构

1数据域: 数据元素本身信息

2链接域(地址域,指针域): 直接后继或直接前驱的存储地址

3 线性链表的分类:
1 单链表: 每个结点除包含数据域外,只设置一个链接域,用于指向其后继结点

2 双向链表: 每个结点除包含数据域外,设置两个链接域,分别用以指向其前驱结点和后继结点.

# 单链表类定义




4 扫描指针和遍历单链表
单链表的特点是每个结点只保存其直接后继的地址,在已知单链表的head首地址的情况下,如果要访问到单链表中的所有结点,需要一个扫描指针(扫描游标)

python实现链表-单链表的插入和删除节点

https://www.bilibili.com/video/BV1Aa4y1E75k?p=12&spm_id_from=pageDriver&vd_source=f0e2d9965dd28bc650e022a3c8d84457


class Node(object):

    def __init__(self, data):
        self.data = data
        self.next = None


class List(object):
    def __init__(self):
        self.__head = None

    def is_empty(self):
        if self.__head is None:
            return 1
        else:
            return 0

    def add(self, item):  # 头插入法插入元素
        s = Node(item)  # 生成一个带插入节点的对象
        s.next = self.__head  # 修改链接
        self.__head = s  # 修改链接

    def travel(self):  # 遍历单链表,并打印输出
        # 对链表中的n个元素扫描一遍,时间复杂度为O(n)
        p = self.__head  # 定义指针p p从表头开始
        while p is not None:  # 判断指针是否指向了链表末尾
            print(p.data, end=" ")  # 打印p指向的结点数据
            p = p.next  # 指针下移到下一个元素

    def length(self):
        count = 0
        p = self.__head
        while p is not None:
            p = p.next
            count += 1
        return count

    def searchpos(self, pos):  # 按序号查找, pos是待查找的位置
        # 性能分析:与pos取值有关,平均时间复杂度O(n)
        len1 = self.length()
        if pos > len1:
            return
        p = self.__head
        count = 1
        if pos == 0:
            return None
        else:
            while p is not None and count != pos:
                p = p.next
                count += 1
        print(p.data)
        return p
        pass

    def search(self, item):  # 按值查找
        # 性能分析: 平均时间复杂度 O(n)
        p = self.__head
        while p is not None and item != p.data:
            p = p.next
        if p is not None:
            if p.data == item:
                print(f"数据找到了 {p.data}")
        elif p is None:
            return None
        return p

    def append(self, x):
        # 因为需要移动指针,时间复杂度是 O(n)
        p = self.__head  # 找到头结点
        n = Node(x)  # 实例化新节点
        if p is None:
            self.__head = n
            return
        while p.next is not None:
            p = p.next
        p.next = n

    def insert(self, pos, x):
        # 时间复杂度为O(n),若按照序号插入,需要先找到位置,按位置
        # 仅仅插入操作时间复杂度是O(1)
        p = self.__head
        count = 1
        s = Node(x)
        # if pos >= self.length():
        #     return -1
        while p.next is not None and count != pos:  # 找到要插入的位置
            p = p.next
            count += 1
        if 0 == pos:
            s.next = self.__head
            self.__head = s
        elif p.next is not None:
            s.next = p.next
            p.next = s
        elif p.next is None:
            p.next = s

    def remove(self, item):  # 按值删除操作
        # 查找操作 时间复杂度是 O(n)
        # 删除操作 时间复杂度是 O(1)
        p = self.__head
        if item == p.data:  # 如果删除的是一个节点
            self.__head = p.next
        else:
            # while p.next is not None and item != p.data:
            #     pre = p
            #     p = p.next
            # else:
            #     pre.next = p.next
            while p is not None:
                if p.data != item:
                    pre = p
                    p = p.next
                else:
                    pre.next = p.next
                    break
        return p


l = List()
# l.add(6)
# l.add(9)
# l.add(11)
l.append(6)
l.append(9)
l.append(11)
l.append(13)

l.travel()
print(f"\r\n链表的长度为: {l.length()}")
l.searchpos(2)
l.search(11)

l.insert(0, 44)
l.travel()
print('-------')
l.remove(13)
l.travel()


单链表特点:
1 它是一种动态结构, 整个内存空间为多个链表公用

2 不需要预先分配空间

3 链接域占用额外存储空间

4 不能随机存取,查找速度慢.

不论查哪一个元素都是从第一个元素开始查找;


3.5.1 双链表的定义和描述

1 每个结点包含两个链接域,一个指向直接前驱(prior),一个指向直接后继(next)

2双链表由头指针head唯一确定

3 将双链表中的头结点和尾结点链接起来可以构成循环链表,并称之为双向链表

双链表的插入和删除节点

https://www.bilibili.com/video/BV1Aa4y1E75k?p=14&spm_id_from=pageDriver&vd_source=f0e2d9965dd28bc650e022a3c8d84457






总结

提示:这里对文章进行总结:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值