数组与链表

下来开始学习数据结构,这篇文章以及以后关于数据结构的文章,不会写太多的细节,比如一些性质等。更多的是关注一些常见的数据结构如何用python 代码实现。
学习的课本是数据结构与算法 python语言描述有兴趣的可以自己买来读读。

1 顺序表

python 的list 是一种采用分离式技术实现的动态顺序表,创建空表时不分配元素存储区,遇到insert 和append 要求加入元素,系统第一次分配能容纳4个元素的存储区,继续扩容时采用的策略使每次扩容的比率接近1.125倍。
只要采用等比扩容,就能保证尾端加入元素的append 的平均时间复杂性是O(1)

2 链接表

**## 单链表:**为了掌握一个表,只需要用一个变量保存着这个节点的首节点的引用,今后把这样的变量成为表头变量或者表头指针
在这里插入图片描述
定义一个单链表:

class LNode():
    def __init__(self, elem, next_=None):
        self.elem = elem
        self.next_ = next_

(参数名字next_是为了避免与Python标准库的函数重名)

基本链表操作

1 创建空链表: 把相应的表头变量设置为空链接,Python 语言中将其设置为None
2 删除链表: 将表指针赋值为None,就抛弃了链表原有的所有节点,python 的解释器的存储管理系统会自动回收不用的存储
3 判断链表是否为空: 将表头变量的值与空链表进行比较
4 判断链表是否满: 一般不会满,除非程序用完了所有的存储空间

2.2 加入元素

表首端加入:
(1)创建一个新节点并保存数据
(2)把原链表的首节点的链接存入新节点的链接域next_
(3)修改表头变量,使之指向新节点,这个操作是新节点实际成为表头变量所指的节点,即表的首节点
在这里插入图片描述

q = LNode(13)
q.next = head.next
head = q

一般情况的插入: 想要在单链表里的某一个位置插入一个新节点,必须找到该位置之前的那个结点,因为新结点要插入到它的后面,需要修改它的next 域。
设变量pre 已指向要插入元素的前一个结点
(1)创建一个新结点,存入数据
(2)把pre 所指结点的next域的值存入新节点的链接域next , 这个操作将原表在pre 所指结点之后的一段链接到新结点之后
(3)修改pre的next 域,使之指向新结点
在这里插入图片描述

q = LNode(13)
q.next = pre.next
pre.next  = q 

2.3 删除元素

删除表首元素: 修改表头指针,使之指向第二个结点 (丢弃的结点将被Python 解释器自动回收)

head = head.next

一般情况下的元素删除: 一般情况下删除必须先找到要删除元素所在结点的前一结点。设用变量pre来指向

pre.next = pre.next.next

在这里插入图片描述

2.4 扫描 定位 遍历

由于单链表只有一个方向上的链接,开始情况下只有表头变量
在掌握中,所以对表内容的一切检查都只能从表头变量开始,沿着表中链接逐步进行,这种过程称为链表的扫描

p = head
while p is not None and 还需其他的条件:
	对p所指结点里的数据做所需操作
	p = p.next

循环中使用的辅助变量p 称为扫描指针。每个扫描循环必须用一个扫描指针作为控制变量
按下标定位: 链表首节点的元素看做下标0,确定第i个元素所在结点的操作称为按下标定位

p = head
while p is not None and i > 0:
	i -=1
	p = p.next

循环结束可能出现两种情况:或者扫描完表中所有节点还没有找到第i个结点,或者p所指结点就是所需。
通过检查P 是否为None 可以区分这两种情况。
显然,如果需要删除第K个结点,可以先将 i 设置为 k-1 ,循环后检查 i 是0 且p.next 不是None 就可以执行删除了

按元素定位: 假设需要在链表中找到满足谓词pred的元素,可以参考上面

p = head
while p is not None and not pred(p.elem):
	p = p.next

循环结束时,p 是None, 或者为pred是True.
链表复杂度总结:
在这里插入图片描述在这里插入图片描述

2.5 单链表类的实现

现在基于结点LNode定义一个单链表对象的类,在这种表对象里只有一个引用链接结点的_head域,初始化为None表示建立一个空表
判断表空的操作检查_head;在表头插入数据的操作是prepend,它把包含新元素的结点链接在最前面;操作pop删除表头结点并返回这个数据
(LNode 表示新的结点,LinkedListUnderflow 表示错误)

class LList:
    def __init__(self):
        self._head = None
        
    def is_empty(self):
        return self._head is None
    
    def prepend(self):
        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
    # 一直找p的下一个不为空的结点,直到为空,说明此时p是最后一个结点
    while p.next is not None:
        p = p.next
        # 让最后一个结点的指针域指向新加入的结点
    p.next = LNode(elem)

** 删除表最后的元素(结点):**要从单链表删除一个结点,必须找到它的前一个结点。在尾端删除的操作里,扫描循环应该找到表中倒数第二个结点,也就是找到p.next.next 为None 的p
在开始之前,需要处理两个特殊情况:
(1)如果表空没有可以返回的元素,应该引发异常;
(2) 表中只有一个元素需要特殊处理,因为这时应该需改表头指针,
(3)一般情况是先通过循环找到位置,取出最后结点的数据后将其删除

def pop_last(self):
    if self._head is None: # 空表
        raise LinkedListUnderflow('in pop_last')
    
    p = self._head
    while p.next is None:  # 表中只有一个元素
        # e 表示要返回的元素的值,
        e = p.elem
        self._head = None
        return e
    
    while p.next.next is not None: # 直到p.next 是最后结点
        p = p.next
    # 这里 e 表示最后一个元素的值
    e = p.next.elem
    # 把最后一个元素移除了,所以让它指向一个空,表示它现在是最后一个元素
    p.next = None
    return e
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值