單鏈表的常見題型彙總

原文地址:http://leihuang.org/2014/05/19/List-Interviews/

單鏈表的一些常見面試題彙總

  1. 單鏈表反轉/逆序
  2. 求單鏈表倒數第N個數
  3. 找到單鏈表的中間結點
  4. 如何判斷鏈表是否有環的存在
  5. 單鏈表建環,無環鏈表變有環
  6. 如何知道環的長度?
  7. 如何找出環的連接點在哪裏?
  8. 刪除單鏈表中的重複元素

下面我先簡單敘述一下每道題的思路,然後把實現的程序一起貼出來,不會講得太細,我覺得只要有了思路之後,接下來的難點就是語言上的一些細節問題了,這個不自己去實現,聽別人講是體會不到的。 要實現下面的代碼你首先要會實現單鏈表, 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

樂此不疲~


發佈了142 篇原創文章 · 獲贊 104 · 訪問量 87萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章