1.概念
对链表而言,双向遍历数据节点相较于单向而言方便许多,因此双向链表在实际运用中是最常见的链式形态。
2.双向链表
1. 基本操操作
-
设计节点
-
创建数据节点
// 创建数据节点 struct node { dataType data; struct node *prev;// 指向上一个节点 struct node *next;// 指向下一个节点 }; 2.
2.创建头节点
// 创建头节点 struct headNode { struct node *first; // 指向首节点 struct node *last; // 指向最后一个节点 int nodeNumber; // 记录节点数 };
1. 初始化空链表
// 创建头节点 struct headNode *create_head() { // 创建头节点 struct headNode *head = malloc(sizeof(struct headNode)); if(head == NULL) { perror("create head failed:"); return NULL; } head->first = NULL; head->last = NULL; head->nodeNumber = 0; return head; } // 创建新节点 struct node *create_new_node(dataType data) { struct node *pnew = malloc(sizeof(struct node)); if(pnew == NULL) { perror("create new node failed:"); return NULL; } pnew->data = data; pnew->prev = NULL; pnew->next = NULL; }
-
-
增删节点
struct headNode *add_node_list(struct headNode *head,dataType newData,dataType data) { // 创建新节点 struct node *pnew = create_new_node(newData); if(pnew == NULL) return NULL; // 找节点 struct node *p = head->first; while(p) { if(p->data == data) break; else { p = p->next; } } // 如果找的是第一个节点 if(p->data == head->first->data) { addHead(pnew,head); } else if(p == NULL) { addTail(pnew,head); } else { pnew->next = p; pnew->prev = p; p->prev->next = pnew; pnew->prev = pnew; } head->nodeNumber++; return head; } struct headNode *del_node(struct headNode *head,dataType data) { // 找节点 struct node *p = head->first; while(p) { if(p->data == data) break; else p = p->next; } // 如果是第一个节点 if(head->first->data == data) { head->first->next->prev = head->first; head->first = p->next; p->next = NULL; p->prev = NULL; free(p); } else if(p->data == head->last->data) { p->prev->next = NULL; p->prev = NULL; free(p); } else if(p == NULL) { printf("没有可删除的节点\n"); } else { p->next->prev = p->prev; p->prev->next = p->next; p->prev = NULL; p->next = NULL; free(p); } head->nodeNumber--; return head; }
-
链表遍历
void showList(struct headNode *head) { for(struct node *p = head->first;p != head->last->next;p = p->next) { printf("%d\t",p->data); } printf("\n"); printf("节点数为:%d\n",head->nodeNumber); }
5.销毁节点
// 销毁链表
struct headNode * distory_list(struct headNode *head)
{
if(isEmpty(head))
return false;
// 逐一删除节点
struct node *p = NULL;
for(struct node *tmp = head->first;tmp != NULL; tmp = p)
{
p = tmp->next;
free(tmp);
head->nodeNumber--;
}
return head;
}
Demo 双向链表
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int dataType;
// 节点
struct node
{
dataType data;
struct node *pre; // 指向前驱节点
struct node *next; // 指向后继节点
};
// 链表管理结构体
struct headNode
{
struct node *first;
struct node *last;
int nodeNumber;
};
struct node *create_node(dataType data);
void show_list(struct headNode *head);
bool isEmpty(struct headNode *head);
// 初始化头节点
struct headNode *init_headNode(void)
{
struct headNode *head = malloc(sizeof(struct headNode));
if (head == NULL)
return NULL;
head->first = NULL;
head->last = NULL;
head->nodeNumber = 0;
return head;
}
// 尾插
void add_tail(struct headNode *head, struct node *pnew)
{
head->last->next = pnew;
pnew->pre = head->last;
head->last = pnew;
}
// 头插
void add_head(struct headNode *head, struct node *pnew)
{
pnew->next = head->first;
head->first->pre = pnew;
head->first = pnew;
}
/*
* insert_node : 插入节点
* 参数:
* head : 链表管理结构体
* OldDate : 待更新旧数据
* newData : 新数据
* 返回值:
* 无
*/
void insert_node(struct headNode *head, dataType OldDate, dataType NewDate)
{
struct node *pnew = create_node(NewDate);
if (pnew == NULL)
return;
struct node *cur = head->first;
// 找位置
while (cur)
{
if (cur->data == OldDate)
break;
else
cur = cur->next;
}
if (cur == NULL) // 没找到,添加到尾部
add_tail(head, pnew);
else if (cur->data == head->first->data) // 在头部插入
add_head(head, pnew);
else
{ // 在中间插入
pnew->next = cur;
pnew->pre = cur->pre;
cur->pre->next = pnew;
cur->pre = pnew;
}
head->nodeNumber++;
}
/*
* delete_node : 删除节点
* 参数:
* head : 链表管理结构体
* data : 待删除数据
* 返回值:
* 无
*/
void delete_node(struct headNode *head, dataType data)
{
if (isEmpty(head))
{
printf("此表为空!\n");
return;
}
struct node *cur = head->first;
while (cur != NULL)
{
if (cur->data == data)
{
if(cur == head->first && cur == head->last)
{
head->first = NULL;
head->last =NULL;
}
else if(cur == head->first)
{
head->first = cur->next;
head->first->pre = NULL;
}
else if (cur == head->last)
{
head->last = cur->pre;
head->last->next = NULL;
}
else
{
cur->pre->next = cur->next;
cur->next->pre = cur->pre;
}
free(cur);
head->nodeNumber--;
return;
}
cur = cur->next;
}
}
/*
* renew_node : 更新节点
* 参数:
* head : 链表管理结构体
* OldDate : 待更新旧数据
* newData : 新数据
* 返回值:
* 无
*/
void renew_node(struct headNode *head, dataType OldDate, dataType NewDate)
{
if (isEmpty(head))
{
printf("此表为空\n");
return;
}
struct node *p = head->first;
while (p!=NULL)
{
if(p->data == OldDate)
{
p->data = NewDate;
return;
}
else
p = p->next;
}
}
// 创建双向链表
struct headNode *create_list(void)
{
// 初始化头节点
struct headNode *head = init_headNode();
if (head == NULL)
return NULL;
while (1)
{
dataType data;
if (scanf("%d", &data) == 0)
break;
// 创建新节点
struct node *pnew = create_node(data);
if (pnew == NULL)
return NULL;
if (head->first == NULL)
{
head->first = pnew;
head->last = pnew;
}
else
{
// 尾插
#if 0
add_tail(head,pnew);
#else
// 头插法
add_head(head, pnew);
#endif
}
head->nodeNumber++;
}
return head;
}
// 判断为空
bool isEmpty(struct headNode *head)
{
return head->nodeNumber == 0;
}
// 显示
void show_list(struct headNode *head)
{
if (isEmpty(head))
{
printf("此表为空!\n");
return;
}
struct node *cur = head->first;
while (cur)
{
printf("%d\t", cur->data);
cur = cur->next;
}
printf("\n");
}
// 创建节点
struct node *create_node(dataType data)
{
struct node *pnew = malloc(sizeof(struct node));
if (pnew == NULL)
return NULL;
pnew->data = data;
pnew->next = NULL;
pnew->pre = NULL;
return pnew;
}
/*
* destroy_list : 销毁链表
* 参数:
* head : 链表管理结构体
* 返回值:
* 无
*/
void destroy_list(struct headNode *head)
{
if (isEmpty(head))
{
printf("此表已经为空!\n");
return;
}
struct node *cur = head->first;
while(cur!=NULL)
{
struct node *p = cur->next;
free(cur);
cur = p;
head->nodeNumber--;
}
head->first = NULL;
head->last = NULL;
}
int main(int argc, char const *argv[])
{
struct headNode *head = create_list();
if (head == NULL)
{
perror("create list failed:");
return -1;
}
printf("原始数据:\n");
show_list(head);
insert_node(head, 2, 888);
printf("将数据888插入:\n");
show_list(head);
delete_node(head,3);
printf("将数据3删除:\n");
show_list(head);
renew_node(head,4,99999);
printf("将数据4改为99999:\n");
show_list(head);
destroy_list(head);
show_list(head);
return 0;
}
3.双向循环链表
// 如果还有节点,首尾相连
if(head->nodeNumber != 0)
{
head->last->next = head->first;
head->first->prev = head->last;
}
4.适用场合
经过单向链表、双向链表的学习,可以总结链表的适用场合:
1.适合用于节点数目不固定,动态变化较大的场合
2.适合用于节点需要频繁插入,删除的场合
3.适合用于对节点查找效率不十分敏感的场合
作业1:
1.实现循环链表删除节点
2.判断链表是否有环
附加题:合并两个链表,按从小到大排序(拆链表,比较节点,插入节点到新链表)