【Code Practice】

目录

1 链表类

1.1 反转链表

1.2 从尾到头打印链表

1.3 查找链表中倒数第k个节点

1.4 合并两个递增的有序链表

1.5 复杂链表的复制(带一个random)

1.6 两个链表的第一个公共节点

1.7 链表环的入口节点

1.8 删除链表中的重复节点(1-2-2-3—>1-3)

1.9 删除链表节点O(1)

 1.10 查找链表的中间节点(如果中间有两个节点,返回第二个)

1.11 删除指定值的节点

1.12 判断是否是回文链表(时间复杂度O(n)和空间复杂度都是O(1))

1.13 链表排序

1.14 链表插入排序

1.15 删除倒数第n个节点

1.16 两两交换链表的节点

1.17 奇偶链表(index,偶数连偶数,接奇数连奇数)

1.18 反转链表II(反转第m-n的节点)

1.19 重排链表(1-2-3-4-5 》 1-5-2-4-3)


1 链表类

1.1 反转链表

增加两个指针,pre每次保存前一个,next保存后一个(防止断开),反转顺序,phead.next->pre,然后pre和phead依次后移(pre=phead, phead=next),最后终止条件是pHead=None,返回前一个节点pre

    def ReverseList(self, pHead):
        # write code here
        if not pHead:
            return None
        pre=None
        pnext=None
        while pHead:
            pnext=pHead.next
            pHead.next=pre
            pre=pHead
            pHead=pnext
        return pre #此时pHead=None.返回的是前一个节点

    #递归方法:
        """
        if not head or not head.next:
            return head
        p=self.reverseList(head.next)
        head.next.next=head
        head.next=None
        return p
        """
    #头插法(依次将后面节点移到头节点)
        if not head or not head.next:
            return head
        
        dummy=ListNode(-1)
        dummy.next=head
        pre=dummy
        p=dummy.next
        while p.next:
            temp=p.next
            p.next=temp.next
            temp.next=pre.next
            pre.next=temp
        return dummy.next

1.2 从尾到头打印链表

思路一:从头到尾依次打印并保存,逆序返回

思路二:递归实现,每次都先递归到下一个节点,当前值最后保存,则保存的结果就是从最后一个节点向前的顺序

递归时,两个关键因素:终止条件,递归过程的语句顺序(此题中,先依次调用本函数,再添加元素,注意语句顺序)

#method 1:
    def printListFromTailToHead(self, listNode):
        # write code here
        a=[]
        while listNode:
            a.append(listNode.val)
            listNode=listNode.next
        return a[::-1]

#method 2:
def __init__(self):
    self.res=[]
def printListFromTailToHead(self, listNode):
    if listNode:
        self.printListFromTailToHead(listNode.next)
        self.res.append(listNode.val)
    return self.res

#or:
def printListFromTailToHead(self, listNode):
    if not listNode:
        return []
    return self.printListFromTailToHead(listNode.next)+[listNode.val]

1.3 查找链表中倒数第k个节点

思路一:一次遍历找到总长度,然后再遍历len-k+1步(时间复杂度较高)

思路二:快慢指针方法(两个指针,快的先走,慢的相隔k-1步再走,当快的走到尾时,慢的刚好在倒数第k个,返回慢的)

考虑特殊情况:空链表、k为0、链表长度小于k

快慢指针还可解决:查找链表中间节点(快的一次走两步,慢的一次走一步,快的到尾时,返回慢的)

    def FindKthToTail(self, head, k):
        # write code here
        if not head or k==0:
            return None
        pbehind=head
        for i in range(k-1):
            if head.next: #注意此处判断条件
                head=head.next
            else: #考虑长度小于k的情况
                return None
        while head.next: #注意此处判断条件
            head=head.next
            pbehind=pbehind.next
        return pbehind

1.4 合并两个递增的有序链表

思路一:非递归,新建一个列表,并记录表头。在都不为空的情况下,顺次比较两个链表的表头,每次取出较小的为新链表的下一个节点,然后较小的节点后移。每比较一次,新链表的记录都后移一次(否则只会在表头操作)。遍历结束后,如果有非空的,则直接添加。最后返回新链表的记录表头。

思路二:递归

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        #递归方法:
        if not pHead1:
            return pHead2
        if not pHead2:
            return pHead1
        if pHead1.val<pHead2.val:
            merge=pHead1
            merge.next=self.Merge(pHead1.next,pHead2)
        else:
            merge=pHead2
            merge.next=self.Merge(pHead1,pHead2.next)
        return merge
    
                """
        非递归方法:
        pnew=ListNode(-1)#任意指定一个头为-1的链表
        res=pnew #保存该链表的头,因为后面pnew会一直后移,需要记录表头位置
        while pHead1 and pHead2:
            if pHead1.val<pHead2.val:
                pnew.next=pHead1
                pHead1=pHead1.next
            else:
                pnew.next=pHead2
                pHead2=pHead2.next
            pnew=pnew.next #每次都要后移当前节点,否则会一直在第一个-1节点
        if pHead1:
            pnew.next=pHead1
        if pHead2:
            pnew.next=pHead2
        return res.next #因为头节点是-1,从下一个开始返回
        """

1.5 复杂链表的复制(带一个random)

带random,可能在前面也可能在后面,不能直接复制。三步:

每个节点后面都插入一个该节点的复制;将复制节点的random链接(原节点的random.next);拆分,返回所有复制节点

【Code Practice】

 

    def Clone(self, pHead):
        # write code here
        #递归方法:
        if not pHead:
            return None
        clonehead=RandomListNode(pHead.label)
        clonehead.random=pHead.random
        clonehead.next=self.Clone(pHead.next)
        return clonehead
        
        """
        非递归
        #1 复制原链表
        pclone=pHead#避免破坏原链表表头
        while pclone:
            copy=RandomListNode(pclone.label)
            pnext=pclone.next
            pclone.next=copy
            copy.next=pnext
            pclone=pnext
        #2 复制random
        dummy=pHead
        while dummy:
            clone=dummy.next
            if dummy.random:
                clone.random=dummy.random.next
            dummy=clone.next
        #3 返回复制链表(偶数项)
        dummy=pHead
        copyNode=pHead.next
        while dummy:
            copynode=dummy.next
            dummynext=copynode.next
            if dummynext:
                copynode.next=dummynext.next
            else:
                copynode.next=None
            dummy=dummynext
        return copyNode
        """

1.6 两个链表的第一个公共节点

思路一:先遍历得到两个长度差step,然后长链表先走step,然后两个一起走并比较是否相等,返回第一个相等的节点

思路二:分别遍历并用两个栈分布存储,然后从栈顶开始一次pop,返回最后一个相等的点

    def FindFirstCommonNode(self, pHead1, pHead2):
        # write code here
    #method 1
        #先分别遍历得到长度
        length1,length2=0,0
        pHead11=pHead1
        pHead22=pHead2
        while pHead11:
            length1+=1
            pHead11=pHead11.next
        while pHead22:
            length2+=1
            pHead22=pHead22.next
        if length2>=length1:
            longhead=pHead2
            shorthead=pHead1
        else:
            longhead=pHead1
            shorthead=pHead2
        step=abs(length2-length1)
        #长链表先走,直到两个长度相等,然后同时走,
        for i in range(step):
            longhead=longhead.next
        while longhead and shorthead:
            if longhead==shorthead:
                return longhead
            longhead=longhead.next
            shorthead=shorthead.next
            
        """
    method 2
        stack1=[]
        stack2=[]
        while pHead1:
            stack1.append(pHead1)
            pHead1=pHead1.next
        while pHead2:
            stack2.append(pHead2)
            pHead2=pHead2.next
        temp=None
        while stack1 and stack2 and stack1[-1]==stack2[-1]:
            temp=stack1[-1] #存储每一对相等的点
            stack1.pop()
            stack2.pop()
        return temp
            """

1.7 链表环的入口节点

快慢指针,快一次走两步,慢一次走一步,直到第一次相遇;

假设环有n个节点,第一次相遇时,慢的走了a,快的走了2a,则有a+n=2a  ,a=n,即慢的走了n步;

此时再让快的回到头,两个一起走每次都为一步,设x为头距入口的距离,y是入口距慢的距离,x+y=n, 所以当快走x步到达入口时,慢的走了x=n-y步,即一周又退回到入口处,两个刚好相遇 

    #判断是否有环:快慢指针,如果相遇,则有环
    def hasCycle(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        f,s=head,head
        while f and f.next:
            f=f.next.next
            s=s.next
            if f==s:
                return True
        return False

    #查找环的入口
    def EntryNodeOfLoop(self, pHead):
        # write code here
        if not pHead or not pHead.next or not pHead.next.next:
            return None
        slow=pHead.next
        fast=pHead.next.next
        while fast != slow:
            if not fast.next or not fast.next.next: #要判断,如果有None则说明没有环
                return None
            slow=slow.next
            fast=fast.next.next
        #slow刚好走了n步(n:环的节点个数) 
        # fast退回头结点,两个同时走,每次一步,直到再相遇
        fast=pHead
        while fast!=slow:
            fast=fast.next
            slow=slow.next
        return fast

1.8 删除链表中的重复节点(1-2-2-3—>1-3)

对于链表操作,主要是确定next,建立一个链表res=ListNode(-1),然后定义指向链表头的指针pre=res,通过操作pre指定next,指针pre依次后移,最后返回建立起res完整链表,最后返回的是res(因为pre一直在变,是操作指针)

    #递归方法
#method 1
    def deleteDuplication(self, pHead):
        # write code here
        if pHead==None or pHead.next==None:
            return pHead
        if pHead.val==pHead.next.val:
            temp=pHead.next
            while temp!=None and temp.val==pHead.val:
                temp=temp.next
            return self.deleteDuplication(temp)
        else:
            temp=pHead.next
            pHead.next=self.deleteDuplication(temp)
            return pHead
        
    """
    # 非递归方法
        if not head or not head.next:
            return head
        dummy=ListNode(-1)
        dummy.next=head
        p=dummy
        while head and head.next:
            if head.next.val==head.val:
                val=head.val
                while head and head.val==val:
                    head=head.next
                p.next=head #只记录,不后移
            else:
                p=head #只有不重复时候,才会后移并记录
                head=head.next
        if head:p.next=head
        return dummy.next
        """

#重复节点,保留一个 1-1-2-3-3(1-2-3)
    def deleteDuplicates(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        Head=ListNode(0)
        p=Head
        while head !=None and head.next!=None:
            if head.val==head.next.val:
                p.next=head #甭管重复与否,先记录
                temp=head.val
                while head and head.val==temp:
                    head=head.next
            else:
                p.next=head
                head=head.next
            p=p.next #每次都会后移
        p.next=head
        return Head.next
        """
        #递归方法
        if not head or not head.next:
            return head
        head.next=self.deleteDuplicates(head.next)
        if head.val==head.next.val:
            head=head.next
        return head
        """

1.9 删除链表节点O(1)

将该节点的下一个节点值复制到该节点上,然后跳过下一个节点,指向node.next.next

    def deleteNode(self,phead, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        #若是尾节点,需要从头遍历
        if node.next==None: 
            while phead.next!=node:
                phead=phead.next
            phead.next=None
        else:#非尾节点
            node.val=node.next.val #复制下个节点值到当前节点
            node.next=node.next.next #跳过下个节点

 1.10 查找链表的中间节点(如果中间有两个节点,返回第二个)

快慢指针。重点是判断条件。

    def middleNode(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        fast=head
        slow=head
        #当下一个非空,下下一个空,也要再走一步,slow就是第二个中间节点了
        while fast!=None and fast.next!=None:
            fast=fast.next.next
            slow=slow.next
        return slow

1.11 删除指定值的节点

遍历,如果非val,则加入p,head后移,p也后移,否则head后移不加入p。

特殊情况:(1,2,5,6)val=6的情况,最后几个元素是val值,则p加入的最后一个元素是5,但是5的next还是6,所以结果还是1256,所以遍历结束后,指定p.next为head(此时head为空)或None

    def removeElements(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        H=ListNode(0)
        p=H
        while head:
            if head.val!=val:
                p.next=head
                head=head.next
                p=p.next
            else:
                head=head.next
        p.next=head #特殊情况,最后几个元素是val
        return H.next

1.12 判断是否是回文链表(时间复杂度O(n)和空间复杂度都是O(1))

回文:翻转后和原来一样。空间复杂度是O(1),说明不能建立新的复制链表了,只能原地操作,唯有原地反转了。如果都反转则无法比较了。所以,只反转后半部分,然后和前半部分进行逐一比较。快慢指针找到中间节点->反转后半部分->前后比较

    def isPalindrome(self, head):
        """
        :type head: ListNode
        :rtype: bool
        """
        fast,slow=head,head
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
        mid=slow
        pre=None
        while mid:
            nex=mid.next
            mid.next=pre
            pre=mid
            mid=nex
        #pre是后半部分反转后的头节点
        while pre:
            if pre.val!=head.val: #比较的值是否一样,记得加val!!!
                return False
            pre=pre.next
            head=head.next
        return True

1.13 链表排序

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

思路一:先遍历一遍并用数组记录链表的所有值,对数组排序,再遍历一遍以数组值将每个节点重新重新赋值

思路二:归并算法:快慢指针,查找中间节点;合并两个子链表。

    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        """
        #归并法:快慢指针每次找到中间节点,合并两个子链表
        if not head or not head.next:
            return head
        fast,slow=head,head
        pre=None
        while fast and fast.next:
            pre=slow #保存最终中间节点的前一个值,并赋值None,实现分割
            slow=slow.next
            fast=fast.next.next
        pre.next=None #将head实现分割
        left=self.sortList(head)
        right=self.sortList(slow)
        return self.merge(left,right)
    
    def merge(self,left,right):
        if not left:
            return right
        if not right:
            return left
        if left.val<right.val:
            res=left
            res.next=self.merge(left.next,right)
        else:
            res=right
            res.next=self.merge(left,right.next)
        return res
    """
        #用链表存储所以节点值,然后排序,并给链表从头重新赋值,但是空间复杂度是O(n)
        res=[]
        q=head
        while q:
            res.append(q.val)
            q=q.next
        res.sort()
        q=head
        leng=len(res)
        for i in range(leng):
            q.val=res[i]
            q=q.next
        return head

1.14 链表插入排序

新建链表,指向head,每次比较时,先取出当前节点,然后从头遍历新链表找到插入的位置,然后将当前节点插入到该位置,当前点继续后移。

    def insertionSortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        """
        if not head or not head.next:
            return head
        dummy=ListNode(-1)
        q=head
        while q:
            temp=q.next
            pre=dummy
            while pre.next and pre.next.val<q.val:
                pre=pre.next
            q.next=pre.next
            pre.next=q
            q=temp
        return dummy.next
        """
    #不额外开辟新的链表空间,直接指向当前链表
        if head is None or head.next is None:
            return head
        
        dummy=ListNode(0)
        cur=dummy.next=head
        
        while cur.next:
            
            if cur.val>cur.next.val:
            
                pre=dummy
                while pre.next.val<cur.next.val:
                    pre=pre.next
                m=cur.next
                cur.next=m.next
                m.next=pre.next
                pre.next=m
            else:
                cur=cur.next
        
        return dummy.next

1.15 删除倒数第n个节点

考察求倒数第n+1个节点

思路一:两次遍历:先遍历一遍,得到总长度,然后从头遍历直接找到倒数第n+1的节点,删除

思路二:快慢指针,先找到倒数第n个节点(fast和slow相隔n-1步),并记录倒数第n+1节点,然后删除;或者fast和slow相隔n步,则可以直接找到倒数第n+1个节点)

    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
    #fast和slow相隔n步,直接找到倒数第n+1个节点(注意特殊情况
    #n有可能是头节点,此时fast刚好是None)

        if not head:
            return None
        fast,slow=head,head
        #假设n有效,不判断n是否超出长度
        #fast先走n-1步,找到倒数第n个节点;走n步,
        #找到倒数n节点的前一个节点
        for i in range(n):
            fast=fast.next
            if i<n-1 and fast==None: #判断n是否有效
                return None
                
        if fast==None: #刚好是要删除头结点
            head=head.next
            return head
        else:  #找到倒数第n+1个节点
            while fast.next:
                fast=fast.next
                slow=slow.next
            slow.next=slow.next.next
            return head

        """
    fast和slow相隔n-1步,直接找到slow是倒数第n个节点,此时要记录倒数第n+1节点
        if not head:
            return None
        fast,slow=head,head
        for i in range(n-1):
            fast=fast.next
            if fast==None:
                return None
        dummy=ListNode(-1)
        dummy.next=head
        q=dummy
        #slow刚好找到倒数第n个节点
        #q比slow还慢一步,刚好是slow前面的节点
        while fast.next:
            fast=fast.next
            q=q.next
            slow=slow.next
        temp=slow
        q.next=temp.next
        return dummy.next
        """

1.16 两两交换链表的节点

记录当前节点,下一个节点,两个反转,然后后移。注意第一个节点要和第四个节点连接(当第三个是非空时)第三个为空时则和第三个节点相连。否则会断开 

    def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
    
        if not head or not head.next:
            return head
        
        pre=head
        p=pre.next
        head=head.next
        while p and pre:
            temp=p.next
            p.next=pre
            if not temp:
                pre.next=temp
                return head
            #注意,第一个和第四个连接,只有第四个是空时和第三个连接
            pre.next=temp if temp.next== None else temp.next 
            pre=temp
            p=temp.next
        return head
        """
        
        #递归
        if not head or not head.next:
            return head
        nex=head.next
        head.next=self.swapPairs(nex.next)
        nex.next=head
        return nex

        """

1.17 奇偶链表(index,偶数连偶数,接奇数连奇数)

分别定义偶数头节点和奇数头节点,依次后移,最后偶数最后一个节点接奇数的头结点

    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        dummy=head
        dummy_odd=head.next
        peven=dummy
        podd=dummy_odd
        while podd and podd.next:
            peven.next=podd.next
            peven=peven.next
            podd.next=peven.next
            podd=podd.next
        if podd: #偶数个节点的情况,需要将最后一个偶数节点的next指定为空
            podd.next=None
        peven.next=dummy_odd #最后一个偶数节点,接奇数头结点
        return dummy

1.18 反转链表II(反转第m-n的节点)

思路一:普通原地反转,找到并记录几个关键节点方便后续连接(第m,m-1,n,n+1)

思路二:头插法反转更容易理解。新建头结点,向后遍历,依次将p.next插入至头结点的后面。

    def reverseBetween(self, head, m, n):
        """
        :type head: ListNode
        :type m: int
        :type n: int
        :rtype: ListNode
        """
        #头插法,简单明了:
        
        if not head or not head.next:
            return head
        dummy=ListNode(-1)
        dummy.next=head
        p=dummy
        #先移动m步,找到头结点pre:
        for i in range(m):
            pre=p
            p=p.next
        #每次移动,都插入到头结点后面
        for j in range(n-m): #总是通不过,发现竟然写成了m-n
            temp=p.next
            p.next=temp.next
            temp.next=pre.next
            pre.next=temp
        return pre
        """
        #普通做法,反转第m-n的链表,再拼接
        dummy=ListNode(-1)
        dummy.next=head
        pm=dummy
        for i in range(m):
            front=pm  #记录第n-1个节点
            pm=pm.next
        pre=pm #记录第n个节点
        pnewpre=None
        for j in range(n-m+1): #要多走一步,pm指向第n+1个节点
            nex=pm.next
            pm.next=pnewpre
            pnewpre=pm
            pm=nex
        front.next=pnewpre
        pre.next=pm
        return dummy.next
        """

1.19 重排链表(1-2-3-4-5 》 1-5-2-4-3)

思路:三步走,快慢指针找到中间节点,反转后半部分,前后两部分合并

    def reorderList(self, head):
        """
        :type head: ListNode
        :rtype: None Do not return anything, modify head in-place instead.
        """
        if not head or not head.next:
            return head
        #1 快慢指针,找到中间节点
        fast=slow=head
        while fast and fast.next:
            slow=slow.next
            fast=fast.next.next
        #2 将后半部分逆序,头插法
        head_back=ListNode(-1)
        head_back.next=slow.next #后半部分的头结点,保证后半部分小于前半部分,方便合并
        p=head_back.next
        pre=head_back
        while p and p.next:
            temp=p.next
            p.next=temp.next
            temp.next=pre.next
            pre.next=temp
        #后半部分是head_back.next,已经逆序
        slow.next=None #将前半部分封死 
        #3 两段合并
        
        #1-2-3-Null 
        #4-5-null
        
        #1-2-3-null
        #4-null
        head2=head_back.next
        head1=head
        while head2:
            temp2=head2.next
            temp1=head1.next
            head1.next=head2
            head2.next=temp1
            head2=temp2
            head1=temp1