單鏈表原地逆置


給定一個帶頭結點的單鏈表,編寫算法將其原地逆置。所謂“原地”是指空間復雜度為O(1)。有兩種方法,頭插法和冒泡法。這兩種方法的時間復雜度均為O(n)。

頭插法

思路

我們知道,用頭插法建立鏈表,得到的鏈表中元素的順序和輸入的順序相反,所以利用這一特點,可以將鏈表逆置。

給定一個帶頭結點的單鏈表L,如下圖所示。

2020-11-28-單鏈表原地逆置-1

首先用指針p存儲鏈表第一個結點,然后將頭結點從鏈表中剝離下來,如下圖所示,此時鏈表L只有一個頭結點。

2020-11-28-單鏈表原地逆置-2

另設一指針r保存p的后繼,將p指向的結點N1用尾插法插入到鏈表L中,

2020-11-28-單鏈表原地逆置3

此時p指向N2,保存p的后繼N3,再將N2尾插到鏈表L中,

2020-11-28-單鏈表原地逆置-4

以此類推,直至保存后繼的指針r為空,退出循環。

頭插法的實現代碼

void Reverse_L1(Linklist L)
{
    /* p 為工作指針, r 為 p 的后繼, 以防斷鏈. */
    LNode *p, *r;

    /* 從第一個元素開始. */
    p = L->next;

    /* 先將頭結點剝離出來. */
    L->next = NULL;

    /* 依次將元素摘下. */
    while (p != NULL)
    {
        /* 暫存 p 的后繼. */
        r = p->next;
        /* 用頭插法插入 p. */
        p->next = L->next;
        L->next = p;
        /* 更新 p, 指向下一個結點. */
        p = r;
    }

    return;
}

冒泡法

我把這種方法稱為“冒泡法”,是因為算法流程和冒泡排序類似,只不過在冒泡排序中是相鄰的元素出現逆序才交換,而鏈表逆置則要求每對結點之間都要交換順序。

思路

冒泡法和頭插法不同,頭插法只有一個工作指針p指向一個操作對象——被摘下來的結點,以及存儲其后繼的指針r。而冒泡法有兩個工作指針,即一對工作指針,以及存儲其后繼的指針r,共計3個指針。考慮如下一般情況,

2020-11-28-單鏈表原地逆置-5

指針pre和p分別指向兩個結點,將其看作一對結點,它們是每次循環操作的對象。循環中,讓N2的后繼指向N1,即完成了(N1,N2)的逆置。之后三個指針進一,pre=p,p=r,r=r->next,重復上述逆置操作,鏈表變成了下圖。

2020-11-28-單鏈表原地逆置-6

顯而易見,如果指針r!=NULL,則循環還要繼續下去,若r==NULL,循環結束,鏈表逆置完成,而指針p指向逆置后的一個元素。

上述步驟發生的前提是鏈表中除了頭結點以外,至少有兩個結點,而為了將判斷是否繼續循環和是否進入循環結合起來,應將指針r初始置為指向第一個結點的后繼,p指向第一個結點,即p=L->next,r=p->next,指針pre無強制要求。

注意初始p指向的結點在完成逆置之后將成為最后一個結點,所以應該在逆置之前將p的后繼置為空,否則逆置之后p的后繼會指向倒數第二個結點,而倒數第二個結點的后繼指向倒數第一個結點,即逆置前的p,兩個結點之間形成環。

冒泡法的實現代碼

void Reverse_L2(Linklist L)
{
    /**
     * 三個指針, p 為要反轉的結點, pre 為 p 前面的結點, r 是保存 p 后繼的指針.
     * 初始狀態 p 指向第一個元素, r 指向第二個元素.
     */
    LNode *pre = L, *p = L->next, *r = p->next;

    /**
     * 將要第一個結點后繼鏈接斷開. 因為它將成為逆置后鏈表的最后一個結點, 否則
     * 將在逆置后的鏈表中的最后一個元素和倒數第二個元素之間形成環.
     */
    p->next = NULL;

    /**
     * 如果 r 不為空, 三個指針前進一個單位.
     * 否則鏈表只有一個結點, 直接執行 while 后的 L->next = p.
     * 循環中將 p 反轉指向其前面的結點 pre, r 是 p 的后繼.
     * 假設鏈表有 n 個元素, 則循環可將前 n - 1 個元素逆置,
     * 之后 p 指向第 n 個元素.
     */
    while (r != NULL)
    {
        /* 指針依次進一. */
        pre = p;
        p = r;
        r = r->next;
        /* 逆置. */
        p->next = pre;
    }

    /* 頭結點指向逆置之后的第一個結點. */
    L->next = p;

    return;
}

代碼倉庫

  1. data-structure/linklist.c at master · tianshihao/data-structure (github.com)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM