数据结构与算法Python(链表)

链表的定义

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。在这里插入图片描述
链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

单向链表

单向链表定义

单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值
在这里插入图片描述

  • 表元素域elem用来存放具体的数据
  • 链接域next用来存放下一个节点的位置(python中的标识)
  • 变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点

单链表在Python中的实现

节点的构建

class Node(object):
    def __init__(self,elem):
    	# elem是存放的实际值
        self.elem = elem
        # next指向下一节点,默认为None
        self.next = None

单链表的构建、判断是否为空、计算长度、遍历链表

class SingleLinkList(object):
    def __init__(self,node = None):    # node = None相当于包含了建立一个空链表的情况
        self.__head = node

    def is_empty(self):
    	'''判断链表是否为空'''
        return self.__head == None    # return的值需要用print()打印出来

    def length(self):
        '''计算链表长度'''
        cur = self.__head    # 建立一个游标,首先指向头部
        count = 0
        while cur != None:    # 只要游标指向的节点不为空
            count += 1
            cur = cur.next    # 指针继续移动
        return count

    def travel(self):
        '''遍历链表'''
        cur = self.__head
        while cur != None:
            print(cur.elem,end = ' ')
            cur = cur.next

头部添加元素

在这里插入图片描述

def add(self, item):
    '''链表头部添加元素:头插法'''
    node = Node(item)    # 先定义一个节点,把值放进去
    # 注意先后顺序,否则后边的节点就找不到了
    node.next = self.__head
    self.__head = node

尾部添加元素

def append(self, item):
    '''链表尾部添加元素:尾插法'''
    node = Node(item)
    cur = self.__head
    if self.is_empty():
        self.__head = node
    else:
        while cur.next != None:
            cur = cur.next    # 节点向下移动,直到最后一个节点,即cur.next == None
        cur.next = node

指定位置添加元素

在这里插入图片描述

def insert(self,pos,item):
    '''链表指定部分添加元素'''
    node = Node(item)
    count = 0
    pre = self.__head
    if pos <= 0:
        self.add(item)
    elif pos >= self.length()-1:
        self.append(item)
    else:
        while count != pos-1:
            pre = pre.next
            count += 1
        node.next = pre.next
        pre.next = node

删除节点

在这里插入图片描述

def remove(self,item):
    '''删除链表中的出现的第一个指定元素'''
    cur = self.__head
    pre = None    #采用两个游标
    while cur != None:
        if cur.elem == item:
            if pre == None:    # 判断删除第一个节点的情况
                self.__head = cur.next
            else:
                pre.next = cur.next
            break    #要退出循环 因为如果执行if中的语句就不会进入else的语句,即游标继续移动,所以要手动break
        else:    # 规定了两个游标的位置关系,同时使两个游标继续移动
            pre = cur    
            cur = cur.next

查找节点是否存在

def search(self,item):
    '''链表中元素是否存在,返回True或False'''
    cur = self.__head
    while cur != None:
        if cur.elem == item:
            return True
        else:
            cur = cur.next
    return False

链表与顺序表的对比

链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的使用相对灵活
链表与顺序表的各种操作复杂度如下所示:
在这里插入图片描述

双向链表

双向链表的定义与结构

双向链表每个节点有两个链接,分别叫前驱后继:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值
在这里插入图片描述

双向链表的实现

节点的构建

相比于单向链表,多出一个prev属性

class Node(object):
    def __init__(self,item):
        self.elem = item
        self.prev = None
        self.next = None

双向链表的构建、判断是否为空、计算长度、遍历链表

因为这些功能并不涉及前驱链接,所以与单向链表相同,可以使用继承的方式,减少代码量。

class DoubleLinkList(object):
    # 与单向链表相同的方法,可以使用继承
    def __init__(self,node = None):
        self.__head = node

    def is_empty(self):
        return self.__head is None    # 尽量使用is

    def length(self):
        '''计算链表长度'''
        cur = self.__head
        count = 0
        while cur != None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        '''遍历链表'''
        cur = self.__head
        while cur != None:
            print(cur.elem,end = ' ')
            cur = cur.next
        print("")    #起到换行的作用

头部添加元素

def add(self, item):
    '''链表头部添加元素:头插法'''
    node = Node(item)
    if self.__head is None:
        self.__head = node
    else:
        node.next = self.__head
        node.next.prev = node
        self.__head = node    # ※不需要再加node.prev = None,因为前面的设定中默认就是None

尾部添加元素

def append(self, item):
    '''链表尾部添加元素:尾插法'''
    node = Node(item)
    cur = self.__head
    if self.is_empty():
        self.__head = node
    else:
        while cur.next != None:
            cur = cur.next
        cur.next = node
        node.prev = cur

指定位置添加元素

def insert(self,pos,item):
    '''链表指定部分添加元素'''
    node = Node(item)
    count = 0
    cur = self.__head
    if pos <= 0:
        self.add(item)
    elif pos >= self.length()-1:
        self.append(item)
    else:
        while count != pos-1:
            cur = cur.next
            count += 1
        # 一定要画图去比较,根据断的先后顺序,代码不唯一
        node.next = cur
        node.prev = cur.prev
        cur.prev.next = node
        cur.prev = node

删除节点

def remove(self,item):
    '''删除链表中的置顶元素'''
    cur = self.__head
    while cur != None:
        if cur.elem == item:
            if cur == self.__head:    #当删除的节点位于第一个
                self.__head = cur.next
                if cur.next != None:    # 补充链表中只有一个节点的情况
                    cur.next.prev = None    # 还需要将下一个节点(这个节点仍存在)的prev置空
            else:
                cur.prev.next = cur.next
                if cur.next:    # 考虑删除最后一个节点的情况;※如果是最后一个节点,不需要将该节点的prev置空,因为这是要删除的节点
                    cur.next.prev = cur.prev
            break    #要退出循环 因为执行if后并没有进入else的语句,要手动break
        else:
            cur = cur.next

查找节点是否存在

也与单向链表完全相同

def search(self,item):
    '''链表中元素是否存在'''
    cur = self.__head
    while cur != None:
        if cur.elem == item:
            return True
        else:
            cur = cur.next
    return False

单向循环链表

单向循环链表的定义与结构

单向链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点
在这里插入图片描述

单向循环链表的实现

节点的构建

节点的构建与单向链表相同。

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

单向循环链表的构建、判断链表是否为空

单向循环链表的构建与单向链表略有差异,如果在建立链表时赋了一个值,要将该节点的后继区域与头结点连接。
判断是否为空的操作与单向链表相同。

class SingleCycleLinkList(object):
    '''单向循环链表'''
    def __init__(self,node = None):
        self.__head = node
        if node:    # node存在实际值,不是None
            node.next = node

    def is_empty(self):
        return self.__head == None

计算链表长度、遍历链表

实现计算链表长度和遍历链表时,与单向链表有一个共同的区别点,就是循环体中不能对最后一个节点进行操作,所以要补充尾节点的情况:对于计算长度是count初始为1 ,对于遍历链表是最后再print一次。

def length(self):
    '''计算链表长度'''
    if self.is_empty():    # 空列表的情况
        return 0
    else:
        cur = self.__head
        count = 1    # 注意count初始要置1
        while cur.next != self.__head:    # 不能使用cur != self.__head的判断语句
            count += 1
            cur = cur.next
        return count

def travel(self):
    '''遍历链表'''
    if not self.is_empty():    # 不能是空链表
        cur = self.__head
        while cur.next != self.__head:
            print(cur.elem,end = ' ')
            cur = cur.next
        # 把尾节点的值补上
        print(cur.elem)
        print("")
    else:    # 空链表返回空
        return

头部添加元素

def add(self, item):
    '''链表头部添加元素:头插法'''
    node = Node(item)
    cur = self.__head
    if self.is_empty():    # ※考虑空列表的情况
        self.__head = node
        node.next = self.__head
    else:
        # ※※※一定要先循环,使游标指向最后!否则如果先进行把指定节点放到第一位的操作,会使self.__head值有变化,导致进入死循环!
        while cur.next != self.__head:
            cur = cur.next
        node.next = self.__head
        self.__head = node
        # 退出循环后,cur指向的就是尾节点
        cur.next = node

尾部添加元素

def append(self, item):
    '''链表尾部添加元素:尾插法'''
    node = Node(item)
    cur = self.__head
    if self.is_empty():
        self.__head = node
        node.next = self.__head
    else:
        while cur.next != self.__head:
            cur = cur.next
        cur.next = node
        node.next = self.__head

指定部位添加元素

def insert(self,pos,item):
    '''链表指定部分添加元素'''
    node = Node(item)
    count = 0
    pre = self.__head
    if pos <= 0:
        self.add(item)
    elif pos >= self.length()-1:
        self.append(item)
    else:
        while count != pos-1:
            pre = pre.next
            count += 1
        node.next = pre.next
        pre.next = node

删除节点

def remove(self,item):
    '''删除链表中第一个出现的指定元素'''
    cur = self.__head
    pre = None    #采用两个游标
    if self.is_empty():
        return
    else:
        while cur.next != self.__head:    # ※注意!①尾节点无法进入循环!②空链表会报错
            if cur.elem == item:
                # 头部节点的删除
                if pre == None:
                    rear = self.__head
                    while rear.next != self.__head:    # 找到尾节点 ※要先找节点,因为后边改变了self.__head的值!
                        rear = rear.next
                    self.__head = cur.next
                    rear.next = self.__head
                # 中间节点的删除
                else:
                    pre.next = cur.next
                return    # 在单项链表中,使用break就相当于跳出了整个函数,但是在这里,break跳出循环后还会执行后边的语句,所以要用return来跳出整个函数
            else:
                pre = cur
                cur = cur.next
        # 退出循环时,游标指向尾节点
        # 尾部节点的删除
        if cur.elem == item:
            if self.__head == cur:    # ※考虑只有一个节点的情况,否则pre.next会报错
                self.__head = None
            else:    # 两个节点以上,尾部节点的删除
                pre.next = cur.next

查找节点是否存在

def search(self,item):
    '''链表中元素是否存在'''
    cur = self.__head
    while cur.next != self.__head:    # ※注意!①指向尾节点时没有进入循环 ②如果是空链表会报错
        if cur.elem == item:
            return True
        else:
            cur = cur.next
    if cur.elem == item:    # 补充了最后遗漏的尾节点的判断以及只有一个节点时的情况
        return True
    return False
发布了14 篇原创文章 · 获赞 0 · 访问量 380

猜你喜欢

转载自blog.csdn.net/Bro_Jun/article/details/105230323