原文地址:http://leihuang.org/2014/05/19/List-Interviews/
單鏈表的一些常見面試題彙總
- 單鏈表反轉/逆序
- 求單鏈表倒數第N個數
- 找到單鏈表的中間結點
- 如何判斷鏈表是否有環的存在
- 單鏈表建環,無環鏈表變有環
- 如何知道環的長度?
- 如何找出環的連接點在哪裏?
- 刪除單鏈表中的重複元素
下面我先簡單敘述一下每道題的思路,然後把實現的程序一起貼出來,不會講得太細,我覺得只要有了思路之後,接下來的難點就是語言上的一些細節問題了,這個不自己去實現,聽別人講是體會不到的。 要實現下面的代碼你首先要會實現單鏈表, see:線性表
1.單鏈表反轉
我是利用三個指針來實現的,三個連續指針依次向前移動,每次反轉前兩個指針指向數之間的指針。
代碼如下:
/* 鏈表反轉 */
void ReverseList(List *L)
{
if(!L->Next->Next)
;
else{
List *pTemp = L->Next->Next->Next ;
List *pMid = L->Next->Next ;
List *pCurrent = L->Next ;
L->Next->Next = NULL ;
while(pTemp){
pMid->Next = pCurrent ;
pCurrent = pMid ;
pMid = pTemp ;
pTemp = pTemp->Next ;
}
pMid->Next = pCurrent ;
L->Next = pMid ;
}
}
2.求單鏈表倒數第N個數
利用兩指針遍歷表,保持他們的距離爲n,當後面的指針爲NULL時,輸出前面指針所指向的數,即倒數第N個數。
函數代碼:
/* 求單鏈表倒數第N個數 */
int NIndex(List L,int n)
{
List *fir,*sec ;
fir = L.Next ;
sec = L.Next ;
int i ;
for(i=0;sec;++i){
if(i>=n){
fir = fir->Next ;
sec = sec->Next ;
}else
sec = sec->Next ;
}
return fir->elem ;
}
3.找到單鏈表的中間結點
也是利用兩個指針,一個慢移動指針(一次走一步),一個快移動指針(一次走兩步),當快指針指向NULL時,則慢指針指向中間節點。
代碼如下:
/* 輸出單鏈表的中間結點*/
int Mid(List L)
{
List *fir,*sec ;
fir = L.Next ;
sec = L.Next ;
while(sec->Next&&sec->Next->Next){
fir = fir->Next ;
sec = sec->Next->Next ;
}
return fir->elem ;
}
4.如何判斷鏈表是否有環的存在
利用快慢指針遍歷鏈表,如果存在環,則兩指針會相遇,否則快指針會指向NULL結束。
函數代碼:
/*判斷鏈表是否存在環 */
bool HasCycle(List *L)
{
List *slow,*fast ; /* 一個走兩步,一個走一步*/
slow = L ;
fast = L ;
while(fast&&fast->Next){
slow = slow->Next ;
fast = fast->Next->Next ;
if(slow == fast) break ;
}
return !(fast==NULL||fast->Next == NULL) ;
}
5.單鏈表建環,無環鏈表變有環
將最後一個指針指向你所指定的結點。
函數代碼:
/* 如果單鏈表沒環,則給它制指定位置n建環操作 */
void CreateCycle(int n,List *L)
{
if(HasCycle(L)){
printf("L has already cycle!") ;
exit(-1) ;
}
List *entry,*pCurrent ; //環入口結點
int i ;
pCurrent = L ;
for(i=0;pCurrent->Next;++i){
if(i==n)
entry = pCurrent ;
pCurrent = pCurrent->Next ;
}
if(i<n){
printf("n is bigger than the length of L") ;
exit(-1) ;
}else
pCurrent->Next = entry ;
}
6.如何知道環的長度?
快慢指針從第一次相遇開始到第二次相遇,慢指針走過的距離即環的長度。
代碼如下:
/* 如果存在環,則輸出其長度*/
int LenCycle(List *L)
{
int i,k ;
i=k=0 ;
if(HasCycle(L)){
List *slow,*fast ;
slow = L ;
fast = L->Next->Next ;
while(i!=2){
if(i==1)
++k ;
if(slow==fast){
++i ;
}
slow = slow->Next ;
fast = fast->Next->Next ;
}
}else{
printf("L hasn't cycle!\n") ;
exit(-1) ;
}
return k ;
}
7.如何找出環的連接點在哪裏?
有定理:碰撞點p到連接點的距離=頭指針到連接點的距離,因此,分別從碰撞點、頭指針開始走,相遇的那個點就是連接點。
代碼實現:
/* 輸出環的鏈接點*/
int EntryCycle(List *L)
{
if(HasCycle(L)){
List *slow,*fast ; /* 一個走兩步,一個走一步*/
slow = L ;
fast = L ;
while(fast&&fast->Next){
slow = slow->Next ;
fast = fast->Next->Next ;
if(slow==fast) break ;
printf("hello\n") ;
}
List *head = L ;
while(head!=slow){
head = head->Next ;
slow = slow->Next ;
printf("world\n") ;
}
return slow->elem ;
}else{
printf("L hasn't cycle!") ;
}
}
8.刪除單鏈表中的重複元素
使用到兩個指針,其中一個指針pCurrent從第一個元素開始遍歷,另外一個指針run從pCurrent後一個數開始遍歷。
- 如果存在run指向的數等於pCurrent指向的數則刪除pCurrent指向的數,回到pCurrent遍歷;
- 如果run走到鏈表末尾還沒找到相等結點,則回到pCurrent遍歷。
代碼如下:
/* 刪除相同的元素*/
void DelSame(List *L)
{
List *run,*pCurrent ;
pCurrent = L->Next ;
while(pCurrent){
run = pCurrent->Next ;
while(run){
if(pCurrent->elem == run->elem){
Delete(pCurrent->elem,L) ;
break ;
}
run = run->Next ;
}
pCurrent = pCurrent->Next ;
}
}
所有代碼彙總源程序
/*************************************************************************
> File Name: reverselist.c
> Author: huanglei
> Mail: [email protected]
> Created Time: 2014年05月19日 星期一 12時44分36秒
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int elem ;
Node *Next ;
}List;
void InitList(List *L) ;
void Insert(int e,List *L) ;
void Delete(int e,List *L) ;
void PrintList(List L) ;
/* 鏈表反轉 */
void ReverseList(List *L) ;
/* 求單鏈表倒數第N個數 */
int NIndex(List L,int n) ;
/* 輸出單鏈表的中間結點*/
int Mid(List L) ;
/*判斷鏈表是否存在環 */
bool HasCycle(List *L) ;
/* 如果單鏈表沒環,則給它制指定位置n建環操作 */
void CreateCycle(int n,List *L) ;
/* 如果存在環,則輸出其長度*/
int LenCycle(List *L) ;
/* 輸出環的鏈接點*/
int EntryCycle(List *L) ;
/* 刪除相同的元素*/
void DelSame(List *L) ;
main()
{
List L ;
InitList(&L) ;
Insert(1,&L) ;
Insert(2,&L) ;
Insert(3,&L) ;
Insert(2,&L) ;
Insert(5,&L) ;
//Delete(1,&L);
DelSame(&L) ;
PrintList(L) ;
printf("\n") ;
ReverseList(&L) ;
PrintList(L) ;
printf("\n") ;
printf("%d\n",NIndex(L,2)) ;
printf("%d\n",Mid(L)) ;
if(HasCycle(&L))
printf("L has cycle!\n") ;
else
printf("L hasn't cycle\n") ;
CreateCycle(2,&L) ;
if(HasCycle(&L))
printf("L has cycle!\n") ;
else
printf("L hasn't cycle\n") ;
//CreateCycle(1,&L) ; //會提示已經有環了,不能再創建
printf("the length cycle is %d\n",LenCycle(&L)) ;
printf("the entry is %d\n",EntryCycle(&L)) ;
}
/* 鏈表反轉 */
void ReverseList(List *L)
{
if(!L->Next->Next)
;
else{
List *pTemp = L->Next->Next->Next ;
List *pMid = L->Next->Next ;
List *pCurrent = L->Next ;
L->Next->Next = NULL ;
while(pTemp){
pMid->Next = pCurrent ;
pCurrent = pMid ;
pMid = pTemp ;
pTemp = pTemp->Next ;
}
pMid->Next = pCurrent ;
L->Next = pMid ;
}
}
/* 求單鏈表倒數第N個數 */
int NIndex(List L,int n)
{
List *fir,*sec ;
fir = L.Next ;
sec = L.Next ;
int i ;
for(i=0;sec;++i){
if(i>=n){
fir = fir->Next ;
sec = sec->Next ;
}else
sec = sec->Next ;
}
return fir->elem ;
}
/* 輸出單鏈表的中間結點*/
int Mid(List L)
{
List *fir,*sec ;
fir = L.Next ;
sec = L.Next ;
while(sec->Next&&sec->Next->Next){
fir = fir->Next ;
sec = sec->Next->Next ;
}
return fir->elem ;
}
/*判斷鏈表是否存在環 */
bool HasCycle(List *L)
{
List *slow,*fast ; /* 一個走兩步,一個走一步*/
slow = L ;
fast = L ;
while(fast&&fast->Next){
slow = slow->Next ;
fast = fast->Next->Next ;
if(slow == fast) break ;
}
return !(fast==NULL||fast->Next == NULL) ;
}
/* 如果單鏈表沒環,則給它制指定位置n建環操作 */
void CreateCycle(int n,List *L)
{
if(HasCycle(L)){
printf("L has already cycle!") ;
exit(-1) ;
}
List *entry,*pCurrent ; //環入口結點
int i ;
pCurrent = L ;
for(i=0;pCurrent->Next;++i){
if(i==n)
entry = pCurrent ;
pCurrent = pCurrent->Next ;
}
if(i<n){
printf("n is bigger than the length of L") ;
exit(-1) ;
}else
pCurrent->Next = entry ;
}
/* 如果存在環,則輸出其長度*/
int LenCycle(List *L)
{
int i,k ;
i=k=0 ;
if(HasCycle(L)){
List *slow,*fast ;
slow = L ;
fast = L->Next->Next ;
while(i!=2){
if(i==1)
++k ;
if(slow==fast){
++i ;
}
slow = slow->Next ;
fast = fast->Next->Next ;
}
}else{
printf("L hasn't cycle!\n") ;
exit(-1) ;
}
return k ;
}
/* 輸出環的鏈接點*/
int EntryCycle(List *L)
{
if(HasCycle(L)){
List *slow,*fast ; /* 一個走兩步,一個走一步*/
slow = L ;
fast = L ;
while(fast&&fast->Next){
slow = slow->Next ;
fast = fast->Next->Next ;
if(slow==fast) break ;
}
List *head = L ;
while(head!=slow){
head = head->Next ;
slow = slow->Next ;
}
return slow->elem ;
}else{
printf("L hasn't cycle!") ;
}
}
/* 刪除相同的元素*/
void DelSame(List *L)
{
List *run,*pCurrent ;
pCurrent = L->Next ;
while(pCurrent){
run = pCurrent->Next ;
while(run){
printf("hello\n") ;
if(pCurrent->elem == run->elem){
printf("world\n") ;
Delete(pCurrent->elem,L) ;
break ;
}
run = run->Next ;
}
pCurrent = pCurrent->Next ;
}
}
/* 鏈式存儲鏈表的基本操作*/
void InitList(List *L)
{
L->Next = NULL ;
}
void Insert(int e,List *L)
{
List *pCurrent ;
pCurrent = L ;
List *eNode = (List*)malloc(sizeof(struct Node)) ;
if(eNode==NULL){
printf("out of space!") ;
exit(-1) ;
}
for(;pCurrent->Next;pCurrent=pCurrent->Next)
;
pCurrent->Next = eNode ;
eNode->elem = e ;
}
void PrintList(List L)
{
List *pCurrent ;
pCurrent = L.Next ;
if(L.Next==NULL)
printf("list is empty") ;
else{
for(;pCurrent;pCurrent=pCurrent->Next)
printf("%d->",pCurrent->elem) ;
}
}
void Delete(int e ,List *L)
{
List *pCurrent,*tmp ;
pCurrent = L ;
while(pCurrent->Next->elem!=e&&pCurrent->Next)
pCurrent = pCurrent->Next ;
if(!pCurrent->Next){
printf("%d is not exit!\n",e) ;
}else{
tmp = pCurrent->Next->Next ;
free(pCurrent->Next) ;
pCurrent->Next = tmp ;
}
}
輸出如下:
1->3->2->4->5->
5->4->2->3->1->
3
2
L hasn't cycle
L has cycle!
the length cycle is 4
the entry is 4
樂此不疲~