一,循環鏈表的概念
1.什麼是循環鏈表
所謂的循環鏈表就是讓單向鏈表的首尾相連,組成一個環狀。
2.循環鏈表的典型應用
約瑟夫環問題。
3.實現循環鏈表的重點
1,循環鏈表在插入第一個元素的時候,需要我們將第一元素的指針域指向其自身,也就構成了循環鏈表。
2,循環鏈表基於單向鏈表而生,單是比循環鏈表多了游標這個概念。要想實現循環鏈表的插入,刪除的關鍵是考慮頭結點問題,因為在頭插法方式(往鏈表的頭部插入數據)中,需要將末尾數據元素的指針域指向新插入的節點。將新插入的節點的指針域指向頭結點的指針域的指針域,還需要將頭結點指向新插入的結點。(插入相同)。
二,循環鏈表新增概念和功能
1,什麼是游標
所謂的游標就是在鏈表中可以移動的指針,游標初始化一般是指向鏈表的第一個結點。
2,游標的功能
三,循環鏈表的實現
1,循環鏈表的功能
# ifndef CIRCLE_LINK_LIST
# define CIRCLE_LINK_LIST
/* 業務節點 */
typedef void Node;
/* 鏈表節點(被包含) */
typedef struct CircleNode
{
struct CircleNode * next;
}CircleNode;
/* 鏈表 */
typedef struct CircleLinkList
{
/* 頭指針 */
Node * ptr;
/* 頭結點 */
CircleNode header;
/* 游標 */
CircleNode slider;
/* 長度 */
int length;
}CircleLinkList;
/* 創建循環鏈表 */
CircleLinkList * createList();
/* 獲取鏈表長度 */
int length(CircleLinkList * list);
/* 銷毀鏈表 */
void destory(CircleLinkList * list);
/* 清空鏈表 */
void clear(CircleLinkList * list);
/* 判斷鏈表是否為空 */
int empty(CircleLinkList * list);
/* 插入結點 */
void insert(CircleLinkList * list,int pos, Node * node);
/* 刪除結點 */
Node * del(CircleLinkList * list, int pos);
/* 獲取結點 */
Node * get(CircleLinkList * list, int pos);
/* 將游標重置指向鏈表的第一個元素 */
void resetSlider(CircleLinkList * list);
/* 獲取當前游標指向的數據元素 */
Node * current(CircleLinkList * list);
/* 將游標指向到鏈表的下一個數據元素並返回當前游標的數據元素 */
Node * next(CircleLinkList * list);
/* 刪除游標,並將游標指向下一個數據元素 */
Node * deleteNode(CircleLinkList * list);
# endif
2,循環鏈表的實現
# include<stdio.h>
# include<stdlib.h>
# include"CircleLinkList.h"
/* 創建循環鏈表 */
CircleLinkList * createList()
{
/* 在堆上分配內存 */
CircleLinkList * list = (CircleLinkList *)malloc(sizeof(CircleLinkList));
/* 初始化 */
list->ptr = &list->header;
list->header.next = NULL;
list->slider.next = NULL;
list->length = 0;
return list;
}
/* 獲取鏈表長度 */
int length(CircleLinkList * list)
{
return list->length;
}
/* 銷毀鏈表 */
void destory(CircleLinkList * list)
{
if (list != NULL)
{
free(list);
}
}
/* 清空鏈表 */
void clear(CircleLinkList * list)
{
if (list != NULL)
{
list->header.next = NULL;
list->slider.next = NULL;
}
}
/* 判斷鏈表是否為空 */
int empty(CircleLinkList * list)
{
if (list->length == 0)
{
return 1;
}
else {
return 0;
}
}
/* 插入結點 */
void insert(CircleLinkList * list, int pos, Node * node)
{
if (list == NULL)
{
printf("鏈表為NULL\n");
return;
}
/* 判斷插入位置是否超過鏈表長度或小於0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? length(list) : pos;
/* 注意:如果在第一個位置插入,則需要遍歷到最後一個元素,然後再把最後一個元素的指針域指向第一個 */
/* 將業務節點轉換為鏈表節點 */
CircleNode * _node = (CircleNode *)node;
/* 判斷是否第一次插入 */
if (length(list) == 0)
{
list->header.next = _node;
_node->next = _node;
list->length++;
return;
}
/* 定義posNode結點 */
CircleNode * posNode = list->header.next;
/* 判斷是否在頭部插入 */
if (pos == 0)
{
/* 將posNode移動到尾部 */
for (int i = 0; i < length(list)-1; i++)
{
posNode = posNode->next;
}
/* 將尾部指針指向新節點 */
posNode->next = _node;
/* 將新節點指針指向頭節點指向的節點 */
_node->next = list->header.next;
/* 將頭節點指向新節點 */
list->header.next = _node;
}
else {
/* 將posNode移動到pos位置上 */
for (int i = 0; i < pos-1; i++)
{
posNode = posNode->next;
}
/* 插入 */
_node->next = posNode->next;
posNode->next = _node;
}
list->length++;
}
/* 刪除結點 */
Node * del(CircleLinkList * list, int pos)
{
if (list == NULL)
{
printf("鏈表為NULL\n");
return NULL;
}
if (length(list) <= 0)
{
printf("鏈表已空\n");
return NULL;
}
/* 判斷插入位置是否超過鏈表長度或小於0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? length(list) : pos;
/* 定義要刪除的節點 */
CircleNode * result = NULL;
/* 定義posNode結點 */
CircleNode * posNode = list->header.next;
/* 判斷是否刪除頭結點 */
if (pos == 0)
{
/* 移動posNode到pos位置 */
for (int i = 0; i < length(list) - 1; i++)
{
posNode = posNode->next;
}
/* 保存要刪除的節點 */
result = posNode->next;
/* 刪除 */
posNode->next = list->header.next->next;
list->header.next = list->header.next->next;
}
else {
/* 定義緩存上一個結點 */
CircleNode * previous = posNode;
/* 移動posNode到pos位置 */
for (int i = 0; i < pos; i++)
{
previous = posNode;
posNode = posNode->next;
}
/* 保存要刪除的節點 */
result = posNode;
/* 刪除 */
previous->next = posNode->next;
}
list->length--;
return result;
}
/* 獲取結點 */
Node * get(CircleLinkList * list, int pos)
{
if (list == NULL)
{
printf("鏈表為NULL\n");
return NULL;
}
/* 判斷插入位置是否超過鏈表長度或小於0 */
pos = pos < 0 ? 0 : pos;
pos = pos > length(list) ? pos%length(list) : pos;
/* 定義頭結點 */
CircleNode * header = list->header.next;
/* 定義pos結點 */
CircleNode * posNode = header;
/* 移動posNode到指定位置 */
for (int i = 0; i < pos; i++)
{
posNode = posNode->next;
}
return posNode;
}
/* 將游標重置指向鏈表的第一個元素 */
void resetSlider(CircleLinkList * list)
{
list->slider.next = list->header.next;
}
/* 獲取當前游標指向的數據元素 */
Node * current(CircleLinkList * list)
{
return list->slider.next;
}
/* 將游標指向到鏈表的下一個數據元素並返回當前游標的數據元素 */
Node * next(CircleLinkList * list)
{
CircleNode * result = list->slider.next;
list->slider.next = list->slider.next->next;
return result;
}
/* 刪除游標,並將游標指向下一個數據元素 */
Node * deleteNode(CircleLinkList * list)
{
if (list == NULL)
{
printf("鏈表為NULL\n");
return NULL;
}
if (length(list) <= 0)
{
printf("鏈表已空\n");
return NULL;
}
/* 獲取當前游標的數據結點 */
Node * node = current(list);
/* 將當前游標下移 */
next(list);
/* 定義要刪除的節點 */
CircleNode * result = NULL;
/* 定義posNode結點 */
CircleNode * posNode = list->header.next;
/* 將業務節點轉換為鏈表節點 */
CircleNode * _node = (CircleNode *)node;
/* 判斷是否刪除頭結點 */
if (_node == list->header.next)
{
/* 移動posNode到pos位置 */
for (int i = 0; i < length(list) - 1; i++)
{
posNode = posNode->next;
}
/* 保存要刪除的節點 */
result = posNode->next;
/* 刪除 */
posNode->next = list->header.next->next;
list->header.next = list->header.next->next;
}
else {
/* 定義緩存上一個結點 */
CircleNode * previous = posNode;
/* 移動posNode到pos位置 */
while (posNode != _node)
{
previous = posNode;
posNode = posNode->next;
}
/* 保存要刪除的節點 */
result = posNode;
/* 刪除 */
previous->next = posNode->next;
}
list->length--;
return result;
}
3,循環鏈表的測試
# include<stdio.h>
# include"CircleLinkList.h"
typedef struct Student
{
CircleNode next;
char name[64];
int age;
}Student;
int main()
{
Student s1 = { NULL,"劉備",56 };
Student s2 = { NULL,"關羽",40 };
Student s3 = { NULL,"張飛",36 };
Student s4 = { NULL,"趙雲",34 };
Student s5 = { NULL,"馬超",20 };
Student s6 = { NULL,"黃忠",80 };
CircleLinkList * list = createList();
insert(list, length(list), &s1);
insert(list, length(list), &s2);
insert(list, length(list), &s3);
insert(list, length(list), &s4);
insert(list, length(list), &s5);
insert(list, length(list), &s6);
printf("##############遍歷################\n");
for (int i = 0; i < length(list); i++)
{
Student * student = (Student *)get(list, i);
printf("name = %s,age = %d\n", student->name, student->age);
}
//printf("##############刪除################\n");
//for (int i = 0; i < 6; i++)
//{
// Student * student = (Student *)del(list, length(list)-1);
// printf("name = %s,age = %d\n", student->name, student->age);
//}
resetSlider(list);
printf("##############游標遍歷################\n");
for (int i = 0; i < length(list); i++)
{
Student * student = next(list);
printf("name = %s,age = %d\n", student->name, student->age);
}
}
三,約瑟夫環問題
/*
* 約瑟夫環運作如下:
* 1、一群人圍在一起坐成環狀(如:N)
* 2、從某個編號開始報數(如:K)
* 3、數到某個數(如:M)的時候,此人出列,下一個人重新報數
* 4、一直循環,直到所有人出列,約瑟夫環結束
*/
# include<stdio.h>
# include"CircleLinkList.h"
typedef struct value
{
CircleNode next;
int value;
}value;
int main()
{
/* 創建循環鏈表 */
CircleLinkList * list = createList();
/* 從0到9十個學生圍成環狀 */
value v0 = { NULL,0 };
value v1 = { NULL,1 };
value v2 = { NULL,2 };
value v3 = { NULL,3 };
value v4 = { NULL,4 };
value v5 = { NULL,5 };
value v6 = { NULL,6 };
value v7 = { NULL,7 };
value v8 = { NULL,8 };
value v9 = { NULL,9 };
/* 尾插法 */
insert(list, length(list), &v0);
insert(list, length(list), &v1);
insert(list, length(list), &v2);
insert(list, length(list), &v3);
insert(list, length(list), &v4);
insert(list, length(list), &v5);
insert(list, length(list), &v6);
insert(list, length(list), &v7);
insert(list, length(list), &v8);
insert(list, length(list), &v9);
/* 重置游標 */
resetSlider(list);
/* 將游標移動到2位置,從第三個人開始報數 */
for (int i = 0; i < 2; i++)
{
next(list);
}
/* 將數到4的人刪除 */
while (empty(list) == 0)
{
/* 循環4次 */
for (int i = 0; i < 3; i++)
{
next(list);
}
/* 刪除當前游標 */
value * t = (value *)deleteNode(list);
printf("result = %d\n", t->value);
}
return 0;
}