雙向鏈表實現隊列與循環鏈表


一、雙向鏈表(double linked list)如圖26.5,是在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。雙向鏈表的基本操作與單鏈表基本一樣,除了插入和刪除的時候需要更改兩個指針變量,需要注意的是修改的順序很重要,插入如圖3-14-5,刪除如圖3-14-6。




鏈表的delete操作需要首先找到要摘除的節點的前趨,而在單鏈表中找某個節點的前趨需要從表頭開始依次查找,對於n個節點的鏈表,刪除操作的時間復雜度為O(n)。可以想像得到,如果每個節點再維護一個指向前趨的指針,刪除操作就像插入操作(這里指只在頭部插入)一樣容易了,時間復雜度為O(1)。要實現雙向鏈表只需在《圖示單鏈表的插入和刪除操作》中代碼的基礎上改動兩個地方。

在linkedlist.h中修改鏈表節點的結構體定義:
struct node

 { 

unsigned char item; 

link prev, next;

};


在linkedlist.c中修改insert和delete函數:

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
void insert(link p)
{
    p->next = head;
    if (head)
        head->prev = p;
    head = p;
    p->prev = NULL;
}
void delete(link p)
{
    if (p->prev)
        p->prev->next = p->next;
    else
        head = p->next;
    if (p->next)
        p->next->prev = p->prev;
}

 


由於引入了prev指針,insert和delete函數中都有一些特殊情況需要用特殊的代碼處理,不能和一般情況用同樣的代碼處理,這非常不爽,如果在表頭和表尾各添加一個Sentinel節點(這兩個節點只用於界定表頭和表尾,不保存數據),就可以把這些特殊情況都轉化為一般情況了。如圖26.6



在《隊列的鏈式存儲結構》中我們使用單鏈表實現隊列的尾進頭出,下面我們演示使用雙向鏈表實現隊列的頭進尾出。

參考:《Linux C編程 一站式學習》

 

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
/*************************************************************************
    > File Name: doublylinkedlist.h
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Fri 28 Dec 2012 08:02:35 PM CST
 ************************************************************************/

#ifndef DOUBLYLINKEDLIST_H
#define DOUBLYLINKEDLIST_H

typedefstructnode *link;

struct node
{
    unsigned char item;
    link prev;
    link next;
} ;

link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void deletep(link p);
void traverse(void (*visit)(link));
void destroy(void);
void enqueue(link p);
link dequeue(void);

#endif


 

 

 C++ Code 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  #include<stdio.h>
#include<stdlib.h>
#include "doublylinkedlist.h"


struct node tailsentinel;
struct node headsentinel = {0, NULL, &tailsentinel};
struct node tailsentinel = {0, &headsentinel, NULL};


static link head = &headsentinel;
static link tail = &tailsentinel;

link make_node(unsigned char item)
{
    link p =  malloc(sizeof(*p));
    p->item = item;
    p->prev = p->next =  NULL;
    printf("make node from Item %d\n", item);
    return p;
}

void free_node(link p)
{
    printf("free node ...\n");
    free(p);
}

link search(unsigned char key)
{
    link p;
    printf("search by key %d\n", key);
    for (p = head->next; p != tail; p = p->next)
        if (p->item == key)
            return p;
    return NULL;
}

void insert(link p)
{
    printf("insert node from head ...\n");
    p->next = head->next;
    head->next->prev = p;
    head->next = p;
    p->prev = head;
}

void deletep(link p)
{
    printf("delete node from ptr ...\n");
    p->prev->next = p->next;
    p->next->prev = p->prev;
}

void traverse(void (*visit)(link))
{
    link p;
    printf("doublylinkedlist traverse ...\n");
    for (p = head->next; p != tail; p = p->next)
        visit(p);
    printf("\n");
}

void destroy(void)
{
    link q, p = head->next;
    printf("destory doublylinkedlist ...\n");
    head->next = tail;
    tail->prev = head;
    while (p != tail)
    {
        q = p;
        p = p->next;
        free_node(q);
    }
}


void enqueue(link p)
{
    printf("enqueue from head ...\n");
    insert(p);
}

link dequeue(void)
{
    if (tail->prev == head)
        return NULL;
    else
    {
        link p = tail->prev;
        printf("dequeue from tail ...\n");
        deletep(p);
        return p;
    }
}


 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
/*************************************************************************
    > File Name: main2.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Fri 28 Dec 2012 08:18:57 PM CST
 ************************************************************************/

#include<stdio.h>
#include "doublylinkedlist.h"

void print_item(link p)
{
    printf("print item %d \n", p->item);
}

int main(void)
{
    link p = make_node(10);
    insert(p);
    p = make_node(5);
    insert(p);
    p = make_node(90);
    insert(p);
    p = search(5);
    deletep(p);
    free_node(p);
    traverse(print_item);
    destroy();
    printf("..................\n");

    p = make_node(100);
    enqueue(p);
    p = make_node(200);
    enqueue(p);
    p = make_node(250);
    enqueue(p);
    while ((p = dequeue()))
    {
        print_item(p);
        free_node(p);
    }

    return 0;
}


輸出為:


解決的error:

關於錯誤 error C2275: “XXX”: 將此類型用作表達式非法

在移植c++代碼到c的時候,經常會出現一個奇怪的錯誤, error C2275: “XXX”: 將此類型用作表達式非法,這個錯誤是由於c的編譯器要求將變量的定義放在所有函數調用語句之前,而c++沒有這樣的要求造成的。解決的辦法就是把變量的定義全部放在變量的生存塊的開始。

------------------------------------------------------------------------------------------------------------------------------------

二、將單鏈表中終端結點的指針端由空指針改為指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表就稱為單循環鏈表,

簡稱循環鏈表(circular linked list)。如下圖所示。


其實循環鏈表和單鏈表的主要差異就在於循環的判斷條件上,原來是判斷p->next是否為空,現在則是p->next不等於頭結點,則循環未結束。

我們在《隊列的順序存儲結構(循環隊列)》中使用數組實現了環形隊列,我們還要“假想”它是首尾相接的,而如果基於鏈表實現環形隊列,我們本來就可以用指針串成首尾相接的。把上面的程序改成雙向環形鏈表也非常簡單,只需要將

把doublylinkedlist.c中的
struct node tailsentinel;

struct node headsentinel = {0, NULL, &tailsentinel};

struct node tailsentinel = {0, &headsentinel, NULL};

static link head = &headsentinel;

static link tail = &tailsentinel;

改成:

struct node sentinel = {0, &sentinel, &sentinel};

static link head = &sentinel;
再把doublylinkedlist.c中所有的tail替換成head即可,相當於把head和tail合二為一了。如圖26.7:




免責聲明!

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



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