**单链表的面试题 (一)**

本文是我关于单链表的一些面试题的理解。有不正确的请批评指正。主要有:

1  单链表从尾到头打印:(递归与非递归方式)

递归方式实现单链表从尾到头打印:要实现长度为k的链表从尾到头打印;,若长度为k-1的单链表从尾到头打印已知,只需要打印最后一个结点,再从尾到头打印长度为k-1的单链表,以此类推;主要代码如下所示:

//递归方式打印
//1. 长度为k的链表直接打印
//2. 长度为k+1的单链表
//(已知:长度为K的单链表的从尾到头的打印问题,{同类型子问题},求k+1)

void ReversePrint2(SListNode *pFirst)
{
	//只有一个结点,直接打印
	if(pFirst->pNext ==  NULL)
		printf("%d  ",pFirst->data);
	//多个结点,递归打印
	else 
	{
		ReversePrint2(pFirst->pNext);
		//链表中,除了pFirst之外的所有结点均已经打印
		printf("%d  ",pFirst->data);
		
	}
}

非递归方式实现单链表从尾到头打印:找到要打印的结点,并记录,(如果该结点的下一个结点不为空,就一直循环)然后打印该结点的数据域的值。主要代码如下所示:

////从尾到头打印单链表
// 非递归方法
void ReversePrint1(SListNode *pFirst)
{
	SListNode *cur;
	SListNode *end = NULL;//要打印结点的后一个结点
	while(end != pFirst)
	{ 
      cur = pFirst;
	  while(cur ->pNext != end)
	  {
		  cur = cur ->pNext;
	  }

	  //要打印的结点
	  printf("%d  ",cur->data);
	  end = cur;

	}
	printf("  \n");
	return 0;
}

 

2  逆置链表

逆置链表的实现思想主要是:将目标链表上的值头删下来,分别头插到新的空链表中。举例:(如下图)

            主要的代码如下:

//链表逆置
SListNode* ReverseList(SListNode *pFirst)
{  
	SListNode * pNode;//记录被头删的值
	SListNode * cur = pFirst;
	SListNode * end=NULL;//声明空链表
    SListInit(&end);
	while(cur != NULL)
	{//从原链表中头删(并未删除,只是取下来)
		pNode = cur;
		cur = cur ->pNext;
	//pNode就是被删下来的结点
	    pNode->pNext = end;
	    end = pNode;
	}
	return end;
	
}

3  删除非尾无头链表

主要思想是替换法:将下一个结点的值赋值给指定结点,然后将指定结点的指针域改为下一个结点的指针域,free掉下一个结点即可。即:将下一个结点cur的data赋值给pos->data;然后让pos->pNext = cur ->pNext最后free掉cur。主要代码如下:

// 删除非尾无头链表
//替换法,将下一个结点cur的data赋值给pos->data;
//然后让pos->pNext = cur ->pNext最后free掉cur
void RemoveNodeNotTail(SListNode *pos)
{
	SListNode *cur = pos->pNext;
	pos->data = cur->data ;
	pos->pNext = cur->pNext ;
	free(cur);

}

4  无头链表前插入

主要思想也是替换法。创建一个新结点,依次将指定位置后边的值前移一位。即:pNode ->data = pos ->data;pNode ->pNext = pos ->pNext;改变pos的值与指针指向,即:pos ->data  = data;pos ->pNext = pNode。主要代码如下:

// 无头链表前插入
// 替换法,(创建一个新结点Pnode,依次将pos后边的值向前移一位,
// 即:pNode ->data = pos ->data;pNode ->pNext = pos ->pNext;改变pos的值与指针指向,
// 即:pos ->data  = data;pos ->pNext = pNode;)
void InsertNoHead(SListNode *pos, int data)
{
	SListNode *pNode = (SListNode *)malloc(sizeof(SListNode));
	assert(pNode != NULL);
	pNode ->data = pos ->data;
	pNode ->pNext = pos ->pNext;
	pos ->data  = data;
	pos ->pNext = pNode;

}

5  约瑟夫环

主要分两步:1 把链表构成环(先找到最后一个结点tail,再让tail->pNext = pFirst)    2 如果剩下的个数>1,每前进k个结点,删除该结点。主要代码如下:

SListNode * JocephCircle(SListNode *pFirst, int k)
{//1 把链表构成环(先找到最后一个结点tail,再让tail->pNext = pFirst)
	
	SListNode * tail;
	SListNode * cur;
	SListNode * pre;
	int i;
	tail = pFirst;
	for(tail = pFirst; tail->pNext != NULL; tail = tail->pNext)
	{
		
	}//此时tail是最后一个结点
	tail->pNext = pFirst;//构成环
 //2 如果剩下的个数大于,每前进K个结点,删除该结点
    cur = pFirst;
	//结束条件是链表中只剩下一个结点,即:cur->pNext != cur 
	while(cur->pNext != cur)
	{
	    pre = pFirst;//用来记录要删除结点的前一个结点
		for(i=0; i<k-1; i++)
		{
			cur = cur->pNext;
		}
		//cur就是第k个结点,即要删除的结点
		pre ->pNext  = cur ->pNext;
		free(cur);
		//继续循环
		cur = pre->pNext;
	}
	cur ->pNext = NULL;
    return cur;
}

以上程序需要建立在链表创建、插入,打印的前提下,用到的代码如下:


typedef int DataType; 

//创建单链表的成员,其实就是结点
typedef struct SListNode { 
 DataType data; // 值
struct SListNode *pNext; // 指向下一个结点
} SListNode; //SListNode为这个结构体的别名


// 初始化链表
void SListInit(SListNode **ppFirst)
{
	*ppFirst = NULL;
}


//打印
void SListprint(SListNode *ppFirst)
{
    SListNode *p = ppFirst;
	if(p == NULL)
	{
		printf("空链表\n");
	}
	else{

		for(p = ppFirst;p != NULL;p = p->pNext)
		{
			printf("%d  ",p->data);
		}

		
	}
	printf("  \n");
}


//创建新结点,结点的数据域为data,pNext域设置为空
SListNode *_CreateNode(DataType data)
{ 
    SListNode *NewNode; //声明新结点
	NewNode = (SListNode *)malloc(sizeof(SListNode));//创建新结点
	if(NewNode == NULL)
	{
		return;
	}//创建失败
	NewNode ->data = data;
	NewNode ->pNext = NULL;//创建成功
	
}

// 尾部插入(先找到最后一个结点,并把它记录下来,再把最后一个结点的pNext域指向新的结点)
void SListPushBack(SListNode** ppFirst, DataType data)
{
	SListNode *p;//新结点p
	SListNode *p1; 
	p = _CreateNode(data); 
	p1 = *ppFirst;//用来遍历链表(开始位于头结点处)
	//空链表
	if( (*ppFirst) == NULL ){
		*ppFirst = p;
		return;
	} else{
		for(p1 = *ppFirst; p1->pNext != NULL; p1 = p1->pNext)
	       {

        	}
	  p1 ->pNext = p;//此时p1为最后一个结点
	}

}

上述5个面试题的测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>

int main()
{
        SListNode *p1;
	SListNode *result;
        SListInit(&p1);
	SListPushBack(&p1,3);
	SListPushBack(&p1,5);
	SListPushBack(&p1,6);
	SListPushBack(&p1,1);
        SListPushBack(&p1,2);
	SListPushBack(&p1,4);
	SListPushBack(&p1,7);
        SListprint(p1);
	result = JocephCircle(p1, 3);
        SListprint(result);

	RemoveNodeNotTail(result);
	InsertNoHead(result,9);
	SListprint(p1);
	result = ReverseList(p1);
	SListprint(result);
	

	system("pause");
	return 0;

}

剩余的冒泡排序、合并有序链表、找中间节点、找到并删除倒数第k个结点的说明见下一篇博客(单链表面试题(二))。由于篇幅问题,造成不便,敬请谅解。

 

猜你喜欢

转载自blog.csdn.net/zr147258369/article/details/82965431