LeetCode算法题-链表类

1.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 (可以参照第2的merge2List实现)

示例:

输入:1->2->4, 1->3->4

输出:1->1->2->3->4->4

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/merge-two-sorted-lists

 

易错点:

(1)没考虑其中一个是空链表情况

(2)往前插入和往后插入的情况处理没想清楚

 

最后提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def mergeTwoLists(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if l1 is None:
            return l2
        if l2 is None:
            return l1
        head = None
        cur = l1
        l2_cur = l2
        while l2_cur is not None:
            l2_cur_tmp = l2_cur.next
            if cur.val > l2_cur.val:
                head = l2_cur
                l2_cur.next = cur
                cur = l2_cur
            else:
                if head is None:
                    head = cur
                while cur.next is not None and cur.next.val <= l2_cur.val:
                    cur = cur.next
                l2_cur.next = cur.next
                cur.next = l2_cur
                cur = cur.next
            l2_cur = l2_cur_tmp
        return head

 

2.合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:

[

  1->4->5,

  1->3->4,

  2->6

]

输出: 1->1->2->3->4->4->5->6

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/merge-k-sorted-lists

 

易错点:

(1)比较容易想到第1个和第2个合并,然后再和第3个合并,但这属于暴力破解法了

(2)还容易想到每次比较每个链表的最前面节点调最小的,比较的方法可以使用最小堆

(3)其实好的方法应该是分治思想,比如先每组两两结合,然后4,4结合,以此类推,做到时间复杂度为O(nlgk)

(4)实现merge2List函数使用一个head作为头指针,可使得程序更易读和写

 

 

最后提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        list_num = len(lists)
        interval= 1
        while interval < list_num:
            for i in range(0, list_num - interval, interval * 2):
                lists[i] = self.merge2Lists(lists[i], lists[i+interval])
            interval *= 2
        return lists[0] if list_num > 0 else None

    def merge2Lists(self, l1, l2):
        pre_cur = head = ListNode(0)
        cur = l1
        head.next = cur
        while l2 != None:
            l2_next = l2.next
            if cur is None or cur.val > l2.val:
                l2.next = cur
                pre_cur.next = l2
                pre_cur = l2
            else:
                while cur.next != None and l2.val >= cur.next.val:
                    cur = cur.next
                l2.next = cur.next
                cur.next = l2
                pre_cur = cur
                cur = cur.next
            l2 = l2_next
        return head.next

 

官方对于merge2Lists有个更优的解放:

    def merge2Lists(self, l1, l2):
        point = head = ListNode(0)
        while l1 and l2:
            if l1.val <= l2.val:
                point.next = l1
                l1 = l1.next
            else:
                point.next = l2
                l2 = l1
                l1 = point.next.next
            point = point.next
        if l1:
            point.next = l1
        else:
            point.next = l2
        return head.next

优点:当两个链表长度相差较大时,时间复杂度是O( min( l1_len, l2_len ) )

实现思路:

point始终和l1保持在一条链表上,方便判断

 

3.给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例:

给定 1->2->3->4, 你应该返回 2->1->4->3.

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs

 

易错点:

(1)两两交换后,没有考虑前面的指向,比如交换了3和4,但却没有把1的指向指向4,导致输出结果为2->1->3,这是我一开始犯的错

(2)增加一个pre指针头部可以让程序逻辑简化

 

 

最后提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def swapPairs(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        pre = ListNode(0)
        pre.next = head
        first = head
        head = pre
        while first and first.next:
            second = first.next
            first.next = second.next
            second.next = first
            pre.next = second
            pre = first
            first = first.next
        return head.next

 

4.给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/reverse-nodes-in-k-group

 

易错点和关键点:

(1)首先联系到链表的翻转算法逻辑,每个k个翻转一次就好,最后一个可能不够k,所以遍历时发现不够就再翻转一次即返回之前那样

(2)处理每段k个这里的细节第一版代码处理的很繁琐,这里想了许久,实现思想如下图

 

当处理完1,2,3,4时,1指向5,但当处理完5,6,7,8时,1应改为指向8

 

第一版提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if k <= 0:
            return None
        if k == 1:
            return head
        pre_list_node = None
        cur = head
        head = None
        while cur != None:
            ret = self.reverse_k_list(cur, k)
            if ret == cur:
                break
            if pre_list_node != None:
                pre_list_node.next = ret
            pre_list_node = cur
            if not head:
                head = ret
            cur = cur.next
        return head if head else cur

    def reverse_k_list(self, cur, k):
        flag = 0
        first = cur
        pre = None
        while cur and flag < k:
            flag += 1
            l_next = cur.next
            cur.next = pre
            pre = cur
            cur = l_next
        if flag < k:
            cur = pre
            pre = None
            while flag > 0:
                flag -= 1
                l_next = cur.next
                cur.next = pre
                pre = cur
                cur = l_next
        else:
            first.next = cur
        return pre

 

经过优化修改后的版本:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def reverseKGroup(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if k <= 0:
            return None
        if k == 1:
            return head
        pre_list_node = ListNode(0)
        cur = head
        head = pre_list_node
        while cur != None:
            ret = self.reverse_k_list(cur, k)
            pre_list_node.next = ret
            pre_list_node = cur
            cur = cur.next
        return head.next

    def reverse_k_list(self, cur, k, is_reverse_back=False):
        flag = 0
        first = cur
        pre = None
        while cur and flag < k:
            flag += 1
            l_next = cur.next
            cur.next = pre
            pre = cur
            cur = l_next
        if flag < k:
            pre = self.reverse_k_list(pre, flag, is_reverse_back=True)
        else:
            if not is_reverse_back:
                first.next = cur
        return pre

这个版本的优化在于优化了一些繁琐的代码,比如再翻转可以利用原函数,找head更简单,使程序逻辑看上去更清晰

 

5.给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: 1->2->3->4->5->NULL, k = 2

输出: 4->5->1->2->3->NULL

解释:

向右旋转 1 步: 5->1->2->3->4->NULL

向右旋转 2 步: 4->5->1->2->3->NULL

 

示例 2:

输入: 0->1->2->NULL, k = 4

输出: 2->0->1->NULL

解释:

向右旋转 1 步: 2->0->1->NULL

向右旋转 2 步: 1->2->0->NULL

向右旋转 3 步: 0->1->2->NULL

向右旋转 4 步: 2->0->1->NULL

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/rotate-list

 

关键点和易错点:

(1)这道题思路很简单,一开始想到的就是跟倒数第几个类似的方法,这里多了对k取模操作,但不必通过走到最后一个None才知道哪个是要变头部的,因为我们有整个链表长度了,可以直接计算得到第几个

(2)计算第几个时注意要用l_len-k才是算第几个,否则是倒数的

 

连接成环再拆思路代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if not head:
            return head
        tmp_head = head
        l_len = 1
        while tmp_head.next:
            l_len += 1
            tmp_head = tmp_head.next
        tmp_head.next = head
        k = k % l_len
        k = l_len - k
        tmp_head = head
        pre = None
        while k > 0:
            pre = tmp_head
            tmp_head = tmp_head.next
            k -= 1
        pre.next = None
        head = tmp_head
        return head

 

6.给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深拷贝。 

示例:

输入:

{"$id":"1","next":{"$id":"2","next":null,"random":{"$ref":"2"},"val":2},"random":{"$ref":"2"},"val":1}

 

解释:

节点 1 的值是 1,它的下一个指针和随机指针都指向节点 2 。

节点 2 的值是 2,它的下一个指针指向 null,随机指针指向它自己。

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/copy-list-with-random-pointer

 

关键点和易错点:

(1)这道题的难点在于处理这个random,常规的方法很容易想到,用一个字典保存原节点对应新节点,然后遍历一遍即可

(2)第一种思路需要使用O(n)的空间,有一种巧妙的解法是O(1)的空间复杂度,但时间复杂度是O(3n),第一遍遍历复制节点并把新节点放到旧结点的前面,用next指针串起来,然后再遍历一遍处理random指针,最后一遍遍历处理next指针

 

 

第一种解法代码:

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val, next, random):
        self.val = val
        self.next = next
        self.random = random
"""
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:
            return None
        record = {}
        tmp_head = Node(head.val, None, None)
        record[head] = tmp_head
        while head:
            if head not in record:
                head2 = Node(head.val, None, None)
                record[head] = head2
            else:
                head2 = record[head]
            if head.next:
                if head.next not in record:
                    head2_next = Node(head.next.val, None, None)
                    record[head.next] = head2_next
                else:
                    head2_next = record[head.next]
                head2.next = head2_next
            if head.random:
                if head.random not in record:
                    head2_random = Node(head.random.val, None, None)
                    record[head.random] = head2_random
                else:
                    head2_random = record[head.random]
                head2.random = head2_random
            head = head.next
        return tmp_head

 

第二种解法代码:

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val, next, random):
        self.val = val
        self.next = next
        self.random = random
"""
class Solution(object):
    def copyRandomList(self, head):
        """
        :type head: Node
        :rtype: Node
        """
        if not head:
            return None
        ptr = head
        while ptr:
            new_node = Node(ptr.val, None, None)
            new_node.next = ptr.next
            ptr.next = new_node
            ptr = new_node.next

        ptr = head
        while ptr:
            if ptr.random:
                ptr.next.random = ptr.random.next
            ptr = ptr.next.next

        ret_head = head.next
        ptr = head
        while ptr:
            old_node = ptr.next
            ptr.next = old_node.next
            old_node.next = old_node.next.next if old_node.next else None
            ptr = ptr.next

        return ret_head

 

7.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1

输出:tail connects to node index 1

解释:链表中有一个环,其尾部连接到第二个节点。

 

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/linked-list-cycle-ii

 

关键点和易错点:

(1)第一步应该判断出是否有环,可以采用快慢指针来找出

(2)根据快慢指针特性列出公式计算出如何找到环的开始点

 

 

很明显two_step走的路程肯定是one_step的两倍,所以可列出公式:

     2 * D(one_step) = D(two_step)

=   2 (F + a) = F + n(a+b) + a

=   F + a = n(a+b)

=   F = n(a+b) - (a+b) +b

=   F = (n-1)(a+b) + b

由于a+b是一个环路,所以如果从起点和相遇点同时走,这两个指针一定会在起始点相遇

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        one_step = head
        two_step = head
        ptr1 = head
        ptr2 = None
        while two_step:
            if two_step.next and two_step.next.next:
                two_step = two_step.next.next
            else:
                break
            one_step = one_step.next
            if one_step == two_step:
                ptr2 = two_step
                break

        if ptr2:
            while ptr1 != ptr2:
                ptr1 = ptr1.next
                ptr2 = ptr2.next
            return ptr1
        return None

 

8.对链表进行插入排序

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。

每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

 

插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。

每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。

重复直到所有输入数据插入完为止。

 

示例 1:

输入: 4->2->1->3

输出: 1->2->3->4

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/insertion-sort-list

 

关键点和易错点:

(1)这道题目看着很简单,但其实有几个是很难察觉到的,且不调试很难看出来,花了一个多钟才完成,复习时去重做一遍

(2)head是会变的,这里是个易错点,想当然的tmp_head = t_head.next写成了tmp_head = head,很难察觉

(3)中间插入过程中很容易忘记已排序的最后一个元素的指向应该改为指向在排的下一个元素,因为以前的插入排序是数组,所以没有考虑这个,换成指针这里很难察觉到,就错了,而且很难发现

(4)加上一个tail指针可以优化算法平均时间

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def insertionSortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return head
        t_head = ListNode(0)
        t_head.next = head
        cur = head.next
        tail = head
        while cur:
            cur_next = cur.next
            tmp_head = t_head.next
            pre = t_head
            if tail.val > cur.val:
                while tmp_head != cur and tmp_head.val <= cur.val:
                    pre = tmp_head
                    tmp_head = tmp_head.next
                if tmp_head != cur:
                    cur.next = pre.next
                    pre.next = cur
                    tail.next = cur_next
            else:
                tail = cur
            cur = cur_next
        return t_head.next 

 

9.编写一个程序,找到两个单链表相交的起始节点。

示例 1:

 

注意:

如果两个链表没有交点,返回 null.

在返回结果后,两个链表仍须保持原有的结构。

可假定整个链表结构中没有循环。

程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists

 

易错点和关键点:

(1)有点难想到可以根据路程来解决此题,解题思路是某条链表的走到尾后,再走另外一条链表头开始走,如果有相交的,则它们一定会相遇,因为路程相等,比如上面的图:4-1-8-4-5-5-0-1-8跟5-0-1-8-4-5-5-1-8这两者走的步数是一样的,都是走9步后到的,原理其实就是A+B=B+A

(2)要判断不相交,则可以用最后一个元素来判断,如果不相交最后一个元素肯定是不相同的

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        if not headA or not headB:
            return None 
        first_tail = None
        curA = headA
        curB = headB
        while True:
            if curA == curB:
                return curA
            if curA.next == None:
                if first_tail:
                    if first_tail != curA:
                        return None
                else:
                    first_tail = curA
                curA = headB
            else:
                curA = curA.next
            if curB.next == None:
                if first_tail:
                    if first_tail != curB:
                        return None
                else:
                    first_tail = curB
                curB = headA
            else:
                curB = curB.next

 

10.给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

 

示例 1:

输入: 1->2->3->4->5->NULL

输出: 1->3->5->2->4->NULL

 

示例 2:

输入: 2->1->3->5->6->4->7->NULL 

输出: 2->3->6->7->1->5->4->NULL

说明:

 

应当保持奇数节点和偶数节点的相对顺序。

链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/odd-even-linked-list

 

关键点和易错点:

(1)这道题本身难度很小,但是要把它写到优雅,这是个难题

(2)用当前偶数节点来作为while里的判断可以保证当前奇数节点一定不是None的,所以节省了很多繁琐判断逻辑

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def oddEvenList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head:
            return head
        even = head.next
        odd_cur = head
        even_cur = even
        while even_cur and even_cur.next:
            odd_cur.next = even_cur.next
            odd_cur = odd_cur.next
            even_cur.next = odd_cur.next
            even_cur = even_cur.next
        odd_cur.next = even
        return head

 

11.给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

如果输入链表不能修改该如何处理?换句话说,你不能对列表中的节点进行翻转。

 

示例:

输入: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)

输出: 7 -> 8 -> 0 -> 7

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/add-two-numbers-ii

 

关键点和易错点:

(1)这道题比较直观的想法是用递归,需要给比较短的那条链表前面插入几个0,使两者长度相同,然后递归回溯相加解决,因为题目说不能改变原始链表,所以要删除掉添加的,如果可以改变原始链表,其实翻转链表更简单

(2)其实可以使用双栈法实现,要注意的就是短的链表在到None时不要插入0填充,,这是我一开始犯的错

(3)往前面填充计算的单元,即头插法

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        stack_l1 = []
        stack_l2 = []
        while l1 or l2:
            if l1:
                stack_l1.append(l1.val)
                l1 = l1.next
            if l2:
                stack_l2.append(l2.val)
                l2 = l2.next

        carry_flag = 0
        tmp_head = ListNode(0)
        while len(stack_l1) > 0 or len(stack_l2) > 0:
            tmp1 = 0 if len(stack_l1) <= 0 else stack_l1.pop(-1)
            tmp2 = 0 if len(stack_l2) <= 0 else stack_l2.pop(-1)
            add_result = tmp1 + tmp2 + carry_flag
            new_node = ListNode(add_result%10)
            carry_flag = add_result / 10
            new_node.next = tmp_head.next
            tmp_head.next = new_node

        if carry_flag > 0:
            new_node = ListNode(carry_flag)
            new_node.next = tmp_head.next
            tmp_head.next = new_node

        return tmp_head.next

 

12.给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, ... 。

每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且  node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。

返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。

注意:在下面的示例中,诸如 [2,1,5] 这样的输入(不是输出)是链表的序列化表示,其头节点的值为 2,第二个节点值为 1,第三个节点值为 5 。

 

示例 1:

输入:[2,1,5]

输出:[5,5,0]

 

示例 2:

输入:[2,7,4,3,5]

输出:[7,0,5,5,0]

 

示例 3 

输入:[1,7,5,1,9,2,5,1]

输出:[7,9,9,9,0,5,0,0]

 

提示:

对于链表中的每个节点,1 <= node.val <= 10^9

给定列表的长度在 [0, 10000] 范围内

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/next-greater-node-in-linked-list

 

关键点和易错点:

(1)一看到这题目总让人感觉不简单的样子,然后就想到是不是可以使用归并排序思想来解决,但是链表其实做归并这种思路其实操作很不方便,而且复杂度是O(nlgn),我一开始就是卡死在这个思路了,正确的方法应该是放弃这个思路,寻找其它思路

(2)其实有点规律在这里面的,假设n-2,n-1都还没找到那个合适的值,已经说明了n-1肯定是比n-2小的,而且没有匹配到的都是这样递减的,是不是可以把匹配到的可以先抽出来,留下未匹配的,只要比较下最前面的未匹配的来决定是否要匹配之前的,这里其实就剪枝了,所以本题使用了一个栈来保存还未匹配的,此方法叫做单栈法

 

思维图解:

 

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def nextLargerNodes(self, head):
        """
        :type head: ListNode
        :rtype: List[int]
        """
        result = []
        stack = []
        cur = head
        while cur:
            result_index = len(result) - 1
            while len(stack) > 0 and stack[len(stack)-1] < cur.val:
                while result[result_index] != 0:
                    result_index -= 1
                result[result_index] = cur.val
                stack.pop(-1)
            result.append(0)
            stack.append(cur.val)
            cur = cur.next
        return result

 

13.给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。

请你返回该链表所表示数字的 十进制值 。

 

示例 1:

输入:head = [1,0,1]

输出:5

解释:二进制数 (101) 转化为十进制数 (5)

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer

 

关键点和易错点:

(1)这道题比较直观的是想到链表翻转来计算,但很难跟位运算联系起来(其实遇到0,1就应该往位运算方面去想才是正确的思路),用位运算完美解决此问题,所以很多算法有时就巧在会把知识融会贯通

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getDecimalValue(self, head):
        """
        :type head: ListNode
        :rtype: int
        """
        result = 0
        while head:
            result <<= 1
            result += head.val
            head = head.next
        return result

 

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

 

示例 1:

输入: 4->2->1->3

输出: 1->2->3->4

 

示例 2:

输入: -1->5->3->4->0

输出: -1->0->3->4->5

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/sort-list

 

关键点和易错点:

(1)看到题目要求的O(nlgn)的时间复杂度,我们就应该联想到快速排序、堆排序和归并排序等,快速排序只是平均时间复杂度是O(nlgn),最坏情况下是O(n²),用归并排序是比较方便且时间复杂度满足要求的,中间节点使用快慢指针来查找

(2)这里有个易错点是l1_head = self.sortList(head),我写成了l1_head = self.sortList(one_step),提交代码后错误了,看了n遍也没找到这个问题,这是非常容易错且不易发现的问题,传参时一定要谨慎再谨慎

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def sortList(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if not head or not head.next:
            return head
        
        one_step = head
        two_step = head.next
        while two_step and two_step.next:
            one_step = one_step.next
            two_step = two_step.next.next
        
        two_step = one_step.next
        one_step.next = None
        l1_head = self.sortList(head)
        l2_head = self.sortList(two_step)
        return self.merge(l1_head, l2_head)
        
    def merge(self, l1, l2):
        tmp_head = ListNode(0)
        cur = tmp_head
        while l1 and l2:
            if l1.val <= l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next
            cur = cur.next
        if l1:
            cur.next = l1
        else:
            cur.next = l2
        return tmp_head.next

 

15.给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。

删除完毕后,请你返回最终结果链表的头节点。

你可以返回任何满足题目要求的答案。

(注意,下面示例中的所有序列,都是对 ListNode 对象序列化的表示。)

 

示例 :

输入:head = [1,2,3,-3,-2]

输出:[1]

 

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list

 

易错点和关键点:

(1)一时很难想到可以使用前缀和来判断

(2)用hashmap来记录

(3)下面代码的while循环那里while delete_node is not head这里一开始想错了,这个是不用del的;还有del时需要判断下该值是否在hash中不然del直接抛异常了

 

提交代码:

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def removeZeroSumSublists(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        tmp_head = ListNode(0)
        tmp_head.next = head
        sum_hash = {}
        sum_hash[0] = tmp_head
        pre_sum = 0
        while head:
            total_sum = pre_sum + head.val
            pre_sum = total_sum
            if total_sum in sum_hash:
                tmp = sum_hash[total_sum]
                delete_node = tmp.next
                tmp.next = head.next
                while delete_node is not head:
                    total_sum += delete_node.val
                    if total_sum in sum_hash:
                        del sum_hash[total_sum]
                    delete_node = delete_node.next
                head = tmp.next
            else:
                sum_hash[total_sum] = head
                head = head.next
        return tmp_head.next

 

16.设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

 

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2); //链表变为1-> 2-> 3
linkedList.get(1); //返回2
linkedList.deleteAtIndex(1); //现在链表是1-> 3
linkedList.get(1); //返回3

 

提示:

所有val值都在 [1, 1000] 之内。
操作次数将在 [1, 1000] 之内。
请不要使用内置的 LinkedList 库。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/design-linked-list

 

提交代码(双链表实现):

class ListNode(object):

    def __init__(self, val):
        """
        Initialize ListNode
        """
        
        self.val = val
        self.pre = None
        self.next = None
    
class MyLinkedList(object):

    def __init__(self):
        """
        Initialize your data structure here.
        """
        
        self.list_head = ListNode(0)
        self.list_len = 0
        
    def get_index_node(self, index):
        """
        Get the index-th node in the linked list, If the index is invaild, return None
        :type index: int
        :rtype: ListNode
        """
        
        if index >= self.list_len or index < 0:
            return None
            
        cur = self.list_head.next
        while index > 0:
            index -= 1
            cur = cur.next
        return cur
     
    def get(self, index):
        """
        Get the value of the index-th node in the linked list. If the index is invalid, return -1.
        :type index: int
        :rtype: int
        """
        
        node = self.get_index_node(index)
        if node:
            return node.val
        else:
            return -1
        

    def addAtHead(self, val):
        """
        Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
        :type val: int
        :rtype: None
        """
        
        tmp = ListNode(val)
        if self.list_head.next:
            tmp.next = self.list_head.next
            self.list_head.next.pre = tmp
        else:
            self.list_head.pre = tmp
            tmp.next = self.list_head
        self.list_head.next = tmp
        tmp.pre = self.list_head
        self.list_len += 1

    def addAtTail(self, val):
        """
        Append a node of value val to the last element of the linked list.
        :type val: int
        :rtype: None
        """
        
        tmp = ListNode(val)
        if self.list_head.next:
            last_node = self.list_head.pre
            last_node.next = tmp
            tmp.pre = last_node
            tmp.next = self.list_head
            self.list_head.pre = tmp
            self.list_len += 1
        else:
            self.addAtHead(val)
        

    def addAtIndex(self, index, val):
        """
        Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
        :type index: int
        :type val: int
        :rtype: None
        """
        
        if index <= 0:
            self.addAtHead(val)
        elif index == self.list_len:
            self.addAtTail(val)
        else:
            pre_node = self.get_index_node(index-1)
            if pre_node:
                tmp = ListNode(val)
                tmp.next = pre_node.next
                pre_node.next.pre = tmp
                pre_node.next = tmp
                tmp.pre = pre_node
                self.list_len += 1

                
    def deleteAtIndex(self, index):
        """
        Delete the index-th node in the linked list, if the index is valid.
        :type index: int
        :rtype: None
        """
        
        node = self.get_index_node(index)
        if not node:
            return None
            
        if node.next is self.list_head and node.pre is self.list_head:
            self.list_head.next = None
            self.list_head.pre = None
        elif node.next is self.list_head:
            node.pre.next = self.list_head
            self.list_head.pre = node.pre
        else:
            node.pre.next = node.next
            node.next.pre = node.pre
        self.list_len -= 1
posted @ 2019-12-19 19:20  luohaixian  阅读(561)  评论(0编辑  收藏  举报