【我要去面試】單向鏈表反轉


又到一年一次的招聘季,很多莘莘學子該忙起來了,復習基礎知識,搜索面試題。面試題里面比較經典的是單向鏈表反轉,就是一個單向鏈表,就地反轉。這個面試題主要考查:1. 基礎知識(單向鏈表設計);2. 邊界條件檢查(空鏈表、是否遍歷到鏈表尾、鏈表頭的處理等);3. 代碼風格等。這兩天手癢,自己寫了一個,調通。


這個題稍稍費心思的地方是:到底需要多少個指針來存放當前節點以及后面的節點;以及,按照什么樣的一個順序來調整節點的后繼指針。


多少個臨時指針呢?至少是兩個,因為要反轉,則必須存儲當前節點以及后續節點,才能讓后續節點“反轉”指向當前節點;除此之外,還需存儲后續節點的后續節點的位置。這樣,就是三個指針。就地反轉的核心是,將后繼結點指向當前節點,然后將指向當前節點和后繼結點的臨時指針沿着鏈表向后移動一位;因為后繼結點的next指針已經改變,則需要從第三個臨時指針中獲取后繼的后繼結點的位置,賦值給指向后繼結點的臨時指針。核心代碼如下:

ListNode* pCur = pList;                                   // 第一個臨時指針,指向當前節點
ListNode* pNext = pList->pNext;                      // 第二個臨時指針,指向后繼結點
ListNode* pNextNext = pNext->pNext;              // 第三個臨時指針,指向后繼的后繼節點

while (pNextNext != NULL)                      // 遍歷鏈表
{
        pNext->pNext = pCur;                     // 將后繼結點的next指針反轉,指向當前節點
        pCur = pNext;                                 // 將第一個臨時指針位置后移,指向后繼結點
        pNext = pNextNext;                         // 將第二個臨時指針 位置后移,指向后繼的后繼節點
        pNextNext = pNext->pNext;             // 在上一句,pNext值已經改變,繼續后移
}

// 當pNextNext指向鏈表尾部之后,還有兩個節點的next指針沒有處理:
// 1. 鏈表頭節點;2.鏈表尾節點
pNext->pNext = pCur;                            // 反轉鏈表尾節點的next指針(它當時運行時值是null)
pList->pNext = NULL;                            // 處理頭結點,使之變成尾節點
pList = pNext;                                        // 重新設定鏈表頭的位置

有了核心代碼,按照我的習慣,還是要實地測試一下,把它調通。以下是測試代碼:從文件中讀取一系列數字,存儲在單向鏈表中,然后反轉,並輸出。

bool GetListAndRevert(const char *sFileIn, const char *sFileOut)
{
    // 打開文件
    ifstream in (sFileIn);
    ofstream out (sFileOut);
    if (!in || !out)
    {
        cerr << "Can not open the file" << endl;
        return false;
    }

    // 聲明鏈表節點
    class ListNode
    {
    public:
        int iData;
        ListNode *pNext;

    public:
        ListNode (void) {iData=0; pNext=NULL;};
        ~ListNode (void) {};
    };

    // 定義鏈表
    ListNode* pList = NULL;

    // 從文本文件中讀取數據,在此基礎上生成單向鏈表
    // 1. get the first data (單獨處理鏈表頭)
    pList = new ListNode;
    if (!(in >> pList->iData))
        return false;
    // 2. get the rest ones (pPre記錄了前一個節點——剛剛讀取出的節點——的位置)
    int iNum = 0;
    ListNode* pPre = pList;
    while (in >> iNum)
    {
        pPre->pNext = new ListNode;
        pPre->pNext->iData = iNum;
        pPre = pPre->pNext;
    }


    // 鏈表反轉

    // 1. judge the case of empty list, and the case of single node
    if (NULL == pList || NULL == pList->pNext)
        return true;

    // 2. judge the case of double nodes
    if (NULL == pList->pNext->pNext)
    {
        // exchange the double nodes to each other
        ListNode* p = pList;
        p->pNext->pNext = pList;
        pList->pNext = NULL;
        pList = p;
        return true;
    }

    // 3. the case of more than 3 nodes
    ListNode* pCur = pList;                                   // 第一個臨時指針,指向當前節點
    ListNode* pNext = pList->pNext;                      // 第二個臨時指針,指向后繼結點
    ListNode* pNextNext = pNext->pNext;              // 第三個臨時指針,指向后繼的后繼節點

    while (pNextNext != NULL)                      // 遍歷鏈表
    {
        pNext->pNext = pCur;                     // 將后繼結點的next指針反轉,指向當前節點
        pCur = pNext;                                 // 將第一個臨時指針位置后移,指向后繼結點
        pNext = pNextNext;                         // 將第二個臨時指針 位置后移,指向后繼的后繼節點
        pNextNext = pNext->pNext;             // 在上一句,pNext值已經改變,繼續后移
    }

    // 當pNextNext指向鏈表尾部之后,還有兩個節點的next指針沒有處理:
    // 1. 鏈表頭節點;2.鏈表尾節點
    pNext->pNext = pCur;                            // 反轉鏈表尾節點的next指針(它當時運行時值是null)
    pList->pNext = NULL;                            // 處理頭結點,使之變成尾節點
    pList = pNext;                                        // 重新設定鏈表頭的位置

    // 遍歷鏈表,將結果存儲到文本文件中
    pCur = pList;
    while (pCur != NULL)
    {
        out << pCur->iData << "\n";
        pCur = pCur->pNext;
    }

    // 鏈表刪除,內存釋放
    pCur = pList;
    while (pCur != NULL)
    {
        pNext = pCur->pNext;
        delete pCur;
        pCur = pNext;
    }
    return true;

調了一下,通了。





注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com