一. 循环链表介绍
① 循环链表的定义
- 常规的链表,有头部,头部指向链表中的第一个节点,尾部最后一个节点指向
NULL - 循环链表就是将尾部指向头部
- 所以一个空的循环链表,它的next不再指向
NULL ,而是指向自身. - 正常链表的遍历结束条件是 NULL,而循环链表的遍历结束条件是
next 是否为head
② 循环链表的分类
- 单向循环链表. 头部->尾部->头部.
- 双向循环链表. 头部 <=> 尾部 <=> 头部
③ 单向循环链表的实现
函数声明头文件的实现 CircleLinkList.h
#ifndef CIRCLE_LINK_LIST
#define CIRCLE_LINK_LIST
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIST_IS_EMPTY 1
#define LIST_NOT_EMPTY 0
typedef struct CircleLinkNode
{
struct CircleLinkNode *next;
} CircleLinkNode;
typedef struct CircleLinkList
{
CircleLinkNode head;
int size;
}CircleLinkList;
typedef int (*COMPARE_PTR)(CircleLinkNode *, CircleLinkNode *);
typedef void (*PRINT_PTR)(CircleLinkNode *);
CircleLinkList *init_circle_linklist();
void insert_circle_linklist(CircleLinkList *list, int pos, CircleLinkNode *data);
CircleLinkNode *front_circle_linklist(CircleLinkList *list);
void remove_by_pos_circle_linklist(CircleLinkList *list, int pos);
void remove_by_val_circle_linklist(CircleLinkList *list, CircleLinkNode *data,
COMPARE_PTR compare);
int get_size_circle_linklist(CircleLinkList *list);
int find_circle_linklist(CircleLinkList *list, CircleLinkNode *data, COMPARE_PTR compare);
void print_circle_linklist(CircleLinkList *list, PRINT_PTR print);
int is_empty_circle_linklist(CircleLinkList *list);
void free_space_circle_linklist(CircleLinkList *list);
void clear_circle_linklist(CircleLinkList *list);
#endif
实现部分.c
#include "CircleLinkList.h"
typedef int (*COMPARE_PTR)(CircleLinkNode *, CircleLinkNode *);
typedef void (*PRINT_PTR)(CircleLinkList *);
CircleLinkList *init_circle_linklist()
{
CircleLinkList *list = (CircleLinkList *)malloc(sizeof(CircleLinkList));
if (list != NULL)
{
list->head.next = &(list->head);
list->size = 0;
}
return list;
}
void insert_circle_linklist(CircleLinkList *list, int pos, CircleLinkNode *data)
{
if (pos <0 || pos > list->size)
{
pos = list->size;
}
if (list != NULL && data != NULL)
{
CircleLinkNode *pCurrent = &(list->head);
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
data->next = pCurrent->next;
pCurrent->next = data;
list->size++;
}
}
CircleLinkNode *front_circle_linklist(CircleLinkList *list)
{
if (list != NULL)
{
CircleLinkNode *node = list->head.next;
return node;
}
return NULL;
}
void remove_by_pos_circle_linklist(CircleLinkList *list, int pos)
{
if (pos < 0 || pos >= list->size)
{
return;
}
if (list != NULL)
{
CircleLinkNode *pCurrent = &(list->head);
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
pCurrent->next = pCurrent->next->next;
list->size--;
}
}
void remove_by_val_circle_linklist(CircleLinkList *list, CircleLinkNode *data, COMPARE_PTR compare)
{
CircleLinkNode *pPre = &(list->head);
CircleLinkNode *pCurrent = list->head.next;
int i = 0;
for (int i = 0; i < list->size; i++)
{
if (compare(pCurrent, data) == 0)
{
pPre->next = pCurrent->next;
break;
}
pPre = pPre->next;
pCurrent = pCurrent->next;
}
}
int get_size_circle_linklist(CircleLinkList *list)
{
if (list != NULL)
{
return list->size;
}
return -1;
}
int find_circle_linklist(CircleLinkList *list, CircleLinkNode *data, COMPARE_PTR compare)
{
if (data != NULL && list != NULL)
{
CircleLinkNode* pCurrent = list->head.next;
int index = -1;
while (pCurrent != (&list->head))
{
if (compare(data, pCurrent) == 0)
{
break;
}
pCurrent = pCurrent->next;
index++;
}
return index;
}
else
{
return -1;
}
}
void print_circle_linklist(CircleLinkList *list, PRINT_PTR print)
{
if (list != NULL)
{
CircleLinkNode *pCurrent = list->head.next;
while (pCurrent != &(list->head))
{
print(pCurrent);
pCurrent = pCurrent->next;
}
}
}
int is_empty_circle_linklist(CircleLinkList *list)
{
if (list != NULL)
{
return list->size == 0 ? LIST_IS_EMPTY : LIST_NOT_EMPTY;
}
return LIST_IS_EMPTY;
}
void free_space_circle_linklist(CircleLinkList *list)
{
if (list != NULL)
{
free(list);
}
}
void clear_circle_linklist(CircleLinkList *list)
{
list->head.next = &(list->head);
list->size = 0;
}
测试部分
#include "CircleLinkList.h"
typedef struct
{
CircleLinkNode *node;
char name[64];
int age;
} Person;
void my_print(CircleLinkNode *node)
{
Person *p1 = (Person *)node;
printf("Name: %s,Age: %d\n", p1->name, p1->age);
}
int my_compare(CircleLinkNode *node1, CircleLinkNode *node2)
{
Person *p1 = (Person *)node1;
Person *p2 = (Person *)node2;
if (strcmp(p1->name, p2->name) == 0 && p1->age == p2->age)
{
return 0;
}
return -1;
}
int main()
{
Person p1, p2, p3, p4, p5;
int copyLen = sizeof(p1.name) / sizeof(p1.name[0]);
strcpy_s(p1.name, copyLen, "AAAA");
strcpy_s(p2.name, copyLen, "BBBB");
strcpy_s(p3.name, copyLen, "CCCC");
strcpy_s(p4.name, copyLen, "DDDD");
strcpy_s(p5.name, copyLen, "EEEE");
p1.age = 10;
p2.age = 20;
p3.age = 30;
p4.age = 40;
p5.age = 50;
CircleLinkList *list = init_circle_linklist();
insert_circle_linklist(list, 0, (CircleLinkNode *)&p1);
insert_circle_linklist(list, 0, (CircleLinkNode *)&p2);
insert_circle_linklist(list, 0, (CircleLinkNode *)&p3);
insert_circle_linklist(list, 0, (CircleLinkNode *)&p4);
insert_circle_linklist(list, 0, (CircleLinkNode *)&p5);
print_circle_linklist(list,my_print);
printf("______________________________\n");
remove_by_pos_circle_linklist(list, 2);
print_circle_linklist(list, my_print);
printf("______________________________\n");
remove_by_val_circle_linklist(list,(CircleLinkList*)&p2,my_print);
printf("------------------------------------\n");
print_circle_linklist(list, my_print);
int pos = find_circle_linklist(list, (CircleLinkNode *)(&p2), my_compare);
if (pos == 1)
{
printf("未找到!\n");
}
else
{
printf("找到了,位置是: %d\n", pos);
}
pos = find_circle_linklist(list, (CircleLinkNode *)(&p1),my_compare);
if (pos == 1)
{
printf("未找到!\n");
}
else
{
printf("找到了,位置是: %d\n", pos);
}
printf("list大小为: %d\n", list->size);
Person *frontPerson = (Person *)front_circle_linklist(list);
printf("list的第一个元素: Name:%s,Age:%d \n", frontPerson->name, frontPerson->age);
clear_circle_linklist(list);
printf("清空之后的大小为: %d", list->size);
free_space_circle_linklist(list);
system("pause");
return 0;
}
二. 双向循环链表
① 为什么需要双向链表
- 单向链表维护一个指针域,指向下一个元素,在某些情况下,单链表并不是最优的存储结构.
- 比如算法中需要大量查找某个节点的前驱节点,使用单链表需要从头遍历才能找到.
- 单链表适合从前往后找,如果有些数据需要从后往前的话,就需要双向链表.
② 双向链表的构成
双向链表的指针域有两个一个是保存的它的前驱节点,一个保存的是它的后继节点
带头节点的双向链表
节点描述
- 指针域 prior: 用于指向当前节点的直接前驱节点
- 数据域 data: 用于存储数据元素
- 指针域 next: 用于指向当前节点的后继节点
双向循环链表的头节点
需要将头节点的prior 和next 都指向自己
实现 头文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct LinkNode
{
void* data;
struct LinkNode *prior;
struct LinkNode *next;
} LinkNode;
typedef struct LinkList
{
struct LinkNode *head;
int size;
} LinkList;
typedef void (*PRINT_PTR)(void *);
typedef int (*COMPARE_PTR)(void *, void *);
LinkList* init_linklist();
void push_front_linklist(LinkList *list, void *data);
void push_back_linklist(LinkList *list, void *data);
void insert_by_pos_linklist(LinkList *list, int pos, void *data);
void* pop_front_linklist(LinkList *list);
void* pop_back_linklist(LinkList *list);
void *get_by_pos_linklist(LinkList *list, int pos);
int get_by_val_linklist(LinkList *list, void *data,COMPARE_PTR mycompare);
void remove_by_pos_linklist(LinkList *list, int pos);
void print_linklist(LinkList *list, PRINT_PTR print);
void free_space_linklist(LinkList *list);
实现文件
#include "CircleListTwo.h"
LinkList* init_linklist()
{
LinkList *list = (LinkList *)malloc(sizeof(LinkList));
if (list != NULL)
{
LinkNode *node = (LinkNode *)malloc(sizeof(LinkNode));
if (node != NULL)
{
list->head = node;
node->data = NULL;
node->prior = node;
node->next = node;
list->size = 0;
}
}
return list;
}
void push_front_linklist(LinkList *list, void *data)
{
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
if (newNode != NULL && list != NULL && data != NULL)
{
LinkNode *firstNode = list->head->next;
newNode->next = firstNode;
newNode->prior = list->head;
newNode->data = data;
list->head->next = newNode;
firstNode->prior = newNode;
list->size++;
}
}
void push_back_linklist(LinkList *list, void *data)
{
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
if (newNode != NULL && list != NULL && data != NULL)
{
LinkNode *lastNode = list->head->prior;
newNode->prior = lastNode;
newNode->next = list->head;
newNode->data = data;
list->head->prior = newNode;
lastNode->next = newNode;
list->size++;
}
}
void insert_by_pos_linklist(LinkList *list, int pos, void *data)
{
if (pos < 0 || pos > list->size)
{
pos = list->size;
}
if (list != NULL)
{
LinkNode *pCurrent = list->head;
for (int i = 0; i < pos; i++)
{
pCurrent = pCurrent->next;
}
LinkNode *newNode = (LinkNode *)malloc(sizeof(LinkNode));
if (newNode != NULL && data != NULL)
{
newNode->prior = pCurrent;
newNode->next = pCurrent->next;
newNode->data = data;
pCurrent->next->prior = newNode;
pCurrent->next = newNode;
list->size++;
}
}
}
void *pop_front_linklist(LinkList *list)
{
if (list != NULL)
{
LinkNode *nodeDel = list->head->next;
list->head->next = nodeDel->next;
nodeDel->next->prior = list->head;
void *data = nodeDel->data;
free(nodeDel);
list->size--;
return data;
}
}
void* pop_back_linklist(LinkList *list)
{
LinkNode *nodeDel = list->head->prior;
list->head->prior = nodeDel->prior;
nodeDel->prior->next = list->head;
void *data = nodeDel->data;
free(nodeDel);
list->size--;
return data;
}
void *get_by_pos_linklist(LinkList *list, int pos)
{
if (pos < 0 || pos >= list->size)
{
return NULL;
}
if (list != NULL)
{
LinkNode *pCurrent = list->head;
for (int i = 0; i <= pos; i++)
{
pCurrent = pCurrent->next;
}
return pCurrent->data;
}
return NULL;
}
int get_by_val_linklist(LinkList *list, void *data,COMPARE_PTR my_compare)
{
if (list != NULL && data != NULL)
{
LinkNode *pCurrent = list->head->next;
int indexFinded = -1;
for (int i = 0; i < list->size; i++)
{
if (my_compare(pCurrent->data, data) == 0)
{
indexFinded = i;
break;
}
}
return indexFinded;
}
}
void remove_by_pos_linklist(LinkList *list, int pos)
{
if (pos < 0 || pos >= list->size)
{
return;
}
if (list != NULL)
{
LinkNode *pCurrent = list->head;
for (int i = 0; i <= pos; i++)
{
pCurrent = pCurrent->next;
}
pCurrent->prior->next = pCurrent->next;
pCurrent->next->prior = pCurrent->prior;
free(pCurrent);
list->size--;
}
}
void print_linklist(LinkList *list, PRINT_PTR print)
{
if (list != NULL)
{
LinkNode *pCurrent = list->head->next;
for (int i = 0; i < list->size; i++)
{
if (pCurrent == list->head)
{
break;
}
print(pCurrent->data);
pCurrent = pCurrent->next;
}
}
else
{
return;
}
}
void free_space_linklist(LinkList *list)
{
if (list != NULL)
{
LinkNode *pHead = list->head;
LinkNode *pCurrent = pHead->next;
while (pCurrent != pHead)
{
LinkNode *pNext = pCurrent->next;
free(pCurrent);
pCurrent = pNext;
}
free(pHead);
free(list);
}
}
测试代码
#include "CircleListTwo.h"
typedef struct Person
{
char name[64];
int age;
}Person;
void my_print(void *v)
{
Person *p = (Person *)v;
printf("姓名: %s, 年龄: %d\n", p->name, p->age);
}
int my_compare(void *v1, void *v2)
{
Person *p1 = (Person *)v1;
Person *p2 = (Person *)v2;
if (strcmp(p1->name, p2->name) == 0 && (p1->age == p2->age))
{
return 0;
}
return -1;
}
int main()
{
Person p1 = { "Person1",10 };
Person p2 = { "Person2",20 };
Person p3 = { "Person3",30 };
Person p4 = { "Person4",40 };
LinkList *list = init_linklist();
push_front_linklist(list, &p1);
printf("list的大小: %d\n", list->size);
print_linklist(list, my_print);
printf("尾部插入p2---------------------------\n");
push_back_linklist(list, &p2);
print_linklist(list, my_print);
printf("在1的位置插入p3-----------------\n");
insert_by_pos_linklist(list, 1, &p3);
print_linklist(list, my_print);
printf("在2的位置插入p4-------------\n");
insert_by_pos_linklist(list, 2, &p4);
print_linklist(list, my_print);
Person * p = (Person*)pop_front_linklist(list);
printf("删除的第一个节点: 姓名:%s,年龄:%d\n", p->name, p->age);
printf("删除之后的大小: %d\n", list->size);
p = (Person *)pop_back_linklist(list);
printf("删除的最后一个节点: 姓名:%s,年龄:%d\n", p->name, p->age);
printf("删除之后的大小: %d\n", list->size);
printf("当前的节点信息: \n");
print_linklist(list,my_print);
int pos = get_by_val_linklist(list, (void *)&p3, my_compare);
if (pos == -1)
{
printf("未找到!");
}
else
{
printf("找到了,位置是: %d\n", pos);
}
pos = get_by_val_linklist(list, (void *)&p2, my_compare);
if (pos == -1)
{
printf("未找到!\n");
}
else
{
printf("找到了,位置是: %d\n", pos);
}
p = (Person *)get_by_pos_linklist(list, 1);
if (p != NULL)
{
printf("找到了,姓名: %s,年龄: %d\n", p->name, p->age);
}
else
{
printf("未找到\n");
}
remove_by_pos_linklist(list, 1);
printf("删除之后: \n");
print_linklist(list, my_print);
free_space_linklist(list);
system("pause");
return 0;
}
三. 约瑟夫问题(循环链表解决)
问题描述: 假设: m = 8, n = 3;
使用上面的循环链表解决约瑟夫问题
#include "CircleListTwo.h"
#define M 8
#define N 3
void my_print_int(void *data)
{
int* p = (int *)data;
printf("%d ", *p);
}
int main()
{
int myNum[M];
LinkList *list = init_linklist();
for (int i = 0; i < M; i++)
{
myNum[i] = i + 1;
insert_by_pos_linklist(list, i, (void *)(&myNum[i]));
}
print_linklist(list, my_print_int);
printf("\n");
int index = 1;
LinkNode *pCurrent = list->head->next;
while(list->size > 1)
{
if (index == N)
{
LinkNode *next = pCurrent->next;
printf("%d ", *((int*)(pCurrent->data)));
pCurrent->next->prior = pCurrent->prior;
pCurrent->prior->next = next;
list->size--;
free(pCurrent);
pCurrent = next;
if (pCurrent == list->head)
{
pCurrent = pCurrent->next;
}
index = 1;
}
pCurrent = pCurrent->next;
if (pCurrent == list->head)
{
pCurrent = pCurrent->next;
}
index++;
}
if (list->size == 1)
{
int num = *((int *)pop_front_linklist(list));
printf("%d ", num);
}
else
{
printf("出错!");
}
printf("\n");
system("pause");
return 0;
}
结果:
|