双向循环链表介绍与实现
双向循环链表是一种常见的链表数据结构,相比单链表多了一个指向前驱节点的指针,使得可以双向遍历链表。在循环链表中,最后一个节点的后继节点指向第一个节点,第一个节点的前驱节点指向最后一个节点,形成一个环形结构。
结构定义
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define TYPE int
// 双向循环链表节点结构
typedef struct DuLnode {
TYPE data;
struct DuLnode* prior;
struct DuLnode* next;
} DuLnode;
// 双向循环链表结构
typedef struct DuLinkList {
DuLnode* head; // 头节点
DuLnode* tail; // 尾节点
size_t cnt; // 节点数量
} DuLinkList;
创建节点函数
// 创建双向循环链表节点
DuLnode* create_dulnode(TYPE data) {
DuLnode* dulnode = (DuLnode*)malloc(sizeof(DuLnode));
dulnode->data = data;
dulnode->prior = dulnode;
dulnode->next = dulnode;
return dulnode;
}
创建双向循环链表函数
// 创建双向循环链表
DuLinkList* create_dulinklist(void) {
DuLinkList* list = (DuLinkList*)malloc(sizeof(DuLinkList));
DuLnode* node = create_dulnode(0);
list->head = node;
list->tail = node;
list->cnt = 0;
return list;
}
头部添加函数
// 头部添加节点
bool add_head_list(DuLinkList* list, TYPE data) {
if (NULL == list) return false;
DuLnode* node = create_dulnode(data);
if (list->cnt == 0) {
node->prior = list->head;
node->next = list->head;
list->head->next = node;
list->head->prior = node;
list->tail = node;
} else {
node->prior = list->head;
node->next = list->head->next;
list->head->next->prior = node;
list->head->next = node;
}
list->cnt++;
return true;
}
尾部添加函数
// 尾部添加节点
bool add_tail_list(DuLinkList* list, TYPE data) {
if (NULL == list) return false;
DuLnode* node = create_dulnode(data);
if (list->cnt == 0) {
node->prior = list->head;
node->next = list->head;
list->head->next = node;
list->head->prior = node;
list->tail = node;
} else {
list->tail->next = node;
node->prior = list->tail;
node->next = list->head;
list->head->prior = node;
list->tail = node;
}
list->cnt++;
return true;
}
插入节点函数
// 插入节点
bool insert_list(DuLinkList* list, int index, TYPE data) {
if (index < 0 || index > list->cnt) return false;
if (index == 0) return add_head_list(list, data);
if (index == list->cnt) return add_tail_list(list, data);
DuLnode* n = list->head->next;
DuLnode* node = create_dulnode(data);
for (int i = 1; i < index; i++) {
n = n->next;
}
node->next = n->next;
n->next->prior = node;
node->prior = n;
n->next = node;
list->cnt++;
return true;
}
删除节点函数(按位置和按值)
// 按位置删除节点
bool del_index_list(DuLinkList* list, int index) {
if (index < 0 || index >= list->cnt) return false;
DuLnode* temp = list->head;
if (index == 0) {
temp = list->head->next;
list->head->next = list->head->next->next;
list->head->next->prior = list->head;
} else if (index == list->cnt - 1) {
temp = list->tail;
list->head->prior = list->tail->prior;
list->tail->prior->next = list->head;
} else {
temp = list->head->next;
for (int i = 0; i < index; i++) {
temp = temp->next;
}
temp->prior->next = temp->next;
temp->next->prior = temp->prior;
}
free(temp);
temp = NULL;
list->cnt--;
return true;
}
// 按值删除节点
bool del_value_list(DuLinkList* list, TYPE val) {
if (0 == list->cnt) return false;
bool flag = false;
DuLnode* current = list->head->next;
while (current != list->head) {
DuLnode* temp = current;
current = current->next;
if (temp->data == val) {
temp->prior->next = temp->next;
temp->next->prior = temp->prior;
free(temp);
list->cnt--;
flag = true;
}
}
return flag;
}
修改节点函数(按位置和按值)
// 按位置修改节点值
bool modify_index_list(DuLinkList* list, int index, TYPE val) {
if (index < 0 || index >= list->cnt) return false;
DuLnode* n = list->head->next;
for (int i = 0; i < index; i++) {
n = n->next;
}
n->data = val;
return true;
}
// 按值修改节点值
bool modify_value_list(DuLinkList* list, TYPE old, TYPE new_val) {
if (0 == list->cnt) return false;
DuLnode* n = list->head->next;
for (int i = 0; i < list->cnt; i++) {
if (n->data == old) {
n->data = new_val;
return true;
}
n = n->next;
}
return false;
}
遍历链表函数
这地方我是根据后继节点遍历的,也可以根据前驱节点遍历。
// 遍历链表
void show_list(DuLinkList* list) {
if (0 == list->cnt) {
printf("Empty list.\n");
return;
}
DuLnode* current = list->head->next;
printf("List elements: ");
for (int i = 0; i < list->cnt; i++) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
主函数示例
int main(int argc, const char* argv[]) {
DuLinkList* list = create_dulinklist();
// 头部添加数据
for (int i = 0; i < 10; i++) {
add_head_list(list, i);
}
show_list(list);
// 尾部添加数据
for (int i = 0; i < 10; i++) {
add_tail_list(list, i);
}
show_list(list);
// 插入数据
insert_list(list, 3, 88);
show_list(list);
// 按位置删除数据
del_index_list(list, 3);
show_list(list);
// 按值删除数据
del_value_list(list, 6);
show_list(list);
// 按位置修改数据
modify_index_list(list, 6, 333);
show_list(list);
// 按值修改数据
modify_value_list(list, 0, 99);
show_list(list);
return 0;
}
总结
双向循环链表通过头部和尾部的节点指针以及节点的前驱和后继指针实现了数据的双向存储和双向遍历,这种结构在某些场景中比单链表更加灵活和高效。通过示例代码,我们展示了如何实现双向循环链表的基本操作,包括节点的创建、添加、删除和修改,以及链表的遍历功能。