Leetcode 笔记2

  Linked List

基本上都要用dummy

dummy = Listnode()

dummy.next = head

return的时候return dummy.next--因为head的位置有可能变化了(head有可能做指针用)

1.获取第N个节点的值(从0开始)

1.判断n在Limit之间

if n < 0 and n > size - 1:

        return None

2.创建指针cur, 让cur = dummy.next

3.遍历到n节点(当n = 0的时候,skip while loop, cur还是head,head是第0个节点)

while n:

        cur = cur.next

        n-=1

return cur

2.头部插入节点

1.创建newNode

2.让newNode指向head(dummy.next)

newNode.next = dummy.next

3.让dummy指向newNode

dummy.next = newNode

3.尾部插入节点

1.cur = dummy

2.让cur指到null之前的最后一个节点(while找到cur.next为空就停止了)

while(cur.next):

        cur = cur.next

3.让cur直接指向newNode, newNode不用指向null,因为后面本来就是null

cur.next = newNode

4.第n个节点前插入节点

第n个节点是cur.next, 第n-1个节点是cur

1.cur = dummy

2.遍历寻找第n个节点(举极端的例子,n= 0,cur = dummy, 由于第n-1个节点是cur, 符合题意,所以遍历没有写错)

while n:

        cur = cur.next

        n-= 1

3.让newNode指向cur.next(先更新后面的边)

newNode.next  = cur.next

4.让cur指向newNode(再更新前面的边)

cur.next = newNode

5.删除第n个节点

1. cur = dummy

2.第n个节点是cur.next, 第n-1个节点是cur,操作cur才能删掉第n个节点.找要删的第n个节点=cur.next

(n=0时不要while loop. cur = dummy. cur.next = head. head是要删掉的第0个节点。cur.next是想要删掉的节点)

while n:

        cur = cur.next

        n-=1

3.删除cur.next. 让cur直接指向cur.next.next

cur.next = cur.next.next

4.删了一个节点,size减少1

size -= 1

 2. 两数相加

最好重新create一个列表的时候都用dummy开头

如果是两个列表,要对于值进行计算,遍历的时候直接 while l1不要l1.next应该会更清楚一点

要建立每一个node都需要用Listnode(),如果里面有值的话,比如将值5填进 Listnode(5)

这一题的难点在于怎么进位,用addDigit。还有就是l1和l2不一定长度一样,如果l1或者l2没有值了该如何表示。

def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        #create dummy
        dummy = ListNode(0)
        cur = dummy
        #进位
        addDigit = 0
        #只要有一个list有值,就不能断
        while l1 or l2:
            if l1:
                val1 = l1.val
            if not l1:
                val1 = 0
            if l2:
                val2 = l2.val
            if not l2:
                val2 = 0
            total = val1 + val2 + addDigit
            #每一个node都要用listNode来建立
            #这个node的值是余数
            cur.next = ListNode(total%10)
            #进位加到下一个node上(更新addDigit的值)
            addDigit = total // 10
            print(addDigit)
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
            cur = cur.next
        if addDigit:
            cur.next = ListNode(addDigit)
        return dummy.next

445. 两数相加 II

要倒着来,有两种方法,一种是把两个linked list倒着,相加,然后把新的linked list 再到过来

一种是取两个stack,把所有的Node push进去,然后pop出来相加,再用头插法构建新的linked list.

 

 

def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        cur = dummy = ListNode(0)
        #使用两个栈,分别push() 进去各自节点, 一次pop()出来,就相当于反转
        stack1 = []
        stack2 = []
        #l1 = [7,2,4,3]
        #把l1里所有的node.val都倒进栈里
        while l1:
            stack1.append(l1.val)
            l1 = l1.next
        #l2 = [5,6,4]
        #把l2里所有的node.val都倒进栈里
        while l2:
            stack2.append(l2.val)
            l2 = l2.next
        #建立dummyhead
        dummy = ListNode(0)
        addDigit = 0
        while stack1 or stack2 or addDigit:
            s1 = stack1.pop() if stack1 else 0
            s2 = stack2.pop() if stack2 else 0
            #pop的时候从最后pop,l1先pop 3,l2 pop 4.
            #加成total
            total = s1 + s2 + addDigit
            cur = ListNode(total%10)
            addDigit = total // 10
            #头插法
            #先把3+4=7这个node加进dummy后面
            #然后把4+6=0这个node加入dummy后面,7的前面
            #然后把2+5+1=8这个node加入dummy后面,0的前面
            #然后把7+0=7这个Node加入dummy后面,8的前面
            #dummy->7->8->0->7
            #把cur这个node加到head后,head.next之前
            cur.next = dummy.next 
            dummy.next = cur
        #7->8->0->7
        return dummy.next

24. 两两交换链表中的节点

 

def swapPairs(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        p = dummy = ListNode(0)
        dummy.next = head 
        stack = []
        #head = [1,2,3,4]
        #把前两个1,2放进stack
        while head and head.next:
            stack.append(head)
            stack.append(head.next)
            #p移到两个node的末尾,cur移到下一个两个node的开头
            #head移动到3的位置
            head = head.next.next
            #pop 2, 把p连接2(p现在是dummy)
            a = stack.pop()
            p.next = a
            #pop 1,把2连接1
            b = stack.pop()
            a.next = b
            #把1连接3(head是3)
            b.next = head
            #把p也往后移两个,移动到2
            p = p.next.next
        #最后如果剩下一个一个node(奇数)
        if head:
            #p后面直接接head
            p.next = head
        #如果没剩Node(偶数)
        else:
            #p后面直接接Null
            p.next = None
        return dummy.next

方法二:递归

def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        #base case(递归结束条件)
        if not head or not head.next:
            return head 
        #head = [1,2,3,4]
        # 确定cur位置,cur = 2
        cur = head.next
        # head指向递归函数,函数的head是3,3是2的next
        head.next = self.swapPairs(cur.next)
        #cur = 2 指向head = 1
        cur.next = head
        #现在cur 是第一个数,所以从cur开始
        return cur

23. 合并K个升序链表

在lists里面找第二个到最后一个: lists[1:]

head和list1 都是整个list也是pointer

def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        def mergeTwoLists(l1,l2):  
            #建立一个空的list
            dummy = ListNode(0)
            #让cur从dummy开始比较好
            cur = dummy
            #如果只有l1,就return l1
            if not l2:
                return l1
            #如果只有l2,就return l2
            if not l1:
                return l2
             #有四种情况
            #有l2和l1
            while l1 and l2:
                #1.l1大
                if l1.val > l2.val:
                    cur.next = l2
                    l2 = l2.next
                #2.l2大
                else:
                    cur.next = l1
                    l1 = l1.next
                cur = cur.next
            #3.有l1没有l2了
            if l1:
                cur.next = l1
            #4.有l2没有l1了
            if l2:
                cur.next = l2
            return dummy.next
        if not lists:
            return None
        #拿一个sublist做res
        res = lists[0]
        #res需要和后面sublists的合并
        for list in lists[1:]:
            #两个变一个
            res = mergeTwoLists(res, list)
        return res

19. 删除链表的倒数第 N 个结点

找到链表的倒数第n个节点:

让p1从head走k步:

让p2到head:

 让p1和p2一起走,走到p1 == null:

p2正好在倒数第k步:

问题:

如何删除链表中的一个Node:

用node.next = node.next.next删除

为什么使用dummy:

因为如果要删除第n个节点,我们要求的是n-1个节点,然后让node.next = node.next.next.

在要删除第一个节点(倒数第n个节点)的情况下,我们假设总共五个节点,需要删除第一个节点(倒数第五个节点),那我们就要找到倒数第六个节点,然后删除他后面的node,由于倒数第六个节点没有,所以我们要设置dummy node来找到倒数第六个节点。

此题详细图解:

力扣https://leetcode.cn/problems/remove-nth-node-from-end-of-list/solution/dong-hua-yan-shi-kuai-man-zhi-zhen-19-sh-n9ih/

碰到这个类型的题时,我们要先把快指针指向最后的null,把慢指针指向我们需要的位置(这一题中应该是n-k位置),然后把慢指针移到dummy, 快指针挪同样的步骤,然后算快指针从dummy到现在的位置需要几步(需要几步有几次for loop循环)。

这个时候, while fast, 因为fast指向的是null。

删除慢指针后面一个node,最后return dummy.next

如果创造了dummy,我们一定要return dummy.next

不确定是while fast对不对,可以写while fast != none,这样肯定取不到后面的none

def removeNthFromEnd(self, head, n):
        
        dummy = ListNode()
        #要让dummy.next = head,只创造dummy不行
        dummy.next = head
        p1 = dummy
        p2 = dummy
        for i in range(n+1):
            p1 = p1.next
        #fast有值,是指fast是最后一个数
        while p1:
            p1 = p1.next
            p2 = p2.next
        p2.next = p2.next.next
        return dummy.next

876. 链表的中间结点

具体的图:

力扣https://leetcode.cn/problems/middle-of-the-linked-list/solution/dong-hua-yan-shi-kuai-man-zhi-zhen-876-l-qdto/中间节点我们也可以用fast和slow指针来做

fast 移动两步,slow移动一步

while fast.next有值的时候,才能移动fast,这样正好可以得到slow的值是正确的。

但是我们要让fast也有值因为如果fast没值,fast就不能有next

def middleNode(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        fast = head
        slow = head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        return slow

成环:

如果fast走两步,slow走一步,他们最终能相遇,那就是环

160. 相交链表

方法一:

def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        if not headA or not headB:
            return None
        node1 = headA
        node2 = headB
        #他俩相等的时候开始相交
        while node1 != node2:
            #如果里面不用if用while,那么就变成了O(n^2)了
            if node1 is None:
                #node1这时候已经是none了,所以直接等于headB.不是node1.next = headB
                node1 = headB
            else:
                node1 = node1.next
            if node2 is None:
                node2 = headA
            else:
                node2 = node2.next
        return node1

第二种解法:

JAVA代码

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    int lenA = 0, lenB = 0;
    // 计算两条链表的长度
    for (ListNode p1 = headA; p1 != null; p1 = p1.next) {
        lenA++;
    }
    for (ListNode p2 = headB; p2 != null; p2 = p2.next) {
        lenB++;
    }
    // 让 p1 和 p2 到达尾部的距离相同
    ListNode p1 = headA, p2 = headB;
    if (lenA > lenB) {
        for (int i = 0; i < lenA - lenB; i++) {
            p1 = p1.next;
        }
    } else {
        for (int i = 0; i < lenB - lenA; i++) {
            p2 = p2.next;
        }
    }
    // 看两个指针是否会相同,p1 == p2 时有两种情况:
    // 1、要么是两条链表不相交,他俩同时走到尾部空指针
    // 2、要么是两条链表相交,他俩走到两条链表的相交点
    while (p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1;
}

141. 环形链表

让两个指针 slow 和 fast 分别指向链表头结点 head。每当慢指针 slow 前进一步,快指针 fast 就前进两步。

如果 fast 最终遇到空指针(注意此处由于快指针每次是走两步,所以也可能是fast.next遇到空指针,即fast.next的next为空,fast.next.next为空),说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head:
            return head
        fast = head
        slow = head
        # 除了fast可能遇到空指针,fast.next也可能遇到空指针
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False

双指针 fast一次两步,slow一次一步,如果能追上就是有环
若存在环。开始找环。
设head到入口长度为x,入口到相遇点长度为y,相遇点到入口距离为z

 

y+z为环长度。有fast走两步slow走一步可知,相当于进环之后fast在一步一步追slow对吧,直到二者相遇,且这段距离绝对不大于y+z。但slow进环之前fast转了多少圈并不知道。设为n
x+y为总循环次数,x + y = n(z+y), so x = (

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值