接下来我将逐步解决,双指针问题给链表排序,还有通过链表转列表然后冒泡排序后再转为链表
1.合并两个有序链表:
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
迭代法:
class Solution:
def mergeTwoLists(self,l1:ListNode,l2:ListNode)->ListNode:
prehead=ListNode(-1) #建立虚拟头部,之后不输出这个结点就行
prev=prehead #为什么不全程用prev就是因为,prev是可变的指针,prehead是固定的指针
while l1 and l2: #两个都有就循环比较
if l1.val<=l2.val: #谁小谁进,然后下一个接着比较
prev.next=l1
l1=l1.next
else:
prev.next=l2
l2=l2.next
prev=prev.next #if判断完,prev也到下一个了
prev.next l1 if not l2 else l2 #谁空剩下的就指另外一个就完事了
return prehead.next
逻辑如上面的代码,总之就是要理解这些值都是指针而不是值,所以他就是链表
为什么要去用虚拟头部,原因就是如果不用虚拟头部的话,就需要再判断这个链表的头具体是谁,然后再进循环,那我不如直接定义一个虚拟头部,然后最后不输出头,从第二个也就是prehead.next开始输出,下面就是如果不用虚拟头部这个题应该怎么写
class Solution:
def mergeTwoLists(self,l1:ListNode,l2:ListNode)->ListNode:
if not l1:
return l2
if not l2:
return l1
if l1.val<=l2.val:
head=l1
l1=l1.next
elif:
head=l2
l2=l2.next
current=head
while l1 and l2:
if l1.val<=l2.val:
current.next=l1
l1=l1.next
else:
current.next=l2
l2=l2.next
current=current.next
current.next=l1 if not l2 else l2
return head
可以看到其实第一部分和第二部分的处理是一样的,那我就没必要写两次,所以不如直接设定虚拟头部。
我之前有疑问,什么时候是单个值,什么时候是全链,在写的过程中,我好像意识到了问题所在,l1.val就是当下这个指针所指代的单个值,而如果去输出l1的话,就是所有的剩下的链,所以接下来如何去从某个节点开始输出,这是个问题。
所以每个链表,一定一定要记住头是谁,记不住头是谁就g了,不然根本回不去了。接下来我将写一个访问链表第三个的值的代码
class ListNode:
def __init__(self,val=0,next=None):
self.val=val
self.next=next
head=ListNode(1)
head.next=ListNode(5)
head.next.next=ListNode(4)
head.next.next.next=ListNode(3)
head.next.next.next.next=ListNode(2)
def get_third_value(head:ListNode):
current=head
for i in range(2): #第三个值是头的两个next,所以循环两次就找到了
if current and current.next:
current=current.next
else:
return None
return current.val
关于这个None,也就是说在循环的时候,要保证赋值的左右都是有值的,加入这个链表只有两个值,那么这时候就会出现,在第一次循环的时候,current.next就指向了None。那么第二次循环的时候,就进else了,那么这个时候就返回了None。那么判断的时候是允许currentNone,或者current.nextNone这样的表述存在的。
可以把链表想象成,链条,他去调用这个数值,就是cur.val。但是你去调用cur,他就是所有剩余值
2.给链表排序
这个是我想的一道题,我想如果我能给链表排序了,那么我对于链表的操控就会变得更加得心应手,那么我就问chat有没有办法去给链表排序,我自己想的是,我管他链表规则是啥,我对于列表的操控已经非常的得心应手了,我不知道排序的具体算法我知道sorted对吧,我直接强制转换成列表,然后调用sorted,然后再建立一个新链表,完事,那我就又有新的想法了,我不会给列表排序,我做过双指针移0操作,但是还没有做过真正的数字排序,都是用sorted直接用。待会我也会尝试学习排序算法。
1.归并排序(Merge Sort)使用快慢指针
class ListNode:
def __init__(self,val=0,next=None):
self.val=val
self.next=next
class Solution:
def sortList(self,head:ListNode)->ListNode:
if not head or not head.next:
return head
slow,fast=head,head.next
while fast and fast.next:
slow=slow.next
fast=fast.next.next
mid=slow.next
slow.next=None
left=self.sortList(head)
right=self.sortList(mid)
return self.mergeTwoList(left,right)
我现在理解什么是归并排序了,就是先递归,再合并,为什么让我去做合并两个有序链表的目的就在这了,其实说实话我现在特别能理解归并问题这种问题的思维了,他就是在不断地简化操作,我这么多可能性不好处理,那我就简化,再简化,简化到最后交换1和2这样子的任务,只不过如何简化,如何去把这个简化的活做成一个递归,让他具有普适性,是要去处理的事情。
代码逻辑如下:
1.如果没有头(空)或者没有下一个(就一个头),这两种情况直接return head,第一种return None,第二种return head
2.定义快慢针,起手慢slow定义为head,快fast定义为head.next,用while循环让快慢针都动起来,因为slow不容易碰到底,所以这里的while的限制条件并不需要去约束slow,所以只需要fast和fast.next有东西,就能动起来
3.slow=slow.next,走一个,fast=fast.next.next走两格这样操作完,fast就走完了整个链表,slow就找到了链表的中间
4.中间找到了,就可以用mid存起来,然后对slow.next=None这个操作就完美的把mid和head分开了,那么直接去分别重做切割,切割到最后最最最后一层直接return head,倒数第二层用这个return的head去做self.mergeTwoLists(left,right)
完事,完美,想出这个办法的人简直是天才。
3.通过给链表进行排序,反推给列表进行排序
from typing import List
class Solution:
def merge(self,list1:List,list2:List)->List:
i,j=0,0
mergelist=[]
n1=len(list1)
n2=len(list2)
while i<n1 and j<n2:
if list1[i]<list2[j]:
mergelist.append(list1[i])
i+=1
else:
mergelist.append(list2[j])
j+=1
mergelist.extend(list1[i:])
mergelist.extend(list2[j:])
return mergelist
def Sort(self,list1:List)->List:
left=[]
right=[]
if len(list1) <= 1:
return list1
n=len(list1)
for i in range (0,n//2):
left.append(list1[i])
for i in range(n//2,n):
right.append(list1[i])
left=self.Sort(left)
right=self.Sort(right)
return self.merge(left,right)
sol=Solution()
list_test=[1,5,6,3,4,5]
list_test=sol.Sort(list_test)
print(list_test)
1.代码逻辑同上,需要注意的是,给链表进行排序操作的时候,他会直接带上后面的值,所以在对列表进行操作的时候,也要注意是否需要带上后面的值,比如在mergelist.extend(list1[i:])和mergelist.extend(list2[j:])操作的时候,我就只加进去了一个量,原因是在链表里加一个就把后面的都加进去了,但是在列表里,不够。
2.另外要注意的是,如果对列表进行append操作的话,就会出现二维列表,用append只能对元素进行操作,这里的extend就非常的合适。
3.head and head.next不为空这里的操作,我想直接等效过来,变成list和list[0]不为空,这样是绝对不合适的,当时写的时候没想太多,现在问题出来了以后才发现这里的逻辑其实很糟糕,应该是list[0]和list[i]都在
4.另外需要注意,不要用list作为变量名
5.分割那里,可以用自带的函数,chat给的代码如下:
from typing import List
class Solution:
def merge(self, list1: List[int], list2: List[int]) -> List[int]:
i, j = 0, 0
merged = []
while i < len(list1) and j < len(list2):
if list1[i] < list2[j]:
merged.append(list1[i])
i += 1
else:
merged.append(list2[j])
j += 1
# 直接添加剩余的元素
merged.extend(list1[i:])
merged.extend(list2[j:])
return merged
def sortList(self, lst: List[int]) -> List[int]:
if len(lst) <= 1:
return lst
mid = len(lst) // 2
left = self.sortList(lst[:mid])
right = self.sortList(lst[mid:])
return self.merge(left, right)
# 示例使用
sol = Solution()
lst = [1, 5, 6, 3, 4, 5]
sorted_lst = sol.sortList(lst)
print(sorted_lst)
他在mid这里进行了优化,同时不需要定义限制条件n1和n2,这个修改并不影响可读性并且还更流畅。
4.冒泡排序
def bubble_sort(arr:List[int])->List[int]:
n=len(arr)
for i in range(n):
swapped = False
for j in range (0,n-i-1):
if arr[j]>arr[j+1]:
arr[j],arr[j+1]=arr[j+1],arr[j]
swapped=True
if not swapped:
break
return arr
arr = [64, 34, 25, 12, 22, 11, 90]
sorted_arr = bubble_sort(arr)
print(sorted_arr)
其实就是最朴素的想法,很简单,在n次的情况下,去做逐个交换,目标是把最大的那个泡泡推到最后面去,剩下的尽量的换一下,然后下一次循环的时候,就可以不用管那个刚才排序过的那个了,对j的限制里,-1的目的是为了让j+1存在,-i的目的是为了让已经排列过的就不用管了。至于循环停止的条件,那就是只要进行过交换那就继续。
5.快速排序
def quick_sort(arr: List[int]) -> List[int]:
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2] # 选择中间元素作为基准
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
# 递归排序左边和右边的子列表
return quick_sort(left) + middle + quick_sort(right)
# 示例使用
arr = [64, 34, 25, 12, 22, 11, 90]
sorted_arr = quick_sort(arr)
print(sorted_arr)
快排和归并排序的思路很像,都是通过递归来一步一步的排序,然后直到最后达到最极端的return条件从而停止,逻辑如下:
1.当列表元素为一个时直接返回这个列表,这是极端条件,也是递归的最后一步
2.pivot是基准,arr[len(arr)//2]是人为定的中点,不是最后答案中最中间的数字,但是在每个循环中,他用这个数字来分割前后
3.左小中等右大,分三个数列,然后左右再次进入快排,中间不变直接输出
4.注意[x for x in arr if x<pivot]这样的表述,以及列表加列表加列表等于列表这样的表述。
6.关于前面所说的链表的排序,也可以使用转换为列表后用sorted排序
class ListNode:
def __init__(self,val=0,next=None):
self.val=val
self.next=next
class Solution:
def sortList(self,head:ListNode)->ListNode:
lst=[]
current=head
while current:
lst.append(current.val)
current=current.next
lst=sorted(lst)
prehead=ListNode(-1)
current=prehead
for val in lst:
current.next=ListNode(val)
current=current.next
return prehead.next
没啥好说的,就是一个练习,双向遍历。
7.链表两数之和
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
from typing import Optional
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
ans=ListNode(-1)
head=ans
carry=0
while l1 and l2:
ans.next=ListNode((l1.val+l2.val+carry)%10)
carry=(l1.val+l2.val+carry)//10
ans=ans.next
l1=l1.next
l2=l2.next
while l1:
ans.next=ListNode((l1.val+carry)%10)
carry=(l1.val+carry)//10
ans=ans.next
l1=l1.next
while l2:
ans.next=ListNode((l2.val+carry)%10)
carry=(l2.val+carry)//10
ans=ans.next
l2=l2.next
if carry!=0:
ans.next=ListNode(carry)
return head.next
这个代码是我写的,很丑很臭,但是能用,我将在明天写出来更合理的代码,这个代码要注意,ans.next不能直接接受int,ans应该也不行,明天我需要去找一下这个东西,今天是真没脑子了。