数据结构之链表(三)

四、双向线性链表

C语言中一种更复杂的链表式“双向链表”或“双面链表”。其表中的每个节点有两个连接:一个指向前一个节点,(当这个“连接”为第一个:“连接”时,指向空值或者空列表);而另一个指向下一个节点,(当这个“连接”为最后一个:“连接”时,指向空值或者空列表)。例如:

编程必学之数据结构——链表(三)

在一些低级语言中,XOR-linking 提供一种在双向链表中通过用一个词来表示两个链接(前后),我们通常不提倡这种方法.

双向链表也叫双链表。双向链表中不仅有指向后一个节点的指针,还有指向前一个节点的指针。这样就可以从任何一个节点访问前一个节点,当然也可以访问后一个节点,以至于整个链表。一般是在需要大批量的另外储存数据在链表中的位置的时候用。双向链表也可以配合下面的其他链表的扩展使用。

由于另外储存了指向链表内容的指针,并且可能会修改相邻的节点,有的时候第一个节点可能会被删除或者之前添加一个新的节点。这时候就要修改指向首个节点的指针。有一种方便的可以消除这种特殊情况的方法是在最后一个节点之后、第一个节点之前储存一个永远不会被删除或移动的虚拟节点,形成一个下面说的循环链表。这个虚拟节点之后的节点就是真正的第一个节点。这种情况通常可以用这个虚拟节点直接表示这个链表,对于把链表单独的存在数据里的情况,也可以直接用这个数组表示链表并用第 0 个或者第 -1 个 (如果编译器支持)节点固定的表示这个虚拟节点。

1、双向线性链表

#include 

#include 

typedef struct Node

{

int data;

struct Node *pNext;

struct Node *pPre;

}NODE, *pNODE;

//创建双向链表

pNODE CreateDbLinkList(void);

//打印链表

void TraverseDbLinkList(pNODE pHead);

//判断链表是否为空

int IsEmptyDbLinkList(pNODE pHead);

//计算链表长度

int GetLengthDbLinkList(pNODE pHead);

//向链表插入节点

int InsertEleDbLinkList(pNODE pHead, int pos, int data);

//从链表删除节点

int DeleteEleDbLinkList(pNODE pHead, int pos);

//删除整个链表,释放内存

void FreeMemory(pNODE *ppHead);

int main(void)

{

int flag = 0, length = 0;

int position = 0, value = 0;

pNODE head = NULL;

head = CreateDbLinkList();

flag = IsEmptyDbLinkList(head);

if (flag)

printf("双向链表为空! ");

else

{

length = GetLengthDbLinkList(head);

printf("双向链表的长度为:%d ", length);

TraverseDbLinkList(head);

}

printf("请输入要插入节点的位置和元素值(两个数用空格隔开):");

scanf("%d %d", &position, &value);

flag = InsertEleDbLinkList(head, position, value);

if (flag)

{

printf("插入节点成功! ");

TraverseDbLinkList(head);

}

else

printf("插入节点失败! ");

flag = IsEmptyDbLinkList(head);

if (flag)

printf("双向链表为空,不能进行删除操作! ");

else

{

printf("请输入要删除节点的位置:");

scanf("%d", &position);

flag = DeleteEleDbLinkList(head, position);

if (flag)

{

printf("删除节点成功! ");

TraverseDbLinkList(head);

}

else

printf("删除节点失败! ");

}

FreeMemory(&head);

if (NULL == head)

printf("已成功删除双向链表,释放内存完成! ");

else

printf("删除双向链表失败,释放内存未完成! ");

return 0;

}

//创建双向链表

pNODE CreateDbLinkList(void)

{

int i, length = 0, data = 0;

pNODE pTail = NULL, p_new = NULL;

pNODE pHead = (pNODE)malloc(sizeof(NODE));

if (NULL == pHead)

{

printf("内存分配失败! ");

exit(EXIT_FAILURE);

}

pHead->data = 0;

pHead->pPre = NULL;

pHead->pNext = NULL;

pTail = pHead;

printf("请输入想要创建链表的长度:");

scanf("%d", &length);

for (i=1; idata = data;

p_new->pNext = NULL;

p_new->pPre = pTail;

pTail->pNext = p_new;

pTail = p_new;

}

return pHead;

}

//打印链表

void TraverseDbLinkList(pNODE pHead)

{

pNODE pt = pHead->pNext;

printf("打印链表如:");

while (pt != NULL)

{

printf("%d ", pt->data);

pt = pt->pNext;

}

putchar(' ');

}

//判断链表是否为空

int IsEmptyDbLinkList(pNODE pHead)

{

pNODE pt = pHead->pNext;

if (pt == NULL)

return 1;

else

return 0;

}

//计算链表的长度

int GetLengthDbLinkList(pNODE pHead)

{

int length = 0;

pNODE pt = pHead->pNext;

while (pt != NULL)

{

length++;

pt = pt->pNext;

}

return length;

}

//向双向链表中插入节点

int InsertEleDbLinkList(pNODE pHead, int pos, int data)

{

pNODE pt = NULL, p_new = NULL;

if (pos > 0 && pos < GetLengthDbLinkList(pHead)+2)

{

p_new = (pNODE)malloc(sizeof(NODE));

if (NULL == p_new)

{

printf("内存分配失败! ");

exit(EXIT_FAILURE);

}

while (1)

{

pos--;

if (0 == pos)

break;

pHead = pHead->pNext;

}

pt = pHead->pNext;

p_new->data = data;

p_new->pNext = pt;

if (NULL != pt)

pt->pPre = p_new;

p_new->pPre = pHead;

pHead->pNext = p_new;

return 1;

}

else

return 0;

}

//从链表中删除节点

int DeleteEleDbLinkList(pNODE pHead, int pos)

{

pNODE pt = NULL;

if (pos > 0 && pos < GetLengthDbLinkList(pHead) + 1)

{

while (1)

{

pos--;

if (0 == pos)

break;

pHead = pHead->pNext;

}

pt = pHead->pNext->pNext;

free(pHead->pNext);

pHead->pNext = pt;

if (NULL != pt)

pt->pPre = pHead;

return 1;

}

else

return 0;

}

//删除整个链表,释放内存

void FreeMemory(pNODE *ppHead)

{

pNODE pt = NULL;

while (*ppHead != NULL)

{

pt = (*ppHead)->pNext;

free(*ppHead);

if (NULL != pt)

pt->pPre = NULL;

*ppHead = pt;

}

}

输出结果:

请输入想要创建链表的长度:5

请输入第1个元素的值:1

请输入第2个元素的值:2

请输入第3个元素的值:3

请输入第4个元素的值:4

请输入第5个元素的值:5

双向链表的长度为:5

打印链表如:1 2 3 4 5

请输入要插入节点的位置和元素值(两个数用空格隔开):2 6

插入节点成功!

打印链表如:1 6 2 3 4 5

请输入要删除节点的位置:2

删除节点成功!

打印链表如:1 2 3 4 5

已成功删除双向链表,释放内存完成!

2、自写双向线性链表

代码实现说明:

插入节点时,首先判断要插入的节点的位置是否正确,然后创建节点。最后,插入节点,插入时分两种情况处理,一种是插入到第一个节点前面,另一种是插入到链表中间。

创建持有待插入元素的新节点,令其前指针指向给定节点的前节点,令其后指针指向给定节点。

若新建节点存在前节点,则令其前节点的后指针指向新建节点,否则新建节点为新的首节点。

令新建节点的后节点的前指针指向新建节点。

数据结构之链表(三)_第1张图片

删除节点时,首先判断要删除的结点位置是否正确。然后,删除结点,删除时分两种情况处理:一种是删除第一个结点,另一种是删除链表的中间结点。最后,释放被删除结点所占有的存储空间。

若将亡节点存在前节点,则令其前节点的后指针指向将亡节点的后节点,否则将亡节点的后节点为新的首节点。

若将亡节点存在后节点,则令其后节点的前指针指向将亡节点的前节点,否则将亡节点的前节点为新的尾节点。

销毁将亡节点。

数据结构之链表(三)_第2张图片


#include 

#include 

#include 

typedef int DataType;

struct Node{

DataType data;

struct Node *pre, *next;

};

void init(struct Node** head)

{

*head = NULL;

}

int getSize(struct Node* head)

{

struct Node* p = head;

int count = 0;

while(p)

{

count ++;

p = p->next;

}

return count;

}

//找到指定位置 元素地址

struct Node* getptr(struct Node* head, int pos) {

struct Node *p = head;

if (p == 0 || pos == 0) {

return head;

}

int i = 0;

for(i = 0; p && i < pos; i++) {

p = p->next;

}

return p;

}

//指定位置 插入元素

bool insert(struct Node** head, int position, DataType d) {

if (position < 0 || position > getSize(*head)) {

return false;

}

//创建 节点

struct Node *node = (struct Node*)malloc(sizeof(struct Node));

node->data = d;

node->pre = NULL;

node->next = NULL;

//插入到第一个节点的前面

if (position == 0) {

node->next = *head;

if (*head != NULL)

(*head)->pre = node;

*head = node;

return true;

}

//插入到链表的中间

struct Node *p = getptr(*head, position - 1);

struct Node* r = p->next;

node->next = r;

r->pre = node;

p->next = node;

node->pre = p;

return true;

}

//删除指定位置元素

bool erases(struct Node** head, int pos) {

if (pos < 0 || pos >= getSize(*head))

return false;

//删除第一个结点

struct Node *p = *head;

if (pos == 0) {

*head = (*head)->next;

if (*head != NULL)

(*head)->pre = NULL;

free(p);

p = NULL;

return true;

}

//删除链表的中间结点

p = getptr(*head, pos - 1);

struct Node *q = p->next;

p->next = q->next;

q->next->pre = p;

free(q);

q = NULL;

return true;

}

//修改指定位置 元素

bool set(struct Node* head, int pos, DataType d) {

if (pos < 0 || pos >= getSize(head)) {

return false;

}

struct Node *p = getptr(head, pos);

p->data = d;

return true;

}

//清理 链表

void clears(struct Node* head) {

while (head) {

struct Node *p = head->next;

free(head);

head = p;

}

}

//打印

void print(struct Node* head) {

struct Node *p = head;

while (p) {

printf("%d ", p->data);

p = p->next;

}

printf(" ");

}

int main()

{

//头指针

struct Node *headList;

init(&headList);

insert(&headList, 0, 10);

insert(&headList, 0, 20);

insert(&headList, 0, 30);

insert(&headList, 2, 40);

insert(&headList, 2, 50);

insert(&headList, 0, 60);

insert(&headList, 0, 80);

print(headList);

erases(&headList, 1);

print(headList);

set(headList, 0, 100);

set(headList, 0, 110);

print(headList);

return 0;

}

输出结果:

80 60 30 20 50 40 10

80 30 20 50 40 10

110 30 20 50 40 10

3、双向线性链表总结

每个节点除了存放元素数据之外,还需要保存指向下一个节点的指针,即所谓后指针,以及指向前一个节点的指针,即所谓前指针。

链表首节点的前指针和尾节点的后指针俱为空指针。

数据结构之链表(三)_第3张图片

你可能感兴趣的:(编程语言,知识)