Python全栈(二)数据结构和算法之3.链表的实现

一、节点的实现&链表的初始化

节点组成部分:item next

1.定义节点类

class SingleNode(object):
    '''the node of single_link_list'''
    def __init__(self,item):
        '''初始化'''
        #item存放数据元素
        self.item = item
        #next是下一个节点的标识
        self.next = None

2.定义单链表并初始化

链表为空:
__head == None
链表不为空:
__head = nodex

class SingleLinkList(object):
    '''单链表'''
    def __init__(self,node = None):
        self.__head = node

3.单链表的操作

is_empty() 链表是否为空
length() 链表长度
travel() 遍历整个链表
append(item) 链表尾部添加元素
add(item) 链表头部添加元素
insert(pos, item) 指定位置添加元素
search(item) 查找节点是否存在
remove(item) 删除节点

二、单链表中的遍历&长度&末尾添加

1.获取长度:

用游标遍历cur = cur.next
最后指向Nonecur == None,即停止.
用count计数,即每遍历一个节点,count += 1.

def length(self):
    '''链表长度'''
    #cur即游标,指向首节点,用来遍历
    cur = self.__head
    count = 0
    #当未到达尾部时
    while cur != None:
        count += 1
        #将游标向后移动一位
        cur = cur.next
    return count

2.遍历链表:

指标每次指向下一个节点
应该先打印出首节点,再移向下一个节点。

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

3.append尾部追加元素

先创建节点,再让游标向后遍历,直到cur.next = node
要先判断链表是否为空。

def append(self,item):
    '''链表尾部添加元素'''
    #创建一个保存数据的节点
    node = SingleNode(item)
    #判断链表是否为空,若为空,则将__head指向新节点
    if self.is_empty():
        self.__head = node
    #若不为空,则找到尾部
    else:
        cur = self.__head
        while cur.next != None:
            #找到最后一个节点
            cur = cur.next
        #将尾节点的next指向新节点
        cur.next = node

三、单链表中的头部添加&指定位置添加

1.add头部添加元素

先将要添加的节点指向头节点,再将head指向要添加的节点。
头部插入元素

def add(self,item):
    '''链表头部添加元素'''
    # 创建一个保存数据的节点
    node = SingleNode(item)
    #将新节点的链接区指向头节点
    node.next = self.__head
    #将链表的头节点指向新节点
    self.__head = node

2.insert指定位置添加元素

新节点指向当前节点的下一个节点node.next = cur.next,当前节点指向新节点cur.next = node
指定位置插入元素

def insert(self,pos,item):
    '''指定位置添加元素'''
    #若指定位置为第一个位置之前,则指向头部插入
    if pos <= 0:
        self.add(item)
    #若指定位置超过链表尾部,则执行尾部插入
    elif pos > self.length() - 1:
        self.append(item)
    #找到指定位置
    else:
        node = SingleNode(item)
        # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
        pre = self.__head
        count = 0
        while count < pos - 1:
            count += 1
            pre = pre.next
        #将新节点node的next指向插入位置的节点
        node.next = pre.next
        #将插入位置的前一个节点的next指向新节点
        pre.next = node

四、单链表的搜索和删除

1.查找

要遍历到最后一个节点后的None,如果没有找到,返回False。

def search(self,item):
    '''查找节点是否存在'''
    cur = self.__head
    while cur != None:
        #判断第一个节点
        if cur.item == item:
            return True
        #未找到,继续向后移动
        else:
            cur = cur.next
    return False

2.remove删除指定元素

将被删除节点的上一个节点指向被删除节点的下一个节点,进行判断。
删除指定元素

def remove(self,item):
    '''删除节点'''
    cur = self.__head
    pre = None
    while cur != None:
        #找到指定元素
        if cur.item == item:
            #如果第一个就是删除的节点,将头指针指向头结点的后一个节点
            if cur == self.__head:
                self.__head = cur.next
            #否则,将删除位置前一个节点的next指向删除位置的后一个节点
            else:
                pre.next = cur.next
            break
        #未找到,继续移动
        else:
            pre = cur
            cur = cur.next

3.完整实现

整个节点和单链表的完整实现和测试代码为:

#单链表
#节点的实现
class SingleNode(object):
    '''the node of single_link_list'''
    def __init__(self,item):
        '''item存放数据元素'''
        self.item = item
        '''next是下一个节点的标识'''
        self.next = None

class SingleLinkList(object):
    '''单链表'''
    def __init__(self,node = None):
        self.__head = node
    
    def is_empty(self):
        '''判断是否为空'''
        return self.__head == None

    def length(self):
        '''链表长度'''
        #cur即游标,指向首节点,用来遍历
        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.item,end=' ')
            cur = cur.next

    def append(self,item):
        '''链表尾部添加元素'''
        #创建一个保存数据的节点
        node = SingleNode(item)
        #判断链表是否为空,若为空,则将__head指向新节点
        if self.is_empty():
            self.__head = node
        #若不为空,则找到尾部
        else:
            cur = self.__head
            while cur.next != None:
                #找到最后一个节点
                cur = cur.next
            #将尾节点的next指向新节点
            cur.next = node

    def add(self,item):
        '''链表头部添加元素'''
        # 创建一个保存数据的节点
        node = SingleNode(item)
        #将新节点的链接区指向头节点
        node.next = self.__head
        #将链表的头节点指向新节点
        self.__head = node

    def insert(self,pos,item):
        '''指定位置添加元素'''
        #若指定位置为第一个位置之前,则指向头部插入
        if pos <= 0:
            self.add(item)
        #若指定位置超过链表尾部,则执行尾部插入
        elif pos > self.length() - 1:
            self.append(item)
        #找到指定位置
        else:
            node = SingleNode(item)
            # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置
            pre = self.__head
            count = 0
            while count < pos - 1:
                count += 1
                pre = pre.next
            #将新节点node的next指向插入位置的节点
            node.next = pre.next
            #将插入位置的前一个节点的next指向新节点
            pre.next = node

    def remove(self,item):
        '''删除节点'''
        cur = self.__head
        pre = None
        while cur != None:
            #找到指定元素
            if cur.item == item:
                #如果第一个就是删除的节点,将头指针指向头结点的后一个节点
                if cur == self.__head:
                    self.__head = cur.next
                #否则,将删除位置前一个节点的next指向删除位置的后一个节点
                else:
                    pre.next = cur.next
                break
            #未找到,继续移动
            else:
                pre = cur
                cur = cur.next

    def search(self,item):
        '''查找节点是否存在'''
        cur = self.__head
        while cur != None:
            #判断第一个节点
            if cur.item == item:
                return True
            #未找到,继续向后移动
            else:
                cur = cur.next
        return False

#测试
#定义一个值为100的节点
node = SingleNode(100)
s = SingleLinkList()
s.append(1)
s.append(2)
s.append(3)
s.append(4)
s.append(5)
s.add(6)
s.insert(-1,7)
s.travel()
s.insert(3,8)
s.insert(10,9)
s.remove(7)
s.remove(3)
s.remove(9)
s.travel()
print(s.is_empty())
print(s.length())
s.travel()

运行结果为:

7 6 1 2 3 4 5 6 1 8 2 4 5 False
6
6 1 8 2 4 5 

五、链表与顺序表的对比

链表没有顺序表随机读取的优点,同时增加了节点的指针域,增大了空间开销,但是对内存的使用相对更灵活。

操作 链表 顺序表
访问元素 O(n) O(1)
头部插入/删除 O(1) O(n)
尾部插入/删除 O(n) O(1)
在中间插入/插入 O(n) O(1)

链表的主要耗时操作时遍历查找,删除和插入操作本身时间复杂度很小,为O(1)
顺序表查找很快,主要耗时的操作是拷贝覆盖。

写单链表的思考、启发和建议:

  1. 考虑各种特殊情况,如为空链表时插入、插入的位置为负;

  2. 对于遍历最后的判断条件,根据不同的操作要求分别判断,如cur != Nonecur.next != None等,还要注意其他边界条件;

  3. 引入有助于理解和实现的辅助工具,如curpre等,理解其意义有助于更好地实现;

  4. 可以举例画图,有助于深入思考和理解。

利用链表实现LRU缓存算法

缓存是一种提高数据读取性能的技术,在硬件设计、软件开发中都有着非常广泛的应用,如CPU缓存、数据库缓存、浏览器缓存等等,Redis、MongoCache等等。
缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?这就需要缓存淘汰策略来决定。常见的策略有三种:先进先出策略FIFO(First In,First Out)、最少使用策略LFU(Least Frequently Used)、最近最少使用策略LRU(Least Recently Used)。
实现的简单思路:
维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表:
1.如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
2.如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部;
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
公众号二维码
大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书

发布了51 篇原创文章 · 获赞 184 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/103001726
今日推荐