所有例题的编程语言为python
单链表双链表的相同点:
1.无法在常量时间内随意访问数据
2.能够在o(1)时间内,在给定节点之后完成新节点的添加
3.能够在o(1)的时间内,删除链表的第一节点
区别:删除给定节点
单链表无法由给定节点获取前一个节点,因此在删除给定节点之前必须花费o(n)的时间来找出前一个节点
双链表中,可以使用“prev”字段获取前一个节点,因此能在o(1)的时间内=删除给定节点。
链表的主要优势:删除添加节点十分方便
leetcode 21
将两个升序链表合并为一个新的升序链表并返回。
# 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==None or l2==None:
return l1 if l1 else l2
res_head=ListNode(0)
res_curr_node=res_head
while(l1 and l2):
if l1.val<=l2.val:
l1_next_node=l1.next
l1.next=None
res_curr_node.next=l1
l1=l1_next_node
res_curr_node=res_curr_node.next
else:
l2_next_node=l2.next
l2.next=None
res_curr_node.next=l2
l2=l2_next_node
res_curr_node=res_curr_node.next
if l1:
res_curr_node.next=l1
if l2:
res_curr_node.next=l2
return res_head.next
精简写法:
class Solution(object):
def mergeTwoLists(self, l1, l2):
if l1==None or l2==None:
return l1 if l1 else l2
res_head=ListNode(0)
res_curr_node=res_head
while(l1 and l2):
if l1.val<=l2.val:
res_curr_node.next=l1
l1=l1.next
else:
res_curr_node.next=l2
l2=l2.next
res_curr_node=res_curr_node.next
res_curr_node.next=l1 if l1 else l2
return res_head.next
递归最重要的是递归出口
class Solution(object):
def mergeTwoLists(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
if l1==None:
return l2
elif l2==None:
return l1
elif l1.val<=l2.val:
l1.next=self.mergeTwoLists(l1.next,l2)
return l1 # 由顶向下时,已经决定了第一个节点是谁,递归得到最底端时,直接将长链表的剩余部分链接回已经排序好的链表中。
else:
l2.next=self.mergeTwoLists(l1,l2.next)
return l2
给出两个 非空的链表 来表示两个 非负整数。其中,他们各自的位数是按照 逆序 方式存储的,并且每个节点只能存储一位 数字。
如果,我们将这两个数相加起来,返回一个新的链表表示它们的和
可以假设除了数字0之外,两个数字都不会以0开头
思路:逆序存储简化了问题,直接遍历两个链表的对位元素,相加后维护一个进位标志flag。
冗余
# 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
"""
flag=0
res_head=ListNode(0)
res_cur_node=res_head
while(l1 and l2):
sum_num=l1.val+l2.val+flag
flag=sum_num//10
res_cur_node.next=ListNode(sum_num%10)
res_cur_node=res_cur_node.next
l1=l1.next
l2=l2.next
while(l1):
#print(l1.val)
sum_num=l1.val+flag
flag=sum_num//10
res_cur_node.next=ListNode(sum_num%10)
res_cur_node=res_cur_node.next
l1=l1.next
while(l2):
#print(l2.val)
sum_num=l2.val+flag
flag=sum_num//10
res_cur_node.next=ListNode(sum_num%10)
res_cur_node=res_cur_node.next
l2=l2.next
if flag:
res_cur_node.next=ListNode(flag)
return res_head.next
精简
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
carry=0
res_head=ListNode(0)
res_cur_node=res_head
while(l1 or l2 or carry):
if l1:
carry+=l1.val
l1=l1.next
if l2:
carry+=l2.val
l2=l2.next
carry,val=divmod(carry,10)
res_cur_node.next=ListNode(val)
res_cur_node=res_cur_node.next
return res_head.next
多级双向链表中,除了指向下一个节点和前一个节点的指针外,它还有一个子链表指针,可能指单独的双向链表。这些子链表也可能有一个或多个自己的子项。一次类推,生成多级数据结构 。
给出位于链表第一级的头节点,请扁平化链表,使所有的节点出现子啊单级双链表中。
直觉:采用递归的方式求解,可每个节点该如何处理呢:
新建一个res_head,res_head下一个节点应该为curr_node.child,curr_node.next,
官方思路递归:扁平化处理可以看作对二叉树进行先序遍历,child作为二叉树中的指向座子树的left指针,next可以看作二叉树中的right指针。
难点:深度优先遍历到了末尾该如何让扁平化操作?
flatten_dfs(prev, curr)接收两个指针作为函数参数,并返回扁平化链表中的尾部指针。curr指向需要扁平化的子列表,prev指向curr元素的前一个元素。
在dfs函数中首先要建立prev和curr的双向链接
然后对左子树进行操作dfs(curr,cuur.child)其将返回扁平化子列表的尾部元素,再调用dfs(tail,curr.next)对右子树进行操作。注意点:
1.在进行左子树操作时,需要先保存curr.next的信息
2.在扁平化child指针所指向的列表之后,应该删除child指针
"""
# Definition for a Node.
class Node(object):
def __init__(self, val, prev, next, child):
self.val = val
self.prev = prev
self.next = next
self.child = child
"""
class Solution(object):
def flatten(self, head):
"""
:type head: Node
:rtype: Node
"""
if head==None:
return head
dummy_head=Node(None,None,head,None)
self.flatten_dfs(dummy_head,head)
dummy_head.next.prev=None
return dummy_head.next
def flatten_dfs(self,prev,curr):
if curr==None:
return prev
curr.prev=prev
prev.next=curr
tempNext=curr.next
tail=self.flatten_dfs(curr,curr.child)
curr.child=None
return self.flatten_dfs(tail,tempNext)
官方思路迭代:
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点
编程实现这个链表的深度拷贝
难点:新建节点,很简单,随机节点该如何指向?
先遍历一遍形成一个单链表,再遍历一遍原链表找到带随机指针的节点,在单链表中找到对应的节点,难的是对应节点怎么找,因为,指针存的是地址,在新链表中并不知道该指向哪一个?解决方案–维护一张map映射表,每个节点对应的新节点,因此便可以找到对应的节点。
"""
# Definition for a Node.
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution(object):
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
if head==None:
return None
visitedHash={}
res_head=Node(0)
res_cur_node=res_head
cur_node=head
while(cur_node):
node=Node(cur_node.val,None,None)
res_cur_node.next=node
visitedHash[cur_node]=node
res_cur_node=res_cur_node.next
cur_node=cur_node.next
cur_node=head
res_cur_node=res_head.next
while(cur_node):
if cur_node.random:
node=visitedHash[cur_node.random]
res_cur_node.random=node
cur_node=cur_node.next
res_cur_node=res_cur_node.next
return res_head.next
参考官网给题解:递归
带随机指针的链表可以看作一张图,要做的是遍历整张图,并拷贝它。拷贝的意思是每当遇到一个新的未访问过的节点,需创造新的节点。在回溯的过程中记录访问过的节点,否则因为随机指针存在,会导致死循环。
"""
# Definition for a Node.
class Node:
def __init__(self, x, next=None, random=None):
self.val = int(x)
self.next = next
self.random = random
"""
class Solution(object):
def __init__(self):
self.visitedHash={}
def copyRandomList(self, head):
"""
:type head: Node
:rtype: Node
"""
if head==None:
return None
if head in self.visitedHash:
return self.visitedHash[head]
node=Node(head.val,None,None)
self.visitedHash[head]=node
node.next=self.copyRandomList(head.next)
node.random=self.copyRandomList(head.random)
return node
给定一个链表,将链表的每个节点向右移动k个位置,其中k是非负数。
思路1:和旋转数组一样,每次右移一位,移动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 head==None or head.next==None:
return head
l=0
cur_node=head
while(cur_node):
l+=1
cur_node=cur_node.next
k=k%l
def rotate_one_step(head):
pre_pre_node=None
pre_node=head
cur_node=head.next
while(cur_node):
pre_pre_node=pre_node
pre_node=cur_node
cur_node=cur_node.next
pre_pre_node.next=None
pre_node.next=head
return pre_node
while(k>0):
head=rotate_one_step(head)
k-=1
return head
思路2: 移动k次,就是将倒数的k个节点相对顺序不变的移动到链表的头部。所以从链表的head开始,往后找到L-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 head==None or head.next==None:
return head
l=0
cur_node=head
while(cur_node):
l+=1
cur_node=cur_node.next
k=k%l
# 首尾相连
pre_node=None
cur_node=head
while(cur_node):
pre_node=cur_node
cur_node=cur_node.next
pre_node.next=head
# 寻找切割点
cur_node=head
for _ in range(1,l-k): #[0,l-k-1]
cur_node=cur_node.next
# 确定的新的头部
new_head=cur_node.next
cur_node.next=None
return new_head