python-链表练习2,赶紧上车练练

一、合并两个已经排好序的链表

有两个链表,已经排好顺序,在不使用额外空间的前提下,也就是在两个链表本身去合并,应该如何做?

在这里插入图片描述
首先,准备两个pointer,一个是L1一个是L2,两者的value进行一个比较,如果L1<L2,那么就L1.next指向L2,这样就完成了吗?这样就错了,当我们把L1.next不加以处理就直接指向L2的话,L1的链表后面就内容我们就找不到了,好,既然这样,能不能先将L1 = L1.next呢?那第一个节点不就找不到了吗?
说来说去,这也不行那也不行,那怎么办呢?
准备一个pre用来存储L1,pre = L1,然后用L1 = pre.next向后遍历,如果L1<L2 ,pre.next = L2,这不就搞定了吗,
在这里插入图片描述
思路有了之后,上代码,链表的创建是用到了之前手动创建链表的文章当中的链表类,创建出一些符合要求的链表,再此基础上进行合并

from create_a_LinkedList import LinkedList
from create_a_LinkedList import Node
# 时间复杂度 O(m + n)
def merge_two_sortedLinkedList(l1, l2):
	dummy = cur = Node(0)

	while l1 and l2:
		if l1.value < l2.value:
			cur.next = l1
			l1 = l1.next
		else:
			cur.next = l2
			l2 = l2.next
		# 指向了链表当中的节点之后,cur也得向后
		cur = cur.next
	# 当l1或者l2有一方结束了
	cur.next = l1 or l2

	return dummy.next


# 创建L1链表
n11 = Node(1)
n12 = Node(3)
n13 = Node(5)
n14 = Node(9)
n11.next, n12.next, n13.next = n12, n13, n14

# 创建L2链表
n21 = Node(2)
n22 = Node(4)
n23 = Node(6)
n24 = Node(10)
n25 = Node(15)
n21.next, n22.next, n23.next, n24.next = n22, n23, n24, n25

res = merge_two_sortedLinkedList(n11, n21)
L = LinkedList()
L.head.next = res
L.print_linkedList()

在这里插入图片描述
通过创建额外的节点进行链表节点的存储,并实时向后遍历,不断更新next的指向,这样就完成了链表的合并了。
有没有更好的思路呢?
我们可以用递归去完成,递归的思路在之前的文章当中有提到过,在做递归的时候一定要有一个base条件,来作为最后的收尾,那base条件也很好去设置,我们设置的链表的长度不一定都是一样的,所以当两个链表中有一个为空了,那就要把剩下的节点接在后面
遍历的时候判断两个链表的value,如果L1.value < L2.value 那么就把当前的节点返回,next指向剩下的合并的结果

在这里插入图片描述

# 使用递归合并两个链表
# 时间复杂度O(m + n)
def recursive_merge_LinkedList(l1, l2):
	# base 条件, 当有一个链表走完之后,另一个链表不为空,那就把剩下的节点直接统一接过去
	if not l1 or not l2:
		return l1 or l2
	if l1.value < l2.value:
		l1.next = recursive_merge_LinkedList(l1.next , l2)
		return l1
	else :
		l2.next = recursive_merge_LinkedList(l1, l2.next)
		return l2

# 创建L1链表
n11 = Node(1)
n12 = Node(3)
n13 = Node(5)
n14 = Node(7)
n11.next, n12.next, n13.next = n12, n13, n14

# 创建L2链表
n21 = Node(2)
n22 = Node(4)
n23 = Node(6)
n24 = Node(10)
n25 = Node(15)
n21.next, n22.next, n23.next, n24.next = n22, n23, n24, n25

res = recursive_merge_LinkedList(n11, n21)
L = LinkedList()
L.head.next = res
L.print_linkedList()

在这里插入图片描述


二、找到两个长度不一的链表中的共同使用的开始节点

类似于下图所示的内容
在这里插入图片描述
第一种思路是利用链表的长度:首先链表A,B的长度不一样,但是他们共同走过的节点数是一样的。以上图的题目为例,A链表的长度是5,B链表的长度为4,A比B多一个,那么准备两个指针,一个是a_pointer,一个是b_pointer,让a_pointer先走一步之后b_pointer在走,两个指针的速度是一样的,那么他们相遇的时候就找到了共同的开始节点

上代码

# 找到两个长度不一的链表中的共同使用的开始节点
def find_the_begin(head1, head2):
	l1, l2 = head1, head2
	len_l1 = 0
	len_l2 = 0
	while l1 is not None:
		len_l1 += 1
		l1 = l1.next

	while l2 is not None:
		len_l2 += 1
		l2 = l2.next
	# 链表遍历完成之后将指针再挪到各自链表开头
	l1, l2 = head1, head2
	if len_l2 > len_l1:
		for i in range(len_l2 - len_l1):
			l2 = l2.next
	elif len_l1 > len_l2:
		for i in range(len_l1 - len_l2):
			l1 = l1.next
	while l1 != l2:
		l1 = l1.next
		l2 = l2.next
	print('a: %s\nb: %s'%(len_l1, len_l2))
	return l2.value

# 创建a链表
a1 = Node(1)
a2 = Node(4)
a3 = Node(10)
# 创建b链表
b1 = Node(8)
b2 = Node(7)
# 创建c链表
c1 = Node(20)
c2 = Node(40)
a1.next, a2.next, a3.next, c1.next = a2, a3, c1, c2
b1.next, b2.next, c1.next = b2, c1, c2
res = find_the_begin(a1, b1)
print(res)

在这里插入图片描述
解决之后问题又来了,如果不能使用长度呢?
那么就直接遍历吧,
首先,在A链表中a指针从头走到尾,然后让a指针从B链表开始走到尾,B链表的b指针也是同样的操作,第一个走完之后,b指针挪到A链表开始移动,二者相遇之后,使用的共同开始节点就找到了,第一趟的时候是不会相遇的,在第一趟完成之后,第二趟完成之前,相遇。节点找到 了
在这里插入图片描述
上代码:

# 不用长度找到共同使用的开始节点
def find_begin(l1, l2):
	if l1 and l2:
		a, b = l1, l2
		while a != b :
			# 指针不断向后,走完之后将指针移动到两个链表开头
			a = a.next if a else l2
			b = b.next if b else l1
		return a.value
# 创建a链表
a1 = Node(1)
a2 = Node(4)
a3 = Node(10)
# 创建b链表
b1 = Node(8)
b2 = Node(7)
# 创建c链表
c1 = Node(20)
c2 = Node(40)
a1.next, a2.next, a3.next, c1.next = a2, a3, c1, c2
b1.next, b2.next, c1.next = b2, c1, c2
res = find_begin(a1, b1)
print('共同开始的节点:%s'%res)

在这里插入图片描述

三、链表的插入排序

给链表进行插入排序:
列表的插入排序是比较简单的,把要插入的值和之前的值一一进行比较,如果要插入的值小,那就依次向前交换即可。
在这里插入图片描述

但是链表的交换要复杂:

首先,我们要准备一个dummyNode,preNode,currentNode和temp用来为交换做准备,dummyNode用来返回最后排序好的链表的所有结果,preNode是用来指向链表的头部的,因为每次进行插入排序的时候,之前的链表节点是已经排序好了的,当currentNode是要小于preNode时,将currentNode的next指向pre的next,pre的next要指向currentNode,那如果currentNode>preNode的时候preNode会后遍历,然后与currentNode再次作比较,直到比较出结果,交换完毕之后,currentNode向后遍历,为了保证节点不会丢失,交换之后,要准备一个temp用来临时存储节点,也就是将currentNode.next给到temp,最后交换完毕,currentNode的内容为temp.
最后全部做完之后,返回dummy的next即可

在这里插入图片描述
上代码

# 时间复杂度——O(n^2)
def insertionSort_of_LinkedList(head):
	# 创建哨兵节点
	dummyNode = Node(0)
	currentNode = head

	while currentNode is not None:
		preNode = dummyNode  # 创建preNode节点
		# 如果currentNode的值 > preNode.next的值,说明不发生交换,preNode向下一个遍历
		while preNode.next is not None and preNode.next.value < currentNode.value:
			preNode = preNode.next
		# 开始做交换
		temp = currentNode.next
		currentNode.next = preNode.next
		preNode.next = currentNode
		currentNode = temp

	return dummyNode.next

n1 = Node(5)
n2 = Node(8)
n3 = Node(4)
n4 = Node(1)
n5 = Node(7)
n1.next, n2.next, n3.next, n4.next = n2, n3, n4, n5
L1 = LinkedList()
L1.head.next = n1
print('插入排序之前:', end = ' ')
L1.print_linkedList()
res = insertionSort_of_LinkedList(n1)
L1.head.next = res
print('插入排序之后:', end = ' ')
L1.print_linkedList()

在这里插入图片描述

四、链表的排序

上一题当中的时间复杂度是在O(n^2)的,那么现在我们想要降低时间复杂度,写出一个O(nlogn)的,那么有什么思路呢?可以使用分治法,然后再将链表合并
在这里插入图片描述
分治法将链表分开之后进行排序,排序完成之后进行合并

def Sort_LinkedList(node):
	if node is None or node.next is None:
		return node
	middle = get_middle_pos(node)
	l_list = node  # 左侧链表
	r_list = middle.next  # 右侧链表
	middle.next = None  # 左侧链表末尾节点指向None
	return merge(Sort_LinkedList(l_list), Sort_LinkedList(r_list))

def merge(l_list, r_list):
    dummyNode = dummyHead = Node(0)
    while l_list and r_list:
        if l_list.value < r_list.value:
            dummyHead.next = l_list
            l_list = l_list.next  # 指向左侧链表节点下一个
        else:
            dummyHead.next = r_list
            r_list = r_list.next  # 指向右侧链表节点下一个
        dummyHead = dummyHead.next
    if l_list:
        dummyHead.next = l_list
    elif r_list:
        dummyHead.next = r_list
    return dummyNode.next
    
def get_middle_pos(node):
    if node is None:
        return node
    fast = node
    slow = node
    while fast.next and fast.next.next:
        slow = slow.next
        fast = fast.next.next
    return slow

n1 = Node(5)
n2 = Node(8)
n3 = Node(4)
n4 = Node(1)
n5 = Node(7)
n1.next, n2.next, n3.next, n4.next = n2, n3, n4, n5
l1 = LinkedList()
l1.head.next = n1
print('排序之前:', end = ' ')
l1.print_linkedList()
res = Sort_LinkedList(n1)
L1 = LinkedList()

L1.head.next = res
print('排序之后:', end = ' ')
L1.print_linkedList()

在这里插入图片描述

五、反转链表

一个列表进行逆序输出是非常简单的,如下代码

num = [3, 5, 6, 4, 1, 2]
print(num[::-1])

在这里插入图片描述
那如果给一个链表呢?由情况1,转换成情况2
在这里插入图片描述

可以准备三个指针,pre_pointercurrent_pointernext_pointer
pre_pointer可以作为一个哨兵节点,current_pointer是指向的当前节点,next_pointer是指向的current_pointer的next节点,当节点创建完毕时,开始反转。
current_pointer的next指向pre_pointer,pre_pointer更新成为current_pointer,next_pointer更新成为current_pointer的next,依次向后遍历,直到current_pointer为空时,pre_pointer正好指向链表当中最后一个节点,然后输出pre_pointer即可在这里插入图片描述
上代码:

from create_a_LinkedList import LinkedList, Node

def reverse_LinkedList(node):
	preNode = None
	currentNode = node  # 当前节点
	nextNode = None

	while currentNode != None:
		nextNode = currentNode.next  # nextNode,向后
		currentNode.next = preNode  # currentNode指向preNode
		preNode = currentNode
		currentNode = nextNode

	return preNode
n1 = Node(1)
n2 = Node(3)
n3 = Node(5)
n4 = Node(7)
n1.next, n2.next, n3.next = n2, n3, n4
res = reverse_LinkedList(n1)
L1 = LinkedList()
L1.head.next = res
L1.print_linkedList()

在这里插入图片描述

六、分治链表

将快排的对象从列表改成链表
给你一个节点的值,该节点左侧都是小于该节点的值,右侧都是大于该节点的值
在做列表的快排的时候我们是准备一个pivot,列表头准备一个指针i,列表尾部准备一个指针j,i指针从左向右去遍历,如果遇到比pivot的值要大的元素,i指针停止,j指针从右向左去遍历,如果遇到了比pivot要小的元素,j指针则停止,此时交换i,j指针对应的元素,如果i,j的指针指向了同一个元素, 则将这个元素和pivot进行交换
在这里插入图片描述
当然了,列表的遍历是很方便的,如果链表是双向链表的话也会好一些,但是目前我们要做的是单链表,那就不太方便了,我们需要转换思路了
准备两个节点,LNode和RNode,当遍历到的节点比设置的节点值要小,那LNode.next直接指向该节点,如果比设置的节点的值要大,RNode.next指向该节点,最后整合的时候RNode节点指向None,LNode.next指向RNode.next连起来就可以了
在这里插入图片描述

def partition_LinkedList(node, x):
    # 先创建左右单独节点
    LNode = Node(0)
    RNode = Node(0)
    left = LNode
    right = RNode
    while node:
        if node.value < x:
            left.next = node
            left = left.next
        else:
            right.next = node
            right = right.next
        # 指向结束之后node接着向后
        node = node.next
    # 排序完成之后
    right.next = Node # 设置右侧链表末尾指向None
    left.next = RNode.next
    return LNode.next

n1 = Node(3)
n2 = Node(5)
n3 = Node(1)
n4 = Node(7)
n5 = Node(4)
n1.next, n2.next, n3.next, n4.next = n2, n3, n4, n5
L1 = LinkedList()
res = partition_LinkedList(n1, 3)
L1.head.next = res
L1.print_linkedList()

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值