链表相关面试题(一)

当我对指针的使用感到很简单的时候,链表给了我一记重击。

在面试中,最经常被提及的就是链表,因为它简单,但又因为需要对指针进行操作,凡是涉及到指针的,都需要我们具有良好的编程基础才能确保代码没有任何错误。

所以,整理了一些面试题,特别记录下来。


注:本文是对上一篇文章的补充。(http://blog.youkuaiyun.com/qq_35524916/article/details/66296292


另外:在对参数进行if判空还是assert判空上,我才用如下方法:若对参数的引用非法,使用assert,否则使用if。


附上,本文要解决的问题

// 使用递归实现从尾到头打印单链表
void PrintFromTail2Head(PNode pHead);

// 删除单链表的非尾结点(不能遍历链表)
void DeleteNotTailNode(PNode pos);

// 在单链表非头结点前插入结点data
void InsertNotHeadNode(PNode pos, DataType data);

//约瑟夫环问题
void JosephCircle(PNode *pHead, size_t M);

// 查找单链表的中间结点,要求只便利一次链表
PNode FindMidNode(PNode pHead);

// 查找单链表的倒数第K个结点,要求只遍历一次链表
PNode FindLastKNode(PNode pHead, size_t k);

// 使用冒泡排序对单链表进行排序
void BubbleSort(PNode pHead);


下面一一分析:

1,利用递归逆序打印单链表

解析:利用递归思想,可以实现从后往前的打印。

注:参入参数为空的情况。

void PrintFromTail2Head(PNode pHead)
{
	if (NULL == pHead)
		return ;

	if (pHead->_pNext)
		PrintFromTail2Head(pHead->_pNext);

	printf("%d->", pHead->_data);
}



2, 删除单链表的非尾结点(不能遍历单链表)

解析:如何在不知道链表节点的前一节点的情况下删除该节点?

可以遍历一次链表,然后保存两个节点的位置,再实现删除操作。

可是,当面试官要求不能遍历单链表呢?那就比较难了。

既然是非尾节点,那么可以激荡pos的下一节点,与当前节点的值互换,然后删除pos的下一节点即可。

注:链表为空或只有一个节点的情况。

void DeleteNotTailNode(PNode pos)
{

	PNode pDelNode = NULL;

	if ((NULL == pos) || (NULL == pos->_pNext))
		return ;

	pDelNode = pos->_pNext;

	pos->_data = pDelNode->_data;
	pos->_pNext = pDelNode->_pNext;

	free(pDelNode);
	pDelNode = NULL;
}



3,在单链表非头结点前插入结点data

解析:和上面的思想差不多,先将节点插入到data后面,然后互换两个节点的值。

注:链表为空。

void InsertNotHeadNode(PNode pos, DataType data)
{
	PNode pNewNode = NULL;
	DataType temp_data = 0;

	if(NULL != pos)
           	return ;

	if (!(pNewNode = BuyNode(data)))
		return ;
	
	pNewNode->_pNext = pos->_pNext;
	pos->_pNext = pNewNode;

	temp_data = pos->_data;
	pos->_data = pNewNode->_data;
	pNewNode->_data = temp_data;
}

4,约瑟夫环问题

解析:先将单链表的首尾相连,形成环。

然后设定一个计数器m,开始就遍历环,每遍历一个节点,m减1,同时删除该节点,直到链表只剩于一个链表。

注:链表传入的参数为空,无法解引用。

void JosephCircle(PNode *pHead, size_t M)
{
	PNode pTailNode = NULL; //用来指向最后一个节点
	PNode pCurNode = NULL;	//用来指向数到M的节点
	size_t m = 0;  

	assert(NULL != pHead); 
	assert(0 != M);   //M不能小于0

	pCurNode = *pHead;		//pCurNode从首节点开始
	if (!(pTailNode = Back(*pHead))) //指向最后一个节点。
		return ;

	pTailNode->_pNext = *pHead;	//首尾相连形成约瑟夫环

	while (pCurNode->_pNext != pCurNode) //当链表剩余不止一个节点时
	{
		PNode pDelNode = NULL;	//用来删除的节点
		m = M;
		while (--m)
			pCurNode = pCurNode->_pNext;//pCurNode指向数到M的节点

		printf("%d->", pCurNode->_data);	//打印节点信息
		
		//将下一节点的值赋给当前节点,删除下一节点。
		pDelNode = pCurNode->_pNext;	//pDelNode指向下一节点

		pCurNode->_data = pDelNode->_data;//拷贝数据
		pCurNode->_pNext = pDelNode->_pNext;//跳过pDelNode,指向pDelNode的下一节点。

		free(pDelNode);//释放节点。
		pDelNode = NULL;
	}
	
	printf("%d-><null>\n", pCurNode->_data);//打印剩余节点信息
	free(pCurNode);	//释放最后一个节点。
	pCurNode = NULL;

	*pHead = NULL;	//链表为空
}


5,查找单链表的中间结点,要求只遍历一次链表

解析:只遍历一次话,只利用一个节点遍历的话,实现比较麻烦。

所以采用两个节点遍历,一个快指针,一个慢指针。快指针一次走一步,慢指针一次走一步。

当快指针走到链表尾时,慢指针刚好指向链表额中间节点。返回慢指针即可。

注:链表为空的情况。

PNode FindMidNode(PNode pHead)
{	
	//定义一个快指针,一次走两步,定义一个慢指针,一次走一步
	PNode pFast = NULL;
	PNode pSlow = NULL;

	//当链表为空,返回头指针
	if (NULL == pHead)
		return pHead;
	
	//都从pHead开始遍历
	pFast = pSlow = pHead;

	//当快指针走完后,返回慢指针的地址。
	while (NULL != pFast->_pNext)
	{
		//快慢指针都先走一步
		pFast = pFast->_pNext;
		pSlow = pSlow->_pNext;

		//快指针多走一步
		if (NULL != pFast->_pNext)
			pFast = pFast->_pNext;
	}

	return pSlow;
}


6,查找单链表的倒数第K个结点,要求只遍历一次链表

解析:类似利用了快慢指针方法,快指针先走k-1个几点,然后快慢指针一起走,直到快指针走完,返回慢指针。

注:链表为空。

PNode FindLastKNode(PNode pHead, size_t k)
{
	//利用快慢指针,快指针比慢指针先走(k-1)步。当快指针走完后,返回慢指针地址。
	PNode pAhead = NULL;
	PNode pBehind = NULL;
		
	if (NULL == pHead)	//链表为空时返回NULL
		return NULL;

	pAhead = pBehind = pHead;	//从头结点开始遍历

	while ((k-- > 1) && (NULL != pAhead->_pNext))	//pFast先走k-1步
		pAhead = pAhead->_pNext;

	while (NULL != pAhead->_pNext)
	{
		pAhead = pAhead->_pNext;
		pBehind = pBehind->_pNext;
	}

	return pBehind;
}


7,使用冒泡排序对单链表排序

解析:定义一个开始节点pStart,从头开始遍历。每遍历一个几点,

在循环内定义一个当前节点pCurNode和pStart比较大小,判断是否需要交换值,若需要,跳出内循环,继续外循环

若不需要,继续内循环,直到pCurNode遍历完或满足交换条件。

void BubbleSort(PNode pHead)
{	
	PNode pStart = NULL;//开始比较的节点指针

	if (NULL == pHead)
		return ;

	pStart = pHead;//从头结点开始遍历

	while (NULL != pStart->_pNext)
	{
		PNode pCurNode = pStart->_pNext;//定义要比较的节点。

		while (NULL != pCurNode)
		{
			if (pStart->_data > pCurNode->_data)
			{
				DataType data = pCurNode->_data;
				pCurNode->_data = pStart->_data;
				pStart->_data = data;
			}

			pCurNode = pCurNode->_pNext;//要比较的节点往后移一步
		}

		pStart = pStart->_pNext;//开始比较节点往后移一步
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值