反转链表系列题

1.反转链表

反转链表有两种做法:迭代法和递归法,这里两种都写一下

1.1 迭代法

定义三个指针pPrev,pNode,pNext,分别指向当前节点的前一节点当前节点当前节点的后一节点,只要当前节点不为空,就不断地将当前节点的next指向前一节点,然后更新当前节点为下一节点,最后返回pPrev,因为退出条件为当前节点为空节点,故此时pPrev为最后一个节点。(PS:这个代码的前后相连真的是美)

def reverseLinkedListIter(head):
	pPrev = None
	pNode = head
	while pNode != None:
		pNext = pNode.next
		pNode.next = pPrev
		pPrev = pNode
		pNode = pNext
	return pPrev

时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( 1 ) O(1) O(1)

1.2 递归法

递归法的思想是,每次翻转当前节点后面的链表,然后把下一节点的next指向当前节点,直接看代码。

def reverseLinkedListRecursion(head):
	if head == None or head.next == None:
		return head
	newHead = reverseLinkedListRecursion(head.next)
	head.next.next = head
	head.next = None
	return newHead

这个代码的巧妙之处在于newHead一直没变,是最后一个节点。
时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( N ) O(N) O(N)

2.成对反转链表

题目描述:
在这里插入图片描述
乍一看这个题目,我有一种很粗暴的解法,就是把链表按单双数分解成两个链表,然后再重新合并,这种方法肯定是行得通的,此处讲另外两种方法,也分别是迭代法和递归法。

2.1 迭代法

思路也是定义三个指针

def reversePairIter(head):
	# 定义一个虚拟节点,方便处理
	dummy = ListNode(0)
	pPrev = dummy
	pNode = head
	while pNode != None and pNode.next != None:
		pNext = pNode.next
		pNode.next = pNext.next
		pNext.next = pNode
		pPrev.next = pNext
		pPrev = pNode
		pNode = pNode.next
	return dummy.next

下面用一张图来说明一下上面代码的第8~10行的过程,注意,这里1,2,3,4表示的是原先的顺序,其中pPrev是已经翻转过的,上面三个步骤分别用图中的带圈1,2,3表示,
在这里插入图片描述
经过交换后,现在的节点顺序如下:

在这里插入图片描述
可见,23对应的节点已经交换了顺序,接下来更新pPrevpNode的值即可继续后续的翻转。
时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( 1 ) O(1) O(1)

2.2 递归法

思路是定义三个指针,分别指向当前节点、下一节点和下下节点,首先将下一节点的next指向当前节点,然后将当前节点的next指向下下节点翻转后的返回值(递归),最后返回下一节点(此时下一节点变为头节点)。

def reversePairRecursion(head):
	if head == None or head.next == None:
		return head
	pNext = head.next
	pNextNext = pNext.next
	pNext.next = head
	head.next = reversePairRecursion(pNextNext)
	return pNext

时间复杂度 O ( N ) O(N) O(N),空间复杂度 O ( N ) O(N) O(N)

3. 反转链表的第m个至第n个节点

在这里插入图片描述
暂空。

4. K个一组反转链表

在这里插入图片描述
这个题目我的做法比较笨,先说自己的笨办法,这个办法的过程是这样的:

  1. 先获取链表的总长度N
  2. m=N//k,即有多少组子链表需要反转
  3. 对于整个链表,断成m组节点数为k的子链表,过程中记录各子链表的头节点,产生一个列表heads,并把各组的尾节点接一个None,对于剩下长度不够k的子链表,记下其头节点
  4. 再断开的同时(这一步也可以放后面做),对每个子链表进行反转,将反转后的头节点也记录下,产生一个列表newHeads
  5. 连接各段子链表
    连接子链表,原先的头为现在的尾,连接规则
        heads:     [p1 , p2 , p3 , p4 , p5]
                     |     |    |    |
    newHeads: [p1 , p2 , p3 , p4 , p5]
    newHeads中的p1为最终反转后的链表的头节点,heads中的p5的下一个节点为剩余的长度不够k的子链表的前序节点

代码敬上:

def reverseKGroupIter(head,k):
	def reversedLinkedList(head):
	    pPrev = None
	    pNode = head
	    while pNode != None:
	        pNext = pNode.next
	        pNode.next = pPrev
	        pPrev = pNode
	        pNode = pNext
		return pPrev
        
	if head == None or head.next == None:
	    return head
	# 获取链表的长度
	N = 0
	pNode = head
	while pNode != None:
	    pNode = pNode.next
	    N += 1
	if N<k:
	    return head
	# m组需要反转的子链表
	m = N // k
	heads = []
	newHeads = []
	pNode = head
	# 断开链表为 m + 1 个子链表,记录前面m个头节点,最后剩余的长度不够k的子链表的头节点为pNode
	for i in range(m):
	    heads.append(pNode)
	    for j in range(k):
	        # 若 j == k-1,此时pNode指向子链表的第k个节点,记录它,然后让它的下一个节点为None
	        if j == k - 1:
	            pCurrent = pNode
	            # 下面两步的顺序不能反,因为此时pcurrent==pNode,若先执行后一步,pNode就丢失了链表后面的节点
	            # 也正是这个原因,我们必须写else
	            pNode = pNode.next
	            pCurrent.next = None
	        else:
	            pNode = pNode.next
	    # 记录翻转后的新子链表的头部
	    newHeads.append(reversedLinkedList(heads[i]))
	# 连接链表节点
	for i in range(len(heads)-1):
	    heads[i].next = newHeads[i+1]
	heads[-1].next = pNode
	return newHeads[0]

再说一种递归的方法,这个方法看的是leetcode里的题解,递归方法的思路其实和上面的意思差不多,但是不需要拆开链表,大致过程是:

  1. 定义一个列表,用于保存需要反转的k个节点
  2. 往列表里塞节点,塞满k个为止,若塞不满k个,则直接返回当前的head
  3. 对于列表里的k个节点,从后往前,每个节点的next指向其前一个节点
  4. 对于列表里的第1个节点,其next为后续链表反转后的结果,故可递归调用
  5. 返回值为列表的最后一个节点

代码:

def reverseKGroupRecursion(head,k):
	cnt = k
	pNode = head
	l = []
	while cnt > 0:
		if pNode != None:
			l.append(pNode)
			pNode = pNode.next
		else:
			return head
		cnt -= 1
	cnt = k - 1
	while cnt > 0:
		l[cnt].next = l[cnt-1]
		cnt -= 1
	# 此时pNode为下一段的头节点
	l[0].next = reverseKGroupRecursion(pNode,k)
	return l[-1]

上面所有代码都通过了leet-code对应的题目。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值