1. 程式人生 > >**單鏈表的面試題 (一)**

**單鏈表的面試題 (一)**

本文是我關於單鏈表的一些面試題的理解。有不正確的請批評指正。主要有:

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個結點的說明見下一篇部落格(單鏈表面試題(二))。由於篇幅問題,造成不便,敬請諒解。