记一道字节跳动的算法面试题

题目来源:csdn博主帅地,原文链接:https://blog.csdn.net/m0_37907797/article/details/103063236

网上已有java的实现版本,其实原理类似,通过自己的学习和理解,现提供C++版本供大家参考(指正),希望对即将面试或准备换工作的你,提供一点点帮助。

题目

这其实是一道变形的链表反转题,大致描述如下:

给定一个单链表的头节点 head,实现一个调整单链表的函数,使得每K个节点之间为一组进行逆序,并且从链表的尾部开始组起,头部剩余节点数量不够一组的不需要逆序。(不能使用队列或者栈作为辅助) 例如: 链表:1->2->3->4->5->6->7->8->null, K = 3。那么 6->7->8,3->4->5,1->2各位一组。调整后:1->2->5->4->3->8->7->6->null。其中 1,2不调整,因为不够一组。

分析

这道题咋一看,挺简单的,就是单链表分段逆序问题。正常情况下,遍历链表都是从头结点开始,但题中指定从尾部开始分组,而单链表是没法反向遍历的。所以,把此题分解成几个子问题,理解起来就简单了,分解后如下:

  • 对单链表逆序,由尾部——头部遍历;
  • 正向分段逆序;
  • 对逆序结果再次逆序,还原原始顺序;

这里先实现2个功能作为备用:链表逆序和分段逆序。

链表逆序

这里用递归来实现,应用求解子问题的思路,来拆分实现。
(1)拆分链表。把f(n)拆分成f(n-1) + 1
在这里插入图片描述
(2)求解f(n-1)。
在这里插入图片描述
(3)将结点1链接道f(n-1)尾部。
在这里插入图片描述
用代码实现为:

// 逆序单链表
Node* InverseTab(Node *pHead)
{
	if (pHead->next == NULL) return pHead;

	Node *pNewHead = InverseTab(pHead->next);

	// 把头部链到表尾,并将next置空
	pHead->next->next = pHead;
	pHead->next = NULL;

	return pNewHead;
}

分段逆序

分段的过程,可以顺序遍历求解,也可以应用递归求解,前者属于常规方法,再次不再赘述。以下描述的是递归方式:
(1)将链接拆分为2部分。一段长度为m的链表和一段长度为n-m的链表。
在这里插入图片描述
(2)前半段直接调用InverseTab,后半段按子问题求解。
在这里插入图片描述在这里插入图片描述
用代码实现为:

// 从头部开始,分段逆序
Node* SectionInverseFromHead(int cLen, Node *pHead)
{
	if (cLen <= 1 || pHead == NULL)	return pHead;

	Node *pcHead = NULL;	// 子链表的头部
	Node *pTemp = pHead;
	for (int i = 1; i < cLen; i++)
	{
		if (pTemp->next == NULL)
		{
			// cLen大于表长,不作逆序
			return pHead;
		}

		pTemp = pTemp->next;
	}

	// 后半段逆序
	pcHead = pTemp->next;
	pcHead = SectionInverseFromHead(cLen, pcHead);

	// 前半段逆序
	pTemp->next = NULL;
	Node *pNewHead = InverseTab(pHead);

	// 连起来
	pHead->next = pcHead;

	return pNewHead;
}

解决问题

回到原问题,可分解为:逆序 —— 分段逆序 —— 逆序。这样看,问题便迎刃而解了,直接上代码:

// 从尾部开始,分段逆序
Node* SectionInverseFromTail(int cLen, Node *pHead)
{
	pHead = InverseTab(pHead);
	pHead = SectionInverseFromHead(cLen, pHead);
	pHead = InverseTab(pHead);

	return pHead;
}
如果文章对您有帮助,记得点个赞哈!即是鼓励,也是提醒我坚持下去的动力~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值