**單鏈表的面試題 (一)**
本文是我關於單鏈表的一些面試題的理解。有不正確的請批評指正。主要有:
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個結點的說明見下一篇部落格(單鏈表面試題(二))。由於篇幅問題,造成不便,敬請諒解。