2024.8.21 Python,链表排序,快慢针归并排序,冒泡排序,快速排序,链表两数之和

接下来我将逐步解决,双指针问题给链表排序,还有通过链表转列表然后冒泡排序后再转为链表

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
		elsereturn 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应该也不行,明天我需要去找一下这个东西,今天是真没脑子了。

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值