《剑指Offer》题二十一~题三十

二十一、调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

测试用例:

  • 功能测试:输入数组中的奇数、偶数交替出现;输入的数组中所有偶数都出现在奇数的前面;输入的数组中所有奇数都出现在偶数的前面。
  • 特殊输入测试:输入nullptr指针;输入的数组只包含一个数字。

只完成功能的解法:

void record_odd_before_even(int *pData, int length)
{
	if(pData == nullptr || length == 0)	return; 
	int *pBegin = pData;
	int *pEnd = pData + length - 1;
	while(pBegin < pEnd) {
		if(pBegin < pEnd && !is_even(*pBegin))
			pBegin++;
		if(pBegin < pEnd && is_even(*pEnd))
			pEnd--;
		if(pBegin < pEnd) {
			int temp = *pBegin;
			*pBegin = *pEnd;
			*pEnd = temp;
		}
	}
}

/* 判断n是否为偶数 */
bool is_even(int n)
{
	bool isEven = ((n & 0x1) == 0);
	return isEven;
}

分析:上段代码在扫描传入的数组时,如果发现有偶数出现在奇数的前面,则交换它们的顺序。所以,它维护两个指针,pBegin初始化时指向数组的第一个数字,它只向后移动;第二个指针初始化时指向数组的最后一个数字,它只向前移动。

二十二、链表中倒数第K个节点

题目:输入一个链表,输出该链表中倒数第K个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

分析:为了得到倒数第K个节点,很自然地想到先遍历链表,从而得到链表的节点数n,于是获悉倒数第K个节点就是从头节点开始的第n-k+1个节点。

遍历链表两次的解法:

ListNode* get_kth_from_tail(ListNode *pHead, unsigned int k)
{
	// 鲁棒性测试点1 
	if(pHead == nullptr || k == 0)	return nullptr;
	int cnt = 0;
	ListNode *pNode = pHead;
	while(pNode != nullptr) {		// 第一次遍历链表 
		pNode = pNode->next;
		++cnt;
	}
	// 鲁棒性测试点2 
	if(k > cnt)	return nullptr;
	pNode = pHead;
	while(cnt != k) {				// 第二次遍历链表 
		pNode = pNode->next;
		--cnt;
	}
	return pNode;
}

分析:如果面试官告诉我们只需遍历链表一次,我们应该想点办法了。可以定义两个指针,第一个指针pAhead从链表的头指针开始遍历向前走K-1步,第二个指针pBehind保持不动;从第K步开始,第二个指针pBehind也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当pAhead指针到达链表的尾节点时,pBehind指针正好指向倒数第K个节点。

扫描二维码关注公众号,回复: 2917502 查看本文章

遍历链表一次的解法:

ListNode* get_kth_from_tail(ListNode *pHead, unsigned int k)
{
	if(pHead == nullptr || k == 0)	return nullptr;
	ListNode *pAhead = pHead;
	ListNode *pAhead = pHead;
	for(int i = 0; i < k - 1; ++i) {		// 向前走k-1步 
		if(pAhead->next != nullptr)
			pAhead = pAhead->next; 
		else								// 链表节点数小于k 
			return nullptr;
	}
	while(pAhead->next != nullptr) {
		pAhead = pAhead->next;
		pBehind = pBehind->next;
	}
	return pBehind;
}

二十三、链表中环的入口节点

题目:如果一个链表中包含环,如何找出环的入口节点?

分析:①判断一个链表中是否包含环;②如果有环,就要找到环的入口。

解法:

/* 返回环的入口节点 */
ListNode* entry_node_of_loop(ListNode *pHead)
{
	if(pHead == nullptr)	return nullptr;
	// 判断是否有环,如果有,则返回一个环中的节点 
	ListNode *meetNode = meet_node(pHead);
	if(meetNode == nullptr)	return nullptr;
	// 计算环中的节点数
	int nodeNumOfLoop = 1;
	ListNode *pCurrent = meetNode;
	while(pCurrent->next != meetNode){
		pCurrent = pCurrent->next;
		nodeNumOfLoop++; 
	}
	// 找出环的入口 
	ListNode *pAhead = pHead;
	ListNode *pBehind = pHead;
	for(int i = 0; i < nodeNumOfCode; ++i) {
		pAhead = pAhead->next;
	} 
	while(pAhead != pBehind) {
		pAhead = pAhead->next;
		pBehind = pBehind->next;
	}
	return pAhead;
}

/* 判断链表中是否包含环 */
/* 如果有环,则返回两个指针相遇的节点 */ 
ListNode meet_node(ListNode *pHead)
{
	ListNode *pSlow = pHead->next;		// 一次走一步 
	if (pSlow == nullptr)	return nullptr;
	ListNode *pFast = pSlow->next;		// 一次走两步
	while(pFast != nullptr) {
		if(pSlow == pFast)				// 如果相遇即有环 
			return pFast;				// 相遇的节点一定在环中 
		pSlow = pSlow->next;
		pFast = pFast->next;
		if(pFast != nullptr)
			pFast = pFast->next;
	}
	return nullptr;
}

小结:利用两个指针pFast和pSlow来判断链表中是否包含环,如果有,就返回它们相遇的节点,显然这个节点是环中的一个节点。根据环中的一个节点,我们就可以遍历该环以得到环中的节点数。根据节点数,结合面试题22,可以利用两个指针来得到环的入口节点

二十四、反转链表

题目:定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

二十五、合并两个排序的链表

题目:输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。  

猜你喜欢

转载自www.cnblogs.com/xzxl/p/9546806.html