目录
可以看出,双向链表中各节点包括:
① 指针域:指向当前节点的直接前驱(前一个)节点;
② 数据域:存储数据元素;
③ 指针域:指向当前节点的直接后继(下一个)节点。
双链表的结构:
typedef struct Node
{
struct Node *front; //指向直接前驱
int data; //可根据需要调整data的数据类型
struct Node *next; //指向直接后继
}Node;
创建双向链表的过程中,每一个新节点都要和前驱节点之间建立两次链接:
- 将新节点的 front 指针指向前驱节点;
- 将前驱节点的 next 指针指向新节点。
创建双向链表:
对于创建双向链表,我们需要创建头节点再逐步进行添加。双向链表的头节点是有数据的,这与单链表是不同的。
对于逐步添加数据,我们的做法是:开辟一段新的内存空间作为新的节点,为这个节点的data进行赋值,然后将已成链表的上一个节点的next指针指向自身,自身的front指针指向上一个节点。
过程可以拆分为:
创建头节点 -> 创建一个新的节点 -> 将头节点和新节点相互链接 -> 再度创建新节点
//创建双链表
Node* createList(Node* head)
{
int num, pos = 1, data;
//三个变量分别代表节点数量、当前位置、输入的数据
printf("请输入创建节点的大小:\n");
scanf("%d", &num);
if(num<1)
{
return NULL; //输入非法直接结束
}
//头结点创建并初始化,链表的头指针为head
head = (Node*)malloc(sizeof(Node));
head->front = NULL;
head->next = NULL;
printf("输入第%d个数据:\n", pos++);
scanf("%d", &data);
head->data = data;
//声明一个指向头节点的指针,方便后期向链表中添加新创建的节点
Node* list = head;
while(pos<=num)
{
//创建新的节点 body 并初始化
Node* body = (Node*)malloc(sizeof(Node));
body->front = NULL;
body->next = NULL;
printf("请输入第%d个数据:\n", pos++);
scanf("%d", &data);
body->data = data;
//新节点与链表最后一个节点建立关系
list->next = body;
body->front = list;
//list永远指向链表中的最后一个节点
list = list->next;
}
//返回新创建的链表
return head;
}
双向链表查找节点:
通常,双向链表同单链表一样,都仅有一个头指针。因此,双链表查找指定元素的实现同单链表类似,都是从表头依次遍历表中元素。
//head为原双链表,data表示被查找元素
int selectNode(Node* head, int data) {
//新建一个指针t,初始化为头指针 head
Node* t = head;
int i = 1;
while (t) {
if (t->data == data) {
return i;
}
i++;
t = t->next;
}
//程序执行至此处,表示查找失败
return -1;
}
双向链表更改节点:
通过遍历找到存储有该数据元素的结点,直接更改其数据域即可。
//更新函数,其中,add 表示更改结点在双链表中的位置,newdata 为新数据的值
Node* amendNode(Node* p, int add, int newdata) {
int i = 0;
Node* temp = p;
//遍历到被删除结点
for (i = 1; i < add; i++) {
temp = temp->next;
if (temp == NULL) {
printf("更改位置有误!\n");
break;
}
}
if (temp) {
temp->data = newdata;
}
return p;
}
双向链表删除节点:
过程:
选择需要删除的结点,选中这个结点的前一个结点,将前一个结点的next指针指向自己的下一个结点,同时,选中该节点的下一个结点,将下一个结点的front指针修改指向为自己的上一个结点,在这样的指针修改操作之后,我们释放删除结点,归还空间给内存。
Node* deleteNode(Node* head, int data) { //输入的参数分别为进行此操作的双链表,需要删除的数据
Node* temp = head;
while (temp) { //遍历链表
//判断当前结点中数据域和data是否相等,若相等,摘除该结点
if (temp->data == data) {
temp->front->next = temp->next;
temp->next->front = temp->front;
free(temp);
printf("删除成功\n");
return head;
}
temp = temp->next;
}
printf("链表中无该数据元素\n");
return head;
}
遍历双链表,并打印元素数据:
利用next指针逐步向后进行索引即可。
void display(Node* head) {
Node* temp = head;
while (temp) {
if (temp->next == NULL) {
printf("%d\n", temp->data);
}
else {
printf("%d->", temp->data);
}
temp = temp->next;
}
}
双向链表插入节点:
过程:
首先需要创建一个独立的结点并通过malloc操作开辟相应的空间,其次我们选中这个新创建的独立节点,将其的front指针指向所需插入位置的前一个结点,同时,前一个结点的next指针修改成指向该新的结点;同理,该新结点的next指针将会指向一个原本的下一个结点,而下一个结点的front指针修改为指向新结点。
Node* insertNode(Node* head, int data, int add)
//上边三个参数分别是:要插入的双链表,插入的数据,插入的位置
{
//新建节点并初始化
Node* temp = (Node*)malloc(sizeof(Node));
temp->data = data;
temp->front = NULL;
temp->next = NULL;
//插入到链表头,要特殊考虑
if(add = 1){
temp->next = head;
head->front = temp;
head = temp;
}else{
Node* body = head;
//找到要插入位置的前一个节点
for(int i=1; i<add-1; i++){
body = body->next;
}
//判断条件为真,说明插入位置为链表尾
if(body->next == NULL){
body->next = temp;
temp->front = body;
}else{
body->next->front = temp;
temp->next = body->next;
body->next = temp;
temp->front = body;
}
}
return head;
}
使用双向链表对数据进行“增删查改”的完整操作代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
struct Node* front;
int data;
struct Node* next;
}Node;
//双链表的创建
Node* createList(Node* head);
//双链表插入元素,add表示插入位置
Node* insertNode(Node* head, int data, int add);
//双链表删除指定元素
Node* deleteNode(Node* head, int data);
//双链表中查找指定元素
int selectNode(Node* head, int data);
//双链表中更改指定位置节点中存储的数据,add表示更改位置
Node* amendNode(Node* p, int add, int newdata);
//输出双链表的实现函数
void display(Node* head);
int main() {
Node* head = NULL;
//创建双链表
printf("初始链表为:\n");
head = createList(head);
display(head);
//在表中第 3 的位置插入元素 7
printf("在表中第 3 的位置插入新元素 7:\n");
head = insertNode(head, 7, 3);
display(head);
//表中删除元素 2
printf("删除元素 2:\n");
head = deleteNode(head, 2);
display(head);
printf("元素 3 的位置是:%d\n", selectNode(head, 3));
//表中第 3 个节点中的数据改为存储 6
printf("将第 3 个节点存储的数据改为 6:\n");
head = amendNode(head, 3, 6);
display(head);
return 0;
}
Node* createList(Node* head) {
int i = 0;
Node* list = NULL;
head = (Node*)malloc(sizeof(Node));
head->front = NULL;
head->next = NULL;
head->data = 1;
list = head;
for (i = 2; i <= 3; i++) {
Node* body = (Node*)malloc(sizeof(Node));
body->front = NULL;
body->next = NULL;
body->data = i;
list->next = body;
body->front = list;
list = list->next;
}
return head;
}
Node* insertNode(Node* head, int data, int add) {
//新建数据域为data的结点
Node* temp = (Node*)malloc(sizeof(Node));
temp->data = data;
temp->front = NULL;
temp->next = NULL;
//插入到链表头,要特殊考虑
if (add == 1) {
temp->next = head;
head->front = temp;
head = temp;
}
else {
int i = 0;
Node* body = head;
//找到要插入位置的前一个结点
for (i = 1; i < add - 1; i++) {
body = body->next;
if (body == NULL) {
printf("插入位置有误\n");
break;
}
}
if (body) {
//判断条件为真,说明插入位置为链表尾
if (body->next == NULL) {
body->next = temp;
temp->front = body;
}
else {
body->next->front = temp;
temp->next = body->next;
body->next = temp;
temp->front = body;
}
}
}
return head;
}
//删除结点的函数,data为要删除结点的数据域的值
Node* deleteNode(Node* head, int data) { //输入的参数分别为进行此操作的双链表,需要删除的数据
Node* temp = head;
//遍历链表
while (temp) {
//判断当前结点中数据域和data是否相等,若相等,摘除该结点
if (temp->data == data) {
temp->front->next = temp->next;
temp->next->front = temp->front;
free(temp);
return head;
}
temp = temp->next;
}
printf("链表中无该数据元素\n");
return head;
}
//head为原双链表,data表示被查找元素
int selectNode(Node* head, int data) {
//新建一个指针t,初始化为头指针 head
Node* t = head;
int i = 1;
while (t) {
if (t->data == data) {
return i;
}
i++;
t = t->next;
}
//程序执行至此处,表示查找失败
return -1;
}
//更新函数,其中,add 表示更改结点在双链表中的位置,newdata 为新数据的值
Node* amendNode(Node* p, int add, int newdata) {
int i = 0;
Node* temp = p;
//遍历到被删除结点
for (i = 1; i < add; i++) {
temp = temp->next;
if (temp == NULL) {
printf("更改位置有误!\n");
break;
}
}
if (temp) {
temp->data = newdata;
}
return p;
}
//输出链表的功能函数
void display(Node* head) {
Node* temp = head;
while (temp) {
if (temp->next == NULL) {
printf("%d\n", temp->data);
}
else {
printf("%d->", temp->data);
}
temp = temp->next;
}
}