美文网首页
Python数据结构之链表(linked list)

Python数据结构之链表(linked list)

作者: 惑也 | 来源:发表于2018-12-31 21:54 被阅读32次

    数据结构 - 链表


    1. 链表(linked list):由一组被称为结点(也叫节点)的数据元素组成的数据结构,每个结点包含2部分:1)结点本身的数据信息(data),称为信息域;2)指向下一个结点的地址信息(next),称为指针域。

    2. head结点:是一个特殊的结节,用于存储链表第一个结点的地址信息,永远指向链表的第一个结点;

    3. tail结点:是一个特殊的结点,永远指向链表的最后一个节点。链表最后一个结点的地址信息(指针域)指向None值,因也叫接地点。

    4. 由于链表的每个结点都包含了可以链接起来的地址信息,所以用一个变量就能够访问整个结点序列。

    5. 链表是计算机科学领域应用最广泛的高阶数据结构之一,它实现了数据之间保持逻辑顺序,但存储空间不必按顺序的方法。

    6. 根据结构的不同,链表可以分为单向链表、单向循环链表、双向链表、双向循环链表等。其中,单向链表和单向循环链表的结构如下图所示:

    7. 链表常用方法
      LinkedList() :创建空链表,不需要参数,返回值是空链表;
      is_empty():测试链表是否为空,不需要参数,返回值是布尔值;
      append(data) :追加元素到链表尾部。参数是要追加的元素,无返回值;
      iter():生成器,遍历链表,无参数,无返回值;
      insert(idx, value) :插入一个元素,参数为插入元素的索引和值;
      remove(idx):移除1个元素,参数为要移除的元素或索引,并修改链表;
      size() :返回链表的元素数,不需要参数,返回值是个整数;
      search(item) :查找链表某元素,参数为要查找的元素或索引,返回是布尔值。

    节点类


    python用类(class)来实现链表的数据结构,节点(Node)是实现链表的基本模块,每个节点至少包括两个重要部分:值和指针(引用)。示例:

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

    此节点类只有一个构建函数,接收一个数据参数,其中next表示指针域的指针,实例化后得到一个节点对象,如下示例,节点对象数据为4,指针指向None。

    node = Node(4)
    print(node)
    <__main__.Node object at 0x10998b3c8>
    
    这样一个节点对象,可以用一个图例来更形象的说明,如下:

    链表类


    先来看LinkedList类的构建函数:

    class LinkedList(object):
        def __init__(self):
            self.head = None
            self.tail = None 
    

    此类实例后会生成一个链表对象,初始化了head和tail节点,且两节点都指向None,实例化代码如下:

    link_list = LinkedList()
    print(link_list)
    <__main__.LinkedList object at 0x10b521710>
    
    也可以用图形象的表示这个链表对象,如下:

    is_empty()函数

    is_empty()函数,用于检查链表是否是一个空链表,这个方法只需要检查head节点是否指向None即可,代码如下:

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

    如果是空列表返回True,否则返回False。

    append()函数


    append()函数,用于追加元素到链表。如果链表为空,则添加为第一个节点,如果链表非空,则追加为最后一个元素。代码如下:

    def append(self, data):
        node = Node(data)   # 把  Node类实例化得到一个node对象
        if self.head is None:
            # 如果链表为空,则将head和tail都指向node,即将元素设置为第一个节点
            self.head = node    
            self.tail = node
        else:
            # 如果链表非空,则将tail节点和上一个节点的next均指向当前node,即将元素追加为最后一个节点
            self.tail.next = node   
            self.tail = node
    
    首先把Node类实例化得到一个node对象。如果链表为空,则将head和tail都指向node,即将元素设置为第一个节点;如果链表非空,则将tail节点和上一个节点的next均指向当前node,即将元素追加为最后一个节点。链表非空时追加元素的过程示意图:

    iter()函数


    iter()函数:生成器,可以遍历链表。遍历链表时,从head开始,直到一个节点的next指向None结束,特别地,需要考虑空链表的情况。代码如下:

    def iter(self):
        # 如果链表为空,则返回None
        if not self.head:
            return 
        cur = self.head
        yield cur.data  # 当前节点data数据的生成器
        while cur.next:
            cur = cur.next  # 当前节点指向下一个节点并遍历
            yield cur.data  # 当前节点data数据的生成器
    

    如果链表为空,遍历时直接返回None;如果链表不为空,遍历时,先将当前节点的data返回生成器,再将当前节点指向下一个节点,进行while循环,同时将对应的data返回生成器,直到next指向None,就可以实现非空链表的遍历。

    insert()函数


    1. insert()函数,用于将数据插入到链表的指定索引的位置。
    2. 通过实例,来说明insert()函数插入数据到链表的过程。例如,需要在链表数据域为6的节点处插入一个新节点,链表如下:
    3. 操作过程
      将新节点的next指针指向数据域为6的这个节点;
      将数据域为5的节点的next指针指向新加的节点。
    4. 过程示意图
    5. 注意事项
      操作过程的两个步骤不能颠倒,如果颠倒,数据域为6的节点会被丢失,数据域为7的节点不再是链表的节点;
      链表为空时,返回None或者抛出异常;
      插入位置超出链表节点的长度时,抛出异常;
      插入位置是链表的最后一个节点时,还需要移动tail结点指向新结点。
    6. 示例代码
    def insert(self, idx, value):
        cur = self.head
        cur_idx = 0
        if cur is None:
            raise Exception('The linked_list is empty')
        while cur_idx < idx - 1:
            cur = cur.next
            if cur is None:
                raise Exception('list length less than index')
            cur_idx += 1
        node = Node(value)
        node.next = cur.next
        cur.next = node 
        if node.next is None:
            self.tail = node
    
    1. 在指定的索引位置插入一个节点,前提是需要找到这个位置,在链表中只有采用遍历的方式,具有O(n)的速度,最糟糕时会遍历链表的所有节点,当找到插入点时,其实并不需要当前节点的信息,而是需要前一个节点的信息,所以,代码中巧妙的使用了while cur_idx < idx-1:的方式,这样能使用cur这个变量能指向插入位置的上一个节点对象。

    remove()函数


    1. remove()函数接收一个idx参数,表示要删除节点的索引。

    2. 使用remove()函数时需要考虑如下情况:
      空链表,直接抛出异常;
      删除第一个节点时,移动head到删除节点的next指针指向的对象;
      链表只有一个节点时,把head与tail都指向None即可;
      删除最后一个节点时,需要移动tail到上一个节点;
      遍历链表时要判断给定的索引是否大于链表的长度,如果大于则抛出异常信息。

    3. 示意图
    4. 示例代码:

    def remove(self, idx):
        cur = self.head
        cur_idx = 0
        if self.head is None:   # 空链表时
            raise Exception('The linked_list is empty')
        while cur_idx < idx - 1:
            cur = cur.next
            if cur is None:
                raise Exception('list length less than index')
            cur_idx += 1
        if idx == 0:    # 当删除第一个节点时
            self.head = cur.next
            cur = cur.next
            return 
        if self.head is self.tail:  # 当只有一个节点的链表时
            self.head = None
            self.tail = None
            return
        cur.next = cur.next.next
        if cur.next is None:    # 当删除的节点是链表最后一个节点时
            self.tail = cur 
    

    size()函数


    size()函数不接收参数,返回链表中节点的个数,要获得链表的节点个数,必定会遍历链表,直到最后一个节点的next指针指向None时链表遍历完成,遍历时可以用一个累加器来计算节点的个数,如下代码:

    def size(self):
        cur = self.head
        count = 0
        if cur is None:
            raise Exception('The linked_list is empty')
        while cur is not None:
            count += 1
            cur = cur.next
        return count
    

    search()函数


    search()函数接收一个参数,表示查找节点中数据域的值。查找时会遍历链表,每到一个节点把当前节点的data值与参数值作比较,最糟糕的情况下会全遍历链表。如果查找到有些数据则返回True,否则返回False,代码如下:

    def search(self, item):
        cur = self.head
        found = False
        while cur is not None and not found:
            if cur.data == item:
                found = True
            else:
                cur = cur.next
        return found
    

    Node类与LinkedList类完整代码

    class Node():
        '创建节点'
        def __init__(self,data):
            self.data = data
            self.next = None
    
    class LinkedList(object):
        '创建列表'
        def __init__(self):
            '初始化列表'
            self.head = None
            self.tail = None 
    
        def is_empty(self):
            '判断链表是否为空'
            return self.head is None
    
        def append(self, data):
            '追加元素'
            node = Node(data)   # 把Node类实例化得到一个node对象
            if self.head is None:
                # 如果链表为空,则将head和tail都指向node,即将元素设置为第一个节点
                self.head = node    
                self.tail = node
            else:
                # 如果链表非空,则将tail节点和上一个节点的next均指向当前node
                self.tail.next = node   
                self.tail = node    
    
        def iter(self):
            '遍历链表'
            # 如果链表为空,则返回None
            if not self.head:
                return 
            cur = self.head
            yield cur.data  # 当前节点data数据的生成器
            while cur.next:
                cur = cur.next  # 当前节点指向下一个节点并遍历
                yield cur.data  # 当前节点data数据的生成器
    
        def insert(self, idx, value):
            '插入元素'
            cur = self.head
            cur_idx = 0
            if cur is None:
                raise Exception('The linked_list is empty')
            while cur_idx < idx - 1:
                cur = cur.next
                if cur is None:
                    raise Exception('list length less than index')
                cur_idx += 1
            node = Node(value)
            node.next = cur.next
            cur.next = node 
            if node.next is None:
                self.tail = node
    
        def remove(self, idx):
            '删除元素'
            cur = self.head
            cur_idx = 0
            if self.head is None:   # 空链表时
                raise Exception('The linked_list is empty')
            while cur_idx < idx - 1:
                cur = cur.next
                if cur is None:
                    raise Exception('list length less than index')
                cur_idx += 1
            if idx == 0:    # 当删除第一个节点时
                self.head = cur.next
                cur = cur.next
                return 
            if self.head is self.tail:  # 当只有一个节点的链表时
                self.head = None
                self.tail = None
                return
            cur.next = cur.next.next
            if cur.next is None:    # 当删除的节点是链表最后一个节点时
                self.tail = cur 
    
        def size(self):
            '统计元素个数'
            cur = self.head
            count = 0
            if cur is None:
                raise Exception('The linked_list is empty')
            while cur is not None:
                count += 1
                cur = cur.next
            return count
    
        def search(self, item):
            '查找指定元素'
            cur = self.head
            found = False
            while cur is not None and not found:
                if cur.data == item:
                    found = True
                else:
                    cur = cur.next
            return found
    
    
    if __name__ == '__main__':
        link_list = LinkedList()    # 实例化
        print(link_list.is_empty())     # 判断是否为空
        for i in range(15):
            link_list.append(i)     # 追加15个元素
        print(link_list.size())     # 元素个数
        print(link_list.is_empty())     # 判断是否为空
        link_list.insert(5, 1000)       # 插入一个元素:1000
        print(link_list.size())     # 元素个数
        link_list.remove(0)     # 删除第一个元素
        print(link_list.size())     # 元素个数
        print(link_list.search(10))     # 查找是否存在元素10
    
        for node in link_list.iter():
            print(f'node is {node}')    # 遍历元素
    
    True
    15
    False
    16
    15
    True
    node is 1
    node is 2
    node is 3
    node is 4
    node is 1000
    node is 5
    node is 6
    node is 7
    node is 8
    node is 9
    node is 10
    node is 11
    node is 12
    node is 13
    node is 14
    

    相关文章

      网友评论

          本文标题:Python数据结构之链表(linked list)

          本文链接:https://www.haomeiwen.com/subject/otyilqtx.html