链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表的最后一个节点指向空值,表示链表的结束。
循环链表是一种特殊的链表,它的最后一个节点不是指向空值,而是指向链表的头节点,形成一个闭环。换句话说,循环链表的头节点的前驱节点是尾节点,尾节点的后继节点是头节点。循环链表可以通过在链表的末尾将尾节点的指针指向头节点来创建。这样一来,可以通过尾节点的指针遍历整个链表,而不需要额外的指针变量。循环链表的优点是可以很方便地实现环形结构,比如循环队列和循环缓冲区。此外,循环链表也可以解决一些特殊的问题,比如约瑟夫环问题。需要注意的是,在处理循环链表时,需要特别注意循环的终止条件,以免陷入死循环。同时,插入、删除节点等操作也需要考虑循环链表的特殊性,确保链表仍然保持循环结构。
总之,循环链表是一种特殊的链表,具有闭环的特点。它可以方便地实现环形结构,并解决一些特殊的问题。在处理循环链表时,需要注意循环的终止条件和操作的特殊性。
单链表
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void insert(struct Node** head, int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
if (*head == NULL) {
*head = newNode;
} else {
struct Node* current = *head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
}
}
void display(struct Node* head) {
struct Node* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
void delete(struct Node** head, int data) {
if (*head == NULL) {
return;
}
struct Node* current = *head;
if (current->data == data) {
*head = current->next;
free(current);
return;
}
struct Node* prev = NULL;
while (current != NULL && current->data != data) {
prev = current;
current = current->next;
}
if (current == NULL) {
return;
}
prev->next = current->next;
free(current);
}
int main() {
struct Node* head = NULL;
insert(&head, 1); // 插入节点 1
insert(&head, 2); // 插入节点 2
insert(&head, 3); // 插入节点 3
insert(&head, 4); // 插入节点 4
insert(&head, 5); // 插入节点 5
printf("Original List: ");
display(head);
delete(&head, 3); // 删除节点 3
printf("List after deleting 3: ");
display(head);
return 0;
}
循环链表
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void insert(struct Node** head, int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
if (*head == NULL) {
*head = newNode;
newNode->next = *head;
} else {
struct Node* current = *head;
while (current->next != *head) {
current = current->next;
}
current->next = newNode;
newNode->next = *head;
}
}
void display(struct Node* head) {
struct Node* current = head;
do {
printf("%d ", current->data);
current = current->next;
} while (current != head);
printf("\n");
}
void delete(struct Node** head, int data) {
if (*head == NULL) {
return;
}
struct Node* current = *head;
struct Node* prev = NULL;
while (current->next != *head) {
if (current->data == data) {
if (prev == NULL) {
struct Node* last = *head;
while (last->next != *head) {
last = last->next;
}
*head = current->next;
last->next = *head;
free(current);
return;
} else {
prev->next = current->next;
free(current);
return;
}
}
prev = current;
current = current->next;
}
if (current->data == data) {
if (prev == NULL) {
*head = NULL;
free(current);
return;
} else {
prev->next = *head;
free(current);
return;
}
}
}
int main() {
struct Node* head = NULL;
insert(&head, 1); // 插入节点 1
insert(&head, 2); // 插入节点 2
insert(&head, 3); // 插入节点 3
insert(&head, 4); // 插入节点 4
insert(&head, 5); // 插入节点 5
printf("Original List: ");
display(head);
delete(&head, 3); // 删除节点 3
printf("List after deleting 3: ");
display(head);
return 0;
}
单向链表和循环链表的主要区别在于插入和删除节点时对尾节点指针的处理。
对于链表,插入和删除节点时,需要考虑头节点和尾节点的特殊情况。在插入节点时,如果在头部插入,只需要将新节点的next指针指向原来的头节点,然后更新头节点为新节点。在尾部插入时,需要遍历链表找到最后一个节点,然后将最后一个节点的next指针指向新节点。在删除节点时,如果删除头节点,只需要将头节点更新为下一个节点。对于其他位置的节点,需要遍历链表找到要删除的节点的前一个节点,然后将前一个节点的next指针指向要删除节点的下一个节点。
而对于循环链表,插入和删除节点时需要特别处理尾节点的指针。在插入节点时,如果在头部插入,需要将新节点的next指针指向原来的头节点,然后更新尾节点的next指针指向新节点,最后更新头节点为新节点。在尾部插入时,需要遍历链表找到尾节点,然后将尾节点的next指针指向新节点,新节点的next指针指向头节点。在删除节点时,如果删除头节点,需要更新尾节点的next指针指向头节点的下一个节点,然后更新头节点为下一个节点。对于其他位置的节点,需要遍历链表找到要删除的节点的前一个节点,然后将前一个节点的next指针指向要删除节点的下一个节点。
除了插入和删除节点时对尾节点指针的处理外,链表和循环链表的基本操作是相似的。都包括在末尾插入节点、在头部插入节点、在指定位置插入节点、删除指定位置的节点和遍历链表。
Demo
#include <stdio.h>
#include <string.h>
#include "SingleLinkList.h" // 引入单链表头文件
#include "welcome.h" // 引入欢迎信息头文件
int main(int argc, char* argv[]) // 主函数
{ SingleLinkList *Head; // 定义单链表头指针
DataType x; // 定义数据类型变量
x int i,m,n,cmd; // 定义循环计数器和命令选项
for(i=0;i<strlen(welcome);i++) // 输出欢迎信息
{
printf("%c",welcome[i]);
for(m=0;m<1000;m++) // 延时
for(n=0;n<1000;n++)
{
;
}
}
printf("-----------简单链表演示程序----------\n"); // 输出菜单
do
{
printf("1. 初始化链表表\n");
printf("2. 插入元素(头插法)\n");
printf("3. 插入元素(尾插法)\n");
printf("4. 插入元素(在位置i插入)\n");
printf("5. 查找元素x\n");
printf("6. 求链表长度\n");
printf("7. 输出链表\n");
printf("8. 删除元素\n");
printf("10. 帮助\n");
printf("0. 退出\n");
printf("请输入您要进行的操作(1~6,0退出):");
scanf("%d", &cmd); // 输入命令选项
switch(cmd) // 根据命令选项进行相应操作
{
case 1:
if(!init(&Head)) // 初始化链表
{
printf("链表已初始化!\n");
}
break;
case 2:
printf("请输入插入元素x:x=");
scanf("%d",&x);
if(!insert_head(&Head,x)) // 头插法插入元素
{
printf("元素(%d)已插入\n", x);
}
break;
case 3:
printf("请输入插入元素x:x=");
scanf("%d",&x);
if(!insert_tail(&Head,x)) // 尾插法插入元素
{
printf("元素(%d)已插入\n", x);
}
break;
case 4:
printf("请输入插入元素位置i和元素x(i,x):");
scanf("%d,%d", &i, &x);
if(!insert(&Head, i, x)) // 在位置i插入元素
{
printf("已在位置(%d)插入元素(%d)!\n",i, x);
}
break;
case 5:
printf("请输入要查找的元素x:");
scanf("%d", &x);
if(i = find(Head,x)) // 查找元素x
{
printf("元素%d存在,在链表位置%d.\n", x, i);
}
else
{
printf("在链表中未找到元素x。\n");
}
break;
case 6:
printf("链表的长度为:%d\n", length(Head)); // 求链表长度
break;
case 7:
print(Head); // 输出链表
break;
case 8:
printf("请输入要删除的元素x:");
scanf("%d", &x);
if(!delete(&Head, x)) // 删除元素x
{
printf("元素x【%d】已删除!\n", x);
}
break;
case 10:
printf(" 本程序为链表的演示程序,有设计开发,程序完成了环形链表功能!\n");
break;
}
}while(cmd != 0);
return 0;
Demo running result
-----------简单链表演示程序----------
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):1
链表已初始化!
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):2
请输入插入元素x:x=3
元素(3)已插入
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):3
请输入插入元素x:x=5
元素(5)已插入
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):3
请输入插入元素x:x=7
元素(7)已插入
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):4
请输入插入元素位置i和元素x(i,x):3,5
已在位置(3)插入元素(5)!
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):5
请输入要查找的元素x:5
元素5存在,在链表位置3.
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):6
链表的长度为:4
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):7
Node[1]. = 3
Node[2]. = 3
Node[3]. = 5
Node[4]. = 5
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
请输入您要进行的操作(1~6,0退出):8
请输入要删除的元素x:3
元素x【3】已删除!
1. 初始化链表表
2. 插入元素(头插法)
3. 插入元素(尾插法)
4. 插入元素(在位置i插入)
5. 查找元素x
6. 求链表长度
7. 输出链表
8. 删除元素
10. 帮助
0. 退出
内容小结
-
循环链表是一种特殊的链表结构,它的最后一个节点的指针指向头节点,形成一个环形结构。
-
循环链表可以通过头指针来访问整个链表,而不需要遍历整个链表。
-
循环链表的操作和单链表类似,包括初始化链表、插入元素、删除元素、查找元素等操作。
-
循环链表可以简化一些操作,例如遍历整个链表时不需要判断是否到达链表末尾,而是通过判断指针是否指向头节点来结束遍历。
-
循环链表可以用于实现队列、栈等数据结构,也可以用于解决一些特殊的问题,例如约瑟夫环问题等。
-
在实现循环链表时,需要注意插入和删除元素时尾节点的指针指向。
-
循环链表在内存分配和释放上相对于单链表更加复杂,需要注意避免内存泄漏和野指针的问题。