线性表基础笔记

关于线性表

定义:线性表的定义是描述其逻辑结构,而通常会在线性表上进行的查找、插入、删除等操作。
线性表作为一种基本的数据结构类型,在计算机存储器中的存储一般有两种形式,一种是顺序存储,一种是链式存储。

线性表的顺序存储

定义

若将线性表L=(a0,a1, ……,an-1)中的各元素依次存储于计算机一片连续的存储空间,这种机制表示为线性表的顺序存储结构。

特点

逻辑上相邻的元素 ai, ai+1,其存储位置也是相邻的;
存储密度高,方便对数据的遍历查找。
对表的插入和删除等运算的效率较差。

程序实现

在Python中,list存放于一片单一连续的内存块,故可借助于列表类型来描述线性表的顺序存储结构,而且列表本身就提供了丰富的接口满足这种数据结构的运算。

顺序存储结构的线性表就是我们常说的列表,列表中附带的功能十分强大。我们通过简单的代码对列表的增删改查进行演示。

#创建一个列表
L = [1,2,5,9,7,8,6]
L.append(10)    #增加列表的元素
# 结果[1,2,5,9,7,8,6,10]
L.remove(6)     #删除列表元素
# 结果[1,2,5,9,7,8,10]
L[3] = 11   #修改列表元素
# 结果[1,2,5,11,7,8,10]
L.index(3) #查找
#11
L.insert(2,9) #插入
# 结果[1,2,5,9,11,7,8,10]

线性表的链式存储

定义

将线性表L=(a0,a1,……,an-1)中各元素分布在存储器的不同存储块,称为结点,每个结点(尾节点除外)中都持有一个指向下一个节点的引用,这样所得到的存储结构为链表结构。

链表结构

特点

逻辑上相邻的元素 ai, ai+1,其存储位置也不一定相邻;
存储稀疏,不必开辟整块存储空间。
对表的插入和删除等运算的效率较高。
逻辑结构复杂,不利于遍历。

程序实现

我们先创建节点类,用简单的代码对单链表进行模拟。

# 创建节点类
class Node:
    """
    自定义类视为节点类,属性作为数据内容
    写一个next属性,用来和下一个节点创建联系
    """

    def __init__(self, val, next=None):
        """
        :param val:必要数据
        :param next:下个节点的引用
        """
        self.val = val
        self.next = next

创建节点并进行实例化

Abby = Node((1,'Abby','w'))
Emma = Node((2,'Emma','w'))
Alex = Node((3,'Alex','m'))
Abby.next = Emma
Emma.next = Alex

不难发现,Abby–>Emma–>Alex,就这样,我们完成了一个简单链表的创建。 那下面我们如果想将一个顺序储存的列表变成一个链表,那又需要如何实现呢?
首先,我们需要对目标列表进行链表初始化。
观察以下代码:

class LinkList:
    """
    生成单链表,通过实例化的对象代表一个链表
    可以调用具体的操作完成各种功能
    """

    def __init__(self):
        self.head = Node(None)  # 表头

    # 初始化链表
    def init_list(self, list_):
        p = self.head
        for i in list_:
            p.next = Node(i)
            p = p.next

我们在初始化链表之前,需要明确链表所在的位置,以方便我们去查找。所以,我们在这先建立一个表头。
在我们初始化链表时,我们需要遍历列表,让列表中每个元素都建立对应节点并与下一元素相连。我们创建出一个类似于指针变量的变量p,在遍历时,元素与元素之间建立节点。p存储的是表头地址,创建节点,储存元素,表头的next指向第一个元素,第一个元素的next又通过p去与下一个元素建立节点……完成初始化。
完成链表的初始化后,我们可以定义对链表的相关操作。

	# 遍历链表
    def show(self):
        p = self.head.next
        while p:
            print(p.val)
            p = p.next

    # 清空链表
    def clear(self):
        self.head.next = None

    # 尾部插入
    def append(self, value):
        p = self.head
        while p.next:
            p = p.next
        p.next = Node(value)

我们实现链表的清空,这里利用的是python它的一个内存处理机制,数据没有被变量绑定,就会自动释放。所以,我们只要实现表头的next为空值就可以实现这样的功能。
当我们需要在链表的尾部插入数据时,我们需要将整个链表遍历一遍才能实现插入数据的功能,此处就体现了链表不便于遍历操作的缺点。

# 头部插入
    def head_insert(self, value):
        node = Node(value)
        node.next = self.head.next
        self.head.next = node

    # 指定位置插入
    def insert(self, index, value):
        p = self.head.next
        for i in range(index):
            if p.next is None:
                break
            p = p.next
            # 插入节点
        node = Node(value)
        node.next = p.next
        p.next = node

头部插入与尾部插入不同,我们只需要找到表头,就可以进行插入操作。先创建节点,让新节点中的next与表头元素的next建立联系,再让表头的next与新节点建立联系,完成插入。
同理,指定位置插入则需要在制定位置的前一个元素插入节点,重新建立联系,完成插入。

	# 删除节点
    def remove(self, value):
        p = self.head
        # 移动至目标节点的上一个节点
        while p.next.val != value and p.next is not None:
            p = p.next
        if p.next is None:
            raise ValueError("x is not in list")
        else:
            p.next = p.next.next

    # 获取某个节点的值
    def search(self, index):
        if index < 0:
            raise IndexError("Index out of range")
        # 移动p
        p = self.head.next
        for i in range(index):
            if p is None:
                raise IndexError("Index out of range")
            p = p.next
            return p.val

删除节点的时候我们需要注意的是,寻找的值在不在链表中,如果找到,只需要斩断该元素与相邻两元素之间的联系就可以了。
而在我们需要查找某个节点的值的时候,我们需要规范查找的操作,index大于0。移动指针p,当p所联系的值是None时,可以确定输入的地址大于链表的长度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值