雙向鏈表&雙向循環(huán)鏈表

1. 雙向鏈表

1.1 結(jié)構(gòu)與初始化

雙向鏈表
  • 單向鏈表只能找到后驅(qū)鸡号。
  • 雙向鏈表能輕松地獲取前驅(qū)和后繼琼牧。
  • 插入時(shí)电爹,不管是單向還是雙向蔫仙,都需要先找對(duì)應(yīng)位置的前驅(qū)。
  • 刪除時(shí)丐箩,由于雙向鏈表可以訪問(wèn)前驅(qū)和后繼摇邦,就不需要先找對(duì)應(yīng)位置的前驅(qū)。
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1

/* ElemType類型根據(jù)實(shí)際情況而定屎勘,這里假設(shè)為int */
typedef int ElemType;
/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼施籍,如OK等 */
typedef int Status;

/*線性結(jié)構(gòu)使用順序表的方式存儲(chǔ)*/

//鏈表結(jié)構(gòu)設(shè)計(jì)
typedef struct Node {
    struct Node *prior;
    ElemType data;
    struct Node *next;
} Node, *LinkList;

Status InitList(LinkList *L)
{
    //產(chǎn)生頭結(jié)點(diǎn),并使用L指向此頭結(jié)點(diǎn)
    *L = (LinkList)malloc(sizeof(Node));
    //存儲(chǔ)空間分配失敗
    if (*L == NULL) return ERROR;
    //將頭結(jié)點(diǎn)的指針域置空
    (*L)->prior = NULL;
    (*L)->next = NULL;
    return OK;
}

1.2 遍歷

Status ListTraverse(LinkList L)
{
    if (L == NULL) return ERROR; // 空表
    LinkList p = L->next; // 跳過(guò)頭結(jié)點(diǎn)
    while (p) {
        printf("%d ",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}

1.3 插入

插入節(jié)點(diǎn)

插入時(shí)概漱,需要前一個(gè)節(jié)點(diǎn)來(lái)找到后一個(gè)節(jié)點(diǎn)丑慎。

Status ListInsert(LinkList *L, int loc, ElemType elem)
{
    if (*L == NULL) return ERROR; // 空表
    LinkList p, q;
    p = *L;
    int i = 1;
    for (; p && i < loc; p = p->next, i++); // 找到idx位置
    if(!p || i > loc) return ERROR; // 第idx個(gè)元素不存在
    q = (LinkList)malloc(sizeof(Node));
    if (!q) return ERROR; // 沒(méi)創(chuàng)建出來(lái)報(bào)錯(cuò)
    q->data = elem;
    q->next = p->next; // q的后繼指向p的后繼,p-next可能為NULL犀概,q->next也不用顯式賦值NULL
    p->next = q; // p的后繼指向q
    if (q->next) q->next->prior = q; // q的后繼的前驅(qū)指向q立哑,最后可能為NULL
    q->prior = p; // q的前驅(qū)指向p
    return OK;
}

1.4 刪除

刪除節(jié)點(diǎn)

找到被刪除節(jié)點(diǎn),讓前后牽手就行了姻灶。

Status ListDeleteLoc(LinkList *L, int loc, ElemType *elem)
{
    if (*L == NULL) return ERROR; // 空表
    LinkList p = (*L)->next;
    int i = 1;
    for (; p && i < loc; p = p->next, i++); // 找到idx位置
    if(!p || i > loc) return ERROR; // 第idx個(gè)元素不存在
    p->prior->next = p->next; // p的前驅(qū)的后繼指向p的后繼
    if (p->next) p->next->prior = p->prior; // p的后繼的前驅(qū)指向p的前驅(qū)铛绰,最后可能為NULL
    *elem = p->data;
    free(p); // 釋放被刪除節(jié)點(diǎn)
    return OK;
}

1.5 刪除指定元素

Status ListDeleteElem(LinkList *L, ElemType elem)
{
    if (*L == NULL) return ERROR; // 空表
    LinkList p = (*L)->next;
    while (p && p->data != elem) p = p->next; // 找到對(duì)應(yīng)元素
    if(!p) return ERROR; // 元素elem不存在
    p->prior->next = p->next; // 將p的前驅(qū)指向p的后繼
    if (p->next) p->next->prior = p->prior; // 將p的后繼指向p的前驅(qū),最后可能為NULL
    free(p); // 釋放被刪除節(jié)點(diǎn)
    return OK;
}

1.6 更新指定位置

Status ListUpdateElem(LinkList L, int loc, ElemType elem)
{
    if (L == NULL) return ERROR; // 空表
    LinkList p = L->next;
    int i = 1;
    for (; p && i < loc; p = p->next, i++); // 找到idx位置
    if(!p || i > loc) return ERROR; // 第idx個(gè)元素不存在
    p->data = elem;
    return OK;
}

2. 循環(huán)雙向鏈表

2.1 結(jié)構(gòu)與初始化

線性雙向鏈表
循環(huán)雙向鏈表

循環(huán)雙向鏈表的創(chuàng)建實(shí)際就是雙向鏈表的尾節(jié)點(diǎn)后驅(qū)指向頭結(jié)點(diǎn)产喉,頭結(jié)點(diǎn)前驅(qū)指向尾節(jié)點(diǎn)捂掰,最終形成一個(gè)環(huán)敢会。

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1

/* ElemType類型根據(jù)實(shí)際情況而定,這里假設(shè)為int */
typedef int ElemType;
/* Status是函數(shù)的類型,其值是函數(shù)結(jié)果狀態(tài)代碼这嚣,如OK等 */
typedef int Status;

/*線性結(jié)構(gòu)使用順序表的方式存儲(chǔ)*/

//鏈表結(jié)構(gòu)設(shè)計(jì)
typedef struct Node {
    struct Node *prior;
    ElemType data;
    struct Node *next;
} Node, *LinkList;

Status InitList(LinkList *L, ElemType elem)
{
    //產(chǎn)生頭結(jié)點(diǎn)鸥昏,并使用L指向此頭結(jié)點(diǎn)
    *L = (LinkList)malloc(sizeof(Node));
    //存儲(chǔ)空間分配失敗
    if (*L == NULL) return ERROR;
    //將頭結(jié)點(diǎn)的指針指向自己
    (*L)->prior = *L;
    (*L)->next = *L;
    (*L)->data = elem;
    return OK;
}

依次輸入創(chuàng)建雙向循環(huán)鏈表:

Status CreateList2(LinkList *L) {
    ElemType elem;
    LinkList tail = NULL;
    while (1) {
        scanf("%d", &elem); // 輸入數(shù)據(jù)
        if (elem == 0) break; // 輸入0時(shí)結(jié)束
        if (*L == NULL) { // 表是空的時(shí)候
            *L = (LinkList)malloc(sizeof(Node)); // 創(chuàng)建表頭
            if (*L == NULL) return ERROR; // 沒(méi)創(chuàng)建出來(lái)報(bào)錯(cuò)
            (*L)->data = elem; // 存入數(shù)據(jù)
            tail = *L; // 尾節(jié)點(diǎn),方便后續(xù)添加
        } else { // 表不為空
            tail->next = (LinkList)malloc(sizeof(Node)); // 創(chuàng)建新節(jié)點(diǎn)
            if (!tail->next) return ERROR; // 沒(méi)創(chuàng)建出來(lái)報(bào)錯(cuò)
            tail->next->data = elem; // 存入數(shù)據(jù)
            tail->next->prior = tail;
            tail = tail->next; // 之前的尾節(jié)點(diǎn)指向新節(jié)點(diǎn)
        }
    }
    tail->next = *L; // 形成環(huán)
    (*L)->prior = tail;
    return OK;
}

2.1 正向遍歷

Status ListTraverse(LinkList L)
{
    LinkList p = L; // 從自己開(kāi)始
    do {
        printf("%d ",p->data);
        p = p->next;
    } while (p != L); // 到自己的時(shí)候停止
    printf("\n");
    return OK;
}

2.2 反向遍歷

Status ListTraverseBackwards(LinkList L)
{
    LinkList p = L->prior;
    do {
        printf("%d ",p->data);
        p = p->prior;
    } while (p != L->prior);
    printf("\n");
    return OK;
}

2.3 插入

插入節(jié)點(diǎn)

插入時(shí)姐帚,不管是單向還是雙向吏垮,都需要先找對(duì)應(yīng)位置的前驅(qū)。

Status ListInsert(LinkList *L, int loc, ElemType elem)
{
    if (*L == NULL || loc < 1) return ERROR; // 空表罐旗,位置非法
    LinkList tmp = (LinkList)malloc(sizeof(Node)); // 創(chuàng)建新節(jié)點(diǎn)
    if (!tmp) return ERROR; // 沒(méi)創(chuàng)建出來(lái)報(bào)錯(cuò)
    tmp->data = elem; // 存入數(shù)據(jù)
    LinkList target = *L; // 不管loc是多少膳汪,都是從頭開(kāi)始找
    if (loc == 1) {
        for (; target->next != *L; target = target->next); // 找到頭結(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)(尾節(jié)點(diǎn))
        *L = tmp; // 頭結(jié)點(diǎn)變?yōu)樾鹿?jié)點(diǎn)
    } else {
        int idx = 1;
        for (; target->next != *L && idx < loc - 1; target = target->next, ++idx); // 找到loc結(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
        if (target->next == *L || idx > loc - 1) return ERROR; // 找了一圈了,還沒(méi)到指定位置九秀,越界
    }
    tmp->next = target->next; // 新節(jié)點(diǎn)的后驅(qū)指向前一個(gè)節(jié)點(diǎn)的后驅(qū)
    target->next = tmp; // 前一個(gè)節(jié)點(diǎn)指向新節(jié)點(diǎn)
    tmp->next->prior = tmp; // 新節(jié)點(diǎn)后繼的前驅(qū)指向新節(jié)點(diǎn)
    tmp->prior = target; // 新節(jié)點(diǎn)的前驅(qū)指向前一個(gè)節(jié)點(diǎn)
    return OK;
}

2.4 刪除

刪除節(jié)點(diǎn)

刪除時(shí)遗嗽,由于雙向鏈表可以訪問(wèn)前驅(qū)和后繼,就不需要先找對(duì)應(yīng)位置的前驅(qū)鼓蜒。

Status ListDeleteLoc(LinkList *L, int loc, ElemType *elem)
{
    if (*L == NULL || loc < 1) return ERROR; // 空表痹换,位置非法
    LinkList target = *L; // 不管loc是多少,都是從頭開(kāi)始找
    if (loc == 1) {
        if ((*L)->next == *L) { // 如果只有1個(gè)節(jié)點(diǎn)
            free(*L);
            *L = NULL;
            return OK;
        };
        *L = (*L)->next; // 頭節(jié)點(diǎn)變?yōu)槲补?jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)
    } else {
        int idx = 1;
        for (; target->next != *L && idx < loc; target = target->next, ++idx); // 找到loc節(jié)點(diǎn)
        if (target->next == *L || idx > loc) return ERROR; // 找了一圈了都弹,還沒(méi)到指定位置娇豫,越界
    }
    target->prior->next = target->next; // target的前驅(qū)指向target的后繼
    target->next->prior = target->prior; // 將target的后繼的前驅(qū)指向target的前驅(qū)
    *elem = target->data;
    free(target); // 釋放被刪除節(jié)點(diǎn)
    return OK;
}

2.5 刪除指定元素

Status ListDeleteElem(LinkList *L, ElemType elem)
{
    if (*L == NULL) return ERROR; // 空表
    LinkList target = *L; // 都是從頭開(kāi)始找
    for (; target->next != *L && target->data != elem; target = target->next); // 找到elem元素所在節(jié)點(diǎn)
    if (target->next == *L && target->data != elem) return ERROR; // 找了一圈了,沒(méi)有找到
    if (target->next == target->prior) { // 前驅(qū)和后繼相等缔杉,說(shuō)明只有一個(gè)節(jié)點(diǎn)
        free(target);
        *L = NULL;
        return OK;
    }
    target->prior->next = target->next; // target的前驅(qū)指向target的后繼
    target->next->prior = target->prior; // 將target的后繼的前驅(qū)指向target的前驅(qū)
    if (*L == target) *L = (*L)->next; // 如果是頭節(jié)點(diǎn)锤躁,移動(dòng)為下一個(gè)
    free(target); // 釋放被刪除節(jié)點(diǎn)
    return OK;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市或详,隨后出現(xiàn)的幾起案子系羞,更是在濱河造成了極大的恐慌,老刑警劉巖霸琴,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椒振,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡梧乘,警方通過(guò)查閱死者的電腦和手機(jī)澎迎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)选调,“玉大人夹供,你說(shuō)我怎么就攤上這事∪士埃” “怎么了哮洽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)弦聂。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么秘症? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮枪眉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘再层。我一直安慰自己贸铜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布树绩。 她就那樣靜靜地躺著萨脑,像睡著了一般隐轩。 火紅的嫁衣襯著肌膚如雪饺饭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天职车,我揣著相機(jī)與錄音瘫俊,去河邊找鬼。 笑死悴灵,一個(gè)胖子當(dāng)著我的面吹牛扛芽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播积瞒,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼川尖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了茫孔?” 一聲冷哼從身側(cè)響起叮喳,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缰贝,沒(méi)想到半個(gè)月后馍悟,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剩晴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年锣咒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赞弥。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅整,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绽左,到底是詐尸還是另有隱情悼嫉,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布妇菱,位于F島的核電站承粤,受9級(jí)特大地震影響暴区,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辛臊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一仙粱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧彻舰,春花似錦伐割、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至尚胞,卻和暖如春硬霍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笼裳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工唯卖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躬柬。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓拜轨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親允青。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橄碾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348