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