1.基本概念
需要注意的是,虽然循环链表成环状,但本质上还是链表,因此在循环链表中,依然能够找到头指针和首元节点等。循环链表和普通链表相比,唯一的不同就是循环链表首尾相连,其他都完全一样。
当表中添加或删除数据元素时,你只需要通过 malloc 或 free 函数来申请或释放空间即可,实现起来比较简单。
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; //main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); //插入节点
void delete_node(int pos); //删除节点
void print_linklist(void); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL) // 第一个节点
head = p;
else
tail->next = p;
tail = p;
tail->next = head; //尾节点指针指向头节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
tail->next = head; // 更新尾节点指向的头节点
}
else
{
while(i < pos - 1)
{
pre = pre->next; // pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; // 新节点数据域填充
p->next = pre->next; // 新节点指向的位置为前驱点原来指向位置
pre->next = p; // 前驱点指向的位置变成新节点
if(p->next == head) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
tail->next = head;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == head) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(void) // 结构体型指针
{
struct node *p;
p = head; // 循环的初始条件
do{
printf("%5d", p->elem);
p = p->next;
}while(p != head);
printf("\n");
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
p = head; // 循环的初始条件
do{
if(p->elem == elem)
return 1;
p = p->next;
}while(p != head);
return 0;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
int n;
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
create_list(7);
create_list(8);
create_list(9);
create_list(10);
create_list(11);
create_list(12);
print_linklist();
insert_node(0, 11);
print_linklist();
delete_node(6);
print_linklist();
delete_node(0);
print_linklist();
search(12);
printf("\n");
return 0;
}
2.循环链表的应用-约瑟夫环
约瑟夫问题是个有名的问题:已知n个人(编号1~n表示)围坐在一张圆桌周围,从编号为k的人从1开始报数,数到m的那个人出列,他的下一个人继续从1开始报数,数到m的人继续出列,依次重复下去,直到所有人全部出列。
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; // main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *next;
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); //插入节点
void delete_node(int pos); //删除节点
void print_linklist(void); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->next = NULL;
if(head == NULL) // 第一个节点
head = p;
else
tail->next = p;
tail = p;
tail->next = head; //尾节点指针指向头节点
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新头点节点
{
p->elem = elem;
p->next = head;
head = p;
tail->next = head; // 更新尾节点指向的头节点
}
else
{
while(i < pos - 1)
{
pre = pre->next; // pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; // 新节点数据域填充
p->next = pre->next; // 新节点指向的位置为前驱点原来指向位置
pre->next = p; // 前驱点指向的位置变成新节点
if(p->next == head) // 尾节点
tail = p;
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
tail->next = head;
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next == head) //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(void) // 结构体型指针
{
struct node *p;
p = head; // 循环的初始条件
do{
printf("%5d", p->elem);
p = p->next;
}while(p != head);
printf("\n");
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
p = head; // 循环的初始条件
do{
if(p->elem == elem)
return 1;
p = p->next;
}while(p != head);
return 0;
}
main.c
#include <stdio.h>
#include "linklist.h"
#include <stdlib.h>
int main(void)
{
int n, k, m;
int i;
struct node *p;
struct node *q;
printf("please enter the number of person:");
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
create_list(i);
}
print_linklist();
p = head;
printf("please enter the start number:");
scanf("%d", &k);
while(--k)
{
p = p->next;
}
printf("please enter the m:");
scanf("%d", &m);
if(1 == m)
{
for(i = 0; i < n; i++)
{
printf("%3d", p->elem);
p = p->next;
}
printf("\n");
}
else
{
while(n--) //
{
for(i = 1; i < m - 1; i++) // 找前驱点
p = p->next;
q = p;
p = p->next;
printf("%3d", p->elem);
q->next = p->next;
//free(p);
p = p->next;
}
printf("\n");
return 0;
}
}
3.双向链表
从名字上理解双向链表,即链表是 "双向" 的,如图 1 所示:
“双向”指的是各节点之间的逻辑关系是双向的,头指针通常只设置一个。
从图 1 中可以看到,双向链表中各节点包含以下 3 部分信息(如图 2 所示):
1.指针域:用于指向当前节点的直接前驱节点;
2.数据域:用于存储数据元素。
3.指针域:用于指向当前节点的直接后继节点;
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; // main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *pre; // 前驱点地址
struct node *next; // 下一个地址
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); //插入节点
void delete_node(int pos); //删除节点
void print_linklist(void); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->pre = NULL;
p->next = NULL;
if(head == NULL) // 第一个节点,指针都是NULL
head = p;
else
{
tail->next = p; //
p->pre = tail;
}
tail = p;
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新双向链表的头节点
{
p->elem = elem;
p->next = head;
head->pre = p;
p->pre = NULL;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; // pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; // 新节点数据域填充
p->pre = pre;
p->next = pre->next; // 新节点指向的位置为前驱点原来指向位置
if(p->next != NULL)
pre->next->pre = p;
pre->next = p; // 前驱点指向的位置变成新节点
if(p->next == NULL)
{
tail = p;
}
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
head->pre = NULL; // 新头的前驱点为空
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next != NULL)
p->next->pre = pre;
else //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(void) // 结构体型指针
{
struct node *p;
for(p = head; p; p = p->next)
printf("%5d", p->elem);
printf("\n");
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
int n;
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
create_list(7);
create_list(8);
create_list(9);
create_list(10);
create_list(11);
create_list(12);
print_linklist();
insert_node(0, 11);
print_linklist();
delete_node(6);
print_linklist();
delete_node(0);
print_linklist();
search(12);
printf("\n");
return 0;
}
4.双向链表的应用-逆置链表
linklist.h
#ifndef LINKLIST_H__
#define LINKLIST_H__
#include <stdio.h>
extern struct node *tail;
extern struct node *head; // main.c 也要使用 head tail
//节点结构体
struct node
{
unsigned int elem;
struct node *pre; // 前驱点地址
struct node *next; // 下一个地址
}; //注意;
//函数声明
void create_list(unsigned int elem); //节点的创建
void insert_node(int pos, unsigned int elem); //插入节点
void delete_node(int pos); //删除节点
void print_linklist(void); //输出链表
int search(unsigned int elem); //查询链表中的某个数值
void reverse_printf_linklist(void); //逆打印
#endif
linklist.c
#include "linklist.h"
#include <stdlib.h>
struct node *head = NULL; // 头指针
struct node *tail = NULL; // 尾指针
//节点的创建
void create_list(unsigned int elem)
{
struct node *p = (struct node *)malloc(sizeof(struct node));
p->elem = elem;
p->pre = NULL;
p->next = NULL;
if(head == NULL) // 第一个节点,指针都是NULL
head = p;
else
{
tail->next = p; //
p->pre = tail;
}
tail = p;
}
// 插入节点
void insert_node(int pos, unsigned int elem)
{
struct node *pre; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
struct node *p = (struct node *)malloc(sizeof(struct node)); // 创建节点
if(pos == 0) //如果更新双向链表的头节点
{
p->elem = elem;
p->next = head;
head->pre = p;
p->pre = NULL;
head = p;
}
else
{
while(i < pos - 1)
{
pre = pre->next; // pre是一个指针,pre->next是该指针所指向结构的一个成员
i++;
}
p->elem = elem; // 新节点数据域填充
p->pre = pre;
p->next = pre->next; // 新节点指向的位置为前驱点原来指向位置
if(p->next != NULL)
pre->next->pre = p;
pre->next = p; // 前驱点指向的位置变成新节点
if(p->next == NULL)
{
tail = p;
}
}
}
//删除节点
void delete_node(int pos)
{
struct node *pre, *p; // 前趋点
pre = head; // 前趋点从头开始
int i = 0;
if(pos == 0) // 如果删除头结点
{
head = head->next;
head->pre = NULL; // 新头的前驱点为空
free(pre);
}
else
{
while(i < pos-1) //更新前趋点直到要删除的前一个
{
pre = pre->next;
i++;
}
p = pre->next;
pre->next = p->next;
if(p->next != NULL)
p->next->pre = pre;
else //如果删除尾节点
tail = pre;
free(p);
}
}
//输出链表
void print_linklist(void) // 结构体型指针
{
struct node *p;
for(p = head; p; p = p->next)
printf("%5d", p->elem);
printf("\n");
}
//查询链表中的某个数值
int search(unsigned int elem)
{
struct node *p;
for(p = head; p; p = p->next)
if(p->elem == elem)
return 1;
return 0;
}
void reverse_printf_linklist(void)
{
struct node *p;
for(p = tail; p; p = p->pre) //直到p->pre = NULL
printf("%5d", p->elem);
printf("\n");
}
main.c
#include <stdio.h>
#include "linklist.h"
int main(void)
{
int n;
// 创建链表1
create_list(1);
create_list(2);
create_list(3);
create_list(4);
create_list(5);
create_list(6);
create_list(7);
create_list(8);
create_list(9);
create_list(10);
create_list(11);
create_list(12);
print_linklist();
reverse_printf_linklist();
return 0;
}