双向链表实现队列与循环链表

一、双向链表(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: [email protected]

> 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: [email protected]

> 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:

原文地址:https://www.cnblogs.com/alantu2018/p/8471624.html

时间: 2024-10-10 23:52:11

双向链表实现队列与循环链表的相关文章

模板(双向链表与队列)

"双向链表与队列" "双向链表"是包含两个指针域,一个数据域,既有指向前驱的指针,还有指向后继的指针,同时可以从两个方向对链表进行遍历,访问各个节点."双向链表"较"单链表"在插入和删除节点方面更为简单,但是所占的空间比"单链表"大."队列"的实现之所以选择使用双向链表,是因为队列的特点是"先进先出",如若使用顺序表则很大程度上的浪费空间.在这样的情境下,链表较顺序表

数据结构之双向链表(包含双向循环链表)

双向(循环)链表是线性表的链式存储结构的又一种形式. 在之前已经讲述了单向链表和循环链表.相比于单向链表只能从头结点出发遍历整个链表的局限性,循环链表使得可以从任意一个结点遍历整个链表. 但是,不管单向链表也好,循环链表也罢,都只能从一个方向遍历链表,即只能查找结点的下一个结点(后继结点),而不能查找结点的上一个结点(前驱结点).鉴于上述问题,引入了双向链表.由于双向循环链表包含双向链表的所有功能操作.因此,我们只讲述双向循环链表. 与单向链表不同,双向链表的结点构造如下图所示.即一个结点由三个

双向链表实现队列

双向链表的结点包含前驱指针和后继指针,队列入队操作是在双向链表尾部添加元素,队列的出队操作是把双向链表的头结点删除,判断队列是否为空只要判断双向链表的头指针是否指向了NULL即可. # include <iostream> # include <cstdlib> using namespace std; struct queueNode { int val; queueNode *pre; queueNode *next; queueNode(int x):val(x),pre(N

Nginx 队列双向链表结构 ngx_quene_t

队列链表结构 队列双向循环链表实现文件:文件:src/core/ngx_queue.h/.c.在 Nginx 的队列实现中,实质就是具有头节点的双向循环链表,这里的双向链表中的节点是没有数据区的,只有两个指向节点的指针.需注意的是队列链表的内存分配不是直接从内存池分配的,即没有进行内存池管理,而是需要我们自己管理内存,所有我们可以指定它在内存池管理或者直接在堆里面进行管理,最好使用内存池进行管理.节点结构定义如下: /* 队列结构,其实质是具有有头节点的双向循环链表 */ typedef str

C++双向循环链表实现

双向循环链表C++实现 1.单链表: 结构图: 2.双向链表: 3.双向循环链表: 对于本程序中,则是给定一个_head  头结点,而不是指针,因为这样更加方便避免一些空判断问题 /* 版权信息:狼 文件名称:BidCirList.h 文件标识: 文件摘要: 利用C++实现简单的双向链表功能.增,删,查,改 //太烦了..我直接给个 带头结点的 表 //swap 移花接木已经是个给力的方法..just try 当前版本:1.1 作 者:狼 完成时间:2015-12-13 */ #ifndef _

双端队列篇deque SDUT OJ 双向队列

双向队列 Time Limit: 1000MS Memory limit: 65536K 题目描述 想想双向链表……双向队列的定义差不多,也就是说一个队列的队尾同时也是队首:两头都可以做出队,入队的操作. 现在给你一系列的操作,请输出最后队列的状态: 命令格式: LIN X  X表示一个整数,命令代表左边进队操作: RIN X  表示右边进队操作: ROUT LOUT   表示出队操作: 输入 第一行包含一个整数M(M<=10000),表示有M个操作: 以下M行每行包含一条命令: 命令可能不合法

双向队列(STL做法)

双向队列 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 想想双向链表--双向队列的定义差不多,也就是说一个队列的队尾同时也是队首:两头都可以做出队,入队的操作. 现在给你一系列的操作,请输出最后队列的状态: 命令格式: LIN X  X表示一个整数,命令代表左边进队操作: RIN X  表示右边进队操作: ROUT LOUT   表示出队操作: 输入 第一行包含一个整数M(M<=10000),表示有M个操作: 以下M行每行包

数据结构习题之栈和队列

第三章  栈和队列 一.    基本要求.重点.难点 本章的目的是介绍栈和队列的逻辑结构定义及在两种存储结构上如何实现栈和队列的基本运算.要求在掌握栈和队列的特点的基础上,懂得在什么样的情况下能够使用栈或队列.本章重点是掌握栈和队列在两种存储结构上实现的基本运算,难点是循环队列中对边界条件的处理. 二.    考核目标和考核要求 要求达到识记层次的有:栈和队列的特点,什么样的情况下能够使用栈或队列 要达到综合应用层次的有:栈的逻辑结构特点,栈与线性表的异同:顺序栈和链栈上实现的进栈.退栈等基本算

双向队列 (STL)

双向队列 Time Limit: 1000MS Memory limit: 65536K 题目描述 想想双向链表……双向队列的定义差不多,也就是说一个队列的队尾同时也是队首:两头都可以做出队,入队的操作.现在给你一系列的操作,请输出最后队列的状态:命令格式:LIN X  X表示一个整数,命令代表左边进队操作:RIN X  表示右边进队操作:ROUTLOUT   表示出队操作: 输入 第一行包含一个整数M(M<=10000),表示有M个操作:以下M行每行包含一条命令:命令可能不合法,对于不合法的命