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

数据结构 - 链表


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

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

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

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

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

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

    Python数据结构之链表(linked list)_第1张图片

  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>

这样一个节点对象,可以用一个图例来更形象的说明,如下:
Python数据结构之链表(linked list)_第2张图片


链表类


先来看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>

也可以用图形象的表示这个链表对象,如下:
Python数据结构之链表(linked list)_第3张图片


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,即将元素追加为最后一个节点。链表非空时追加元素的过程示意图:
Python数据结构之链表(linked list)_第4张图片
Python数据结构之链表(linked list)_第5张图片


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的节点处插入一个新节点,链表如下:
    Python数据结构之链表(linked list)_第6张图片
  3. 操作过程
    将新节点的next指针指向数据域为6的这个节点;
    将数据域为5的节点的next指针指向新加的节点。
  4. 过程示意图
    Python数据结构之链表(linked list)_第7张图片
  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. 示意图
    Python数据结构之链表(linked list)_第8张图片
    Python数据结构之链表(linked list)_第9张图片
  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))