【算法】链表

1. 将含有random指针的链表复制一份

第一遍循环:将每个节点复制一份放在原节点后面;

第二遍循环:每个新复制节点的random指向的是“其对应原节点的random节点的下一个节点”,如此得到一个double的大链表;

第三遍循环:将大链表拆成两份。

class Solution:
    # 返回 RandomListNode
    def Clone(self,pHead):
        # write code here
        if pHead is None:
            return pHead
        #第一遍循环复制指针
        t1 = pHead
        while t1:
            temp = RandomListNode(t1.label)
            temp.next = t1.next
            t1.next = temp
            t1 = temp.next
        #第二遍复制random指针
        t1 = pHead
        while t1:
            t2 = t1.next
            if t1.random:
                t2.random = t1.random.next
            t1 = t2.next
        #拆分成两个链表
        h1 = pHead
        h2 = pHead.next
        t1 = h1
        t2 = h2
        while t1:
            if t1.next.next:
                t1.next = t1.next.next
                t2.next = t2.next.next
            else:
                t1.next = None
                t2.next = None
            t1 = t1.next
            t2 = t2.next
        return h2

2. 判断链表是否有环

无环返回-1,有环,返回环的第一个节点的值。

方法一:使用哈希表,若走完链表无重复值则无环,否则第一个重复的值为环的入口。空间复杂度O(n);

方法二:使用快慢指针,快指针走两步,慢指针走一步。若快指针走到None,说明无环。否则当快慢指针第一次相遇时,快指针回到头结点,然后快慢指针同时向后走一步,知道两者再次相遇,该相遇点为环的入口。空间复杂度O(1)。

class ChkLoop:
    def chkLoop(self, head, adjust):
        if head is None:
            return -1
        f = head
        s = head
        #1.若快指针走到none,说明无环;2. 快慢指针相遇后,快从头以步长1走,慢继续走,下一次相遇点为环的第一个入节点
        while 1:
            #s走一步,f走两步
            if f is None or f.next is None:
                return -1
            s = s.next
            f = f.next.next
            #如果s与f相遇,出循环
            if s == f:
                break
        #第二次循环,s继续原位置走一步,f从头走一步知道相遇
        f = head
        while 1:
            if s == f:
                return s.val
            s = s.next
            f = f.next

3. 判断两个无环链表相交

方法一:哈希表,遍历链表1并将每个node放入哈希表,然后遍历链表2,若发现重复节点,则该节点为相交节点。若链表2遍历完了都没有发现重复节点,则不相交。空间复杂度O(n)。

方法二:遍历链表1,记录长度M;遍历链表2,记录长度N;先遍历长的链表知道二者长度相等,再同时遍历,若有相同节点,则返回。否则不相交。空间复杂度O(1), 时间复杂度O(m+n)

#方法二
class CheckIntersect:
    def chkIntersect(self, headA, headB):
        # write code here
        if headA is None or headB is None:
            return False
        #计算两个链表的长度
        h1 = headA
        num1 = 0
        h2 = headB
        num2 = 0
        while h1:
            h1 = h1.next
            num1 += 1
        while h2:
            h2 = h2.next
            num2 += 1
        #先遍历长链表
        if num1 > num2:
            h1 = headA
            for i in range(num1-num2):
                h1 = h1.next
            h2 = headB
        else:
            h2 = headB
            for i in range(num2-num1):
                h2 = h2.next
            h1 = headA
        #此时同时移动h1和h2,看是否有相等节点
        while h1:
            if h1 == h2:
                return True
            h1 = h1.next
            h2 = h2.next
        return False

4. 判断两个有环链表是否相交

分别找到两个的第一个入环节点(方法如第2题)node1和node2,若node1==node2, 则肯定相交,相交点在node1之前,可以根据第3题来找到相交点。若node1 != node2, 则node1往下走,在返回自己之前若与node2相遇,则相交,否则不相交。

class ChkIntersection:
    def chkInter(self, head1, head2, adjust0, adjust1):
        #找到两个链表第一个入环节点
        node1 = self.findFirstNode(head1)
        node2 = self.findFirstNode(head2)
        #如果两个入环节点相等,则说明相交的位置在入环之前,则肯定相交,返回True
        if node1 == node2:
            return True
        #如果两个入环节点不相等,让链表1的入环节点往下走,如果在遇到自己之前能遇到链表2的入环节点,则相交。
        h1 = node1.next
        while 1:
            if h1 == node2:
                return True
            if h1 == node1:
                break
            h1 = h1.next
        return False
    
    def findFirstNode(self, head):
        f = head
        s = head
        while 1:
            f = f.next.next
            s = s.next
            if f == s:
                break
        f = head
        while 1:
            if f == s:
                return f
            f = f.next
            s = s.next

5. 判断两个链表是否相交(不知道有没有环)

综合2,3,4三道题:

1)若两个有环:判断有环链表是否相交;

2)若两个无环:判断无环链表是否相交;

3)一个有环一个无环:一定不相交;

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#1. 一个有环,一个无环,不相交
#2. 两个无环,判断
#3. 两个有环,判断
class ChkIntersection:
    def chkInter(self, head1, head2, adjust0, adjust1):
        node1 = self.checkCircle(head1)
        node2 = self.checkCircle(head2)
        #若都有环,则判断两个有环链表是否相交
        if node1 and node2:
            if node1 == node2:
                return True
            p = node1.next
            while 1:
                if p == node2:
                    return True
                if p == node1:
                    break
                p = p.next
            return False
        #若都无环,则判断两个无环链表是否相交:
        if not node1 and not node2:
            h1 = head1
            h2 = head2
            num1 = 0
            num2 = 1
            while h1:
                h1 = h1.next
                num1 += 1
            while h2:
                h2 = h2.next
                num2 += 1
            if num1 > num2:
                h1 = head1
                for i in range(num1-num2):
                    h1 = h1.next
                h2 = head2
            else:
                h2 = head2
                for i in range(num2-num1):
                    h2 = h2.next
                h1 = head1
            while h1:
                if h1 == h2:
                    return True
                h1 = h1.next
                h2 = h2.next
            return False
          #若一个有环一个无环,肯定不相交,返回false
        else:
            return False
        
    #若有环,返回第一个入环节点。若无环,返回false。
    def checkCircle(self, head):
        f = head
        s = head
        while 1:
            if f is None or f.next is None:
                return False
            f = f.next.next
            s = s.next
            if f == s:
                break
        f = head
        while 1:
            if f == s:
                return f
            f = f.next
            s = s.next

总结:

trick1:在无空间复杂度要求的情况下,可以使用哈希表简化计算;

trick2:快指针走两步,慢指针走一步,第一次相交;快指针回到头走一步,慢指针走一步,第二次相交点为环的入点;

trick3:同时遍历两个链表看是否有相同节点时,先比较,后移动。

总之只要把第5题做会就OK了。

### 回答1: LRU (Least Recently Used) 算法是一种常用于缓存淘汰策略的算法,它的原理是根据数据的访问时间来进行淘汰,即最近最少使用的数据优先被淘汰。在实现 LRU 算法时,我们通常使用一个双向链表和一个哈希表来存储数据。 链表中的每个节点用来存储一个数据,节点包含三个属性:key,value 和 prev、next 指针,其中 key 和 value 分别表示数据的键和值,prev 和 next 指针用来指向前驱和后继节点。 哈希表用来存储每个数据在链表中的位置,键为数据的键,值为链表中对应节点的指针。这样,在对数据进行查找和删除时,我们只需要在哈希表中查找对应节点的指针,就可以快速地访问到链表中的数据。 每次访问数据时,我们需要将其移到链表的末尾,这样就可以保证链表的头部始终为最近最少使用的数据。当链表已满时,我们需要删除链表头部的数据,也就是最近最少使用的数据。 总的来说,实现 LRU 算法需要以下几个步骤: 1. 初始化一个双向链表和一个哈希表; 2. 每次访问数据时,将其移到链表的末尾; 3. 当链表已满时,删除链表头部的数据; 4. 在链表和哈希表中添加、删除或查找数据时,需要保证两者的一致性。 ### 回答2: Java LRU(最近最少使用)算法可以通过链表实现。 LRU算法是一种用于缓存淘汰的策略,它根据数据的使用情况来决定哪些数据将被保留在缓存中,哪些数据将被淘汰。在LRU算法中,最近最少使用的数据会被选择淘汰。 在Java中,我们可以使用链表来实现LRU算法链表的每个节点代表一个数据,节点的顺序表示数据的使用顺序,最近使用的数据位于链表的头部,最近最少使用的数据位于链表的尾部。 实现LRU算法链表需要支持两个操作:插入和删除。当有新的数据被访问时,我们首先在链表中查找该数据。如果数据已经存在于链表中,我们将其从原来的位置删除,并将其插入到链表的头部。如果数据不存在于链表中,我们将其插入到链表的头部。当链表的大小达到缓存的容量上限时,我们需要删除链表尾部的节点。 通过链表实现LRU算法有一定的优势。插入和删除操作的时间复杂度为O(1),即常数时间。同时,由于链表的特性,我们可以轻松地调整节点的顺序,以实现LRU算法的功能。 总之,通过链表实现Java LRU算法是一种简单有效的方法。它通过维护一个有序的链表结构来实现数据的使用顺序,并且支持快速的插入和删除操作。这种实现方式可以用于缓存等需要快速访问和淘汰数据的场景,提高系统性能和效率。 ### 回答3: LRU(Least Recently Used,最近最少使用)算法是一种常用的缓存替换算法,用于解决缓存空间有限的情况下,有效地管理缓存中的数据。 在实现LRU算法时,通常使用双向链表和HashMap结合的方式。 双向链表是用于记录数据的访问顺序,最近访问的数据位于链表头部,而最久未访问的数据位于链表尾部。每当有数据被访问时,如果该数据在链表中已存在,则将该数据移到链表头部;如果数据不存在,则将该数据添加到链表头部。当缓存满时,需要替换最久未访问的数据,即链表尾部的数据。 HashMap用于快速定位某个数据是否在缓存中,并记录该数据在双向链表中的位置。当需要访问数据时,首先在HashMap中查找是否存在该数据,若存在,则将该数据移到链表头部,同时更新HashMap中该数据对应的位置;若不存在,则将该数据添加到链表头部,并在HashMap中添加该数据的映射。 通过以上的链表和HashMap的结合,可以实现LRU算法。当数据被访问时,可以在O(1)的时间复杂度内完成查找和移动操作,从而提高算法的效率。 总结起来,LRU算法通过双向链表和HashMap的结合,实现了对缓存中数据的高效管理,提高了数据访问效率。这种算法在很多场景下都有广泛的应用,比如操作系统的页面置换、数据库查询优化等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值