劍指offer_面試題16_反轉鏈表(兩種方法)

題目:定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉後鏈表的頭結點。

在解決問題前,先想好測試用例:

1、功能測試:輸入的鏈表含有多個結點,鏈表中只有一個結點

2、特殊輸入測試:頭結點爲 NULL指針

解決這個問題有兩種方式:

前提:

這兩種方式 是以 頭結點並不是第一個數據節點 爲基準 表示的。

這種方式,頭結點並不保存鏈表的結點數據,其數據位只保存鏈表結點總數。

在輸出鏈表時,從頭結點後的第一個數據結點開始輸出,並不輸出頭結點。

方法:

1、頭插法:將頭結點後的鏈中結點 從前向後一個個斷開,並依次以頭插法插入在頭結點後,這樣也相當於完成了鏈表的反轉,鏈表的頭結點依然是原頭結點。

2、逆轉指針:即書上所闡述的方法。這種方法比較直觀,將每個結點的指向翻轉過來,原來指向後一結點的,現在指向前一個結點。

對於第二種方法,可以對照下圖來理解:

圖中闡述了一般情況下,即在鏈表中間,反轉一個結點的步驟。其他結點反轉的方式相同。


算法如下:

#include <iostream>

using namespace std;

typedef struct Node{
    int m_nValue;
    struct Node *m_pNext;
}ListNode;

/**創建空鏈表*/
ListNode * creat_List()
{
    ListNode *pHead;
    pHead = new ListNode;
    pHead->m_nValue = 0;    /**頭結點中保存數據節點總數*/
    pHead->m_pNext = NULL;

    return pHead;
}

/**在鏈表最後插入節點*/
ListNode * insert_Node_behind(ListNode *pHead,int pElem)
{
    if(NULL == pHead)
    {
        cout << "頭結點爲空" << endl;
        return pHead;
    }

    ListNode *temp, *current;
    current = pHead;
    while(NULL != current->m_pNext)
    {
        current = current->m_pNext;
    }
    temp = new ListNode;
    temp->m_nValue = pElem;
    temp->m_pNext = NULL;
    current->m_pNext = temp;

    pHead->m_nValue += 1;

    return pHead;
}

/**遍歷輸出鏈表*/
void show_List(ListNode *pHead)
{
    if(NULL == pHead)
    {
        cout << "頭結點爲空" << endl;
        return;
    }
    ListNode *temp;
    temp = pHead->m_pNext;

    cout << "該 list 爲:" ;
    while(NULL != temp)
    {
        cout << temp->m_nValue << ' ';
        temp = temp->m_pNext;
    }
    cout << endl;
}

/**方法一:將頭結點後的鏈表,一個個斷開,以從前往後的方式,用頭插法依次接在頭結點後*/
ListNode * reverse_List_1(ListNode *pHead)
{
    if(NULL == pHead || NULL == pHead->m_pNext)
    {
        return pHead;
    }

    ListNode *temp, *current;
    current  = pHead->m_pNext;
    pHead->m_pNext = NULL;      /**頭結點和後面的結點斷開*/

    while(NULL != current)
    {
        temp = current->m_pNext;
        current->m_pNext = pHead->m_pNext;
        pHead->m_pNext = current;
        current = temp;
    }
    return pHead;
}

/**方法二,改變指針方向*/
ListNode *reverse_List_2(ListNode *pHead)
{
    if(NULL == pHead || NULL == pHead->m_pNext)
    {
        return pHead;
    }

    ListNode *pPre, *pCurr, *pNex;
    //ListNode *temp = pHead;

    /**設定三個指針的初始狀態*/
    pCurr = pHead->m_pNext;    /**保存當前結點*/
    pNex = pCurr->m_pNext;     /**保存當前結點的下一結點*/
    pPre = NULL;               /**保存當前結點的前一結點*/

    //pHead->m_pNext = NULL;
    //pHead = NULL;


    while(NULL != pNex)
    {
        pCurr->m_pNext = pPre; /*反轉步驟*/

        pPre = pCurr;          /*更新步驟*/
        pCurr = pNex;
        pNex = pNex->m_pNext;
    }

    pCurr->m_pNext = pPre;       /*將最後一個節點指向它的前一結點,防止鏈表斷裂*/
    /** pNex = pPre;   pCurr->m_pNext 和 pNex 雖然指的位置相同,但是並不是同一個指針,如果這樣寫會連接不起來 */
    pHead->m_pNext = pCurr;      /**將倒序後的結點鏈表,從新接頭結點後,這樣反轉後的頭結點依然是原來的頭結點,而第一個數據結點則變成了原來的尾結點*/

    return pHead;
}

int main()
{
    int n_of_list = 6;
    int i = 1;
    ListNode *pHead;
    pHead = creat_List();
    while(n_of_list > 0)
    {
        insert_Node_behind(pHead,i++);
        n_of_list--;
    }
    show_List(pHead);

    reverse_List_1(pHead);
    show_List(pHead);

    reverse_List_2(pHead);
    show_List(pHead);
    
    return 0;
}
結果如下:

以下是鏈表多結點情況,對於其他情況的測試(如單個結點,NULL指針等),可以改變代碼中的數據,來測試。

/*點滴積累,我的一小步O(∩_∩)O~*/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章