理解循环链表
单链表和循环链表的区别在于,尾部结点的后继指针是否指向头部结点。相对于其他功能基本相似。
循环链表的插入分析
普通插入:例如pos = 2,和单链表是一样的
尾插法:辅助指针向后跳lenght次,指向链表最后元素,单链表支持尾插法
头插法:插入第一个元素时,当链表中有其他元素时插入头部时
插入第一个元素时
pos = 0, 提供辅助指针current,当前辅助指针current指向头部结点。
首先判断current指针是否指向头部结点,其次通过Get方法获取到链表的最后一个结点(Null),最后让最后一个结点的后继指针指向插入的结点。
当链表中有其他元素时插入头部时
插入结点的异常处理:
- 如果是插入第一个结点需要特别注意
循环链表的删除分析
普通删除:假设删除pos = 3/pos = length
pos = 3
pos = length
删除头部结点:
删除结点的异常处理:
- 如果删除头部结点
- 如果没有任何结点
//CircleLinkList.h
#pragma once
typedef void CircleLinkList;
//链表结点(包含了后继结点指针域)
typedef struct tag_CircleLinkListNode
{
struct tag_CircleLinkListNode *next;
}TCircleLinkListNode;
//创建循环链表
CircleLinkList *CircleLinkList_Creat();
//销毁链表
void CircleLinkList_Destroy(CircleLinkList*list);
//清空链表
void CircleLinkList_Clear(CircleLinkList *list);
//获取链表长度
int CircleLinkList_GetLength(CircleLinkList *list);
//在指定位置插入结点
int CircleLinkList_inster(CircleLinkList *list, TCircleLinkListNode *node, int pos);
//获取指定位置的结点
TCircleLinkListNode *CircleLinkList_Get(CircleLinkList *list, int pos);
//删除指定位置的结点
TCircleLinkListNode *CircleLinkList_Delete(CircleLinkList *list, int pos);
//CircleLinkList.c
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CircleLinkList.h"
//循环链表结构
typedef struct tag_CircleLinkList
{
TCircleLinkListNode header;//头部结点
TCircleLinkListNode *slider;//游标
int length;//链表长度
}TCircleLinkList;
//创建循环链表
CircleLinkList * CircleLinkList_Creat()
{
TCircleLinkList *list = NULL;
list = (TCircleLinkList *)malloc(sizeof(TCircleLinkList));//给链表分配堆内存
if (list == NULL)
{
printf("LinkList_Creat err: list = NULL");
return NULL;
}
memset(list, 0, sizeof(TCircleLinkList));//初始化堆内存
list->length = 0;
list->header.next = NULL;
list->slider = NULL;
return list;
}
//销毁链表
void CircleLinkList_Destroy(CircleLinkList * list)
{
if (list == NULL)
{
printf("LinkList_Destroy err: list = NULL");
return;
}
free(list);//释放堆内存空间
printf("LinkList_Destroy finished \n");
return;
}
//清空循环链表
void CircleLinkList_Clear(CircleLinkList * list)
{
TCircleLinkList *temp = NULL;
if (list == NULL)
{
printf("LinkList_Clear err: list = NULL");
return;
}
temp = (TCircleLinkList *)list;//类型转换
temp->length = 0;
temp->header.next = NULL;
temp->slider = NULL;
return;
}
//获取链表长度
int CircleLinkList_GetLength(CircleLinkList * list)
{
TCircleLinkList *temp = NULL;
if (list == NULL)
{
printf("LinkList_GetLength err: list = NULL");
return -1;
}
temp = (TCircleLinkList *)list;//类型转换
return temp->length;//返回链表长度
}
//在指定位置插入结点
int CircleLinkList_inster(CircleLinkList * list, TCircleLinkListNode * node, int pos)
{
TCircleLinkList *temp = NULL;
int i = 0;
if (list == NULL || node == NULL || pos < 0)
{
printf("LinkList_inster err: list = NULL || node = NULL || pos < 0");
return -1;
}
temp = (TCircleLinkList *)list;//类型转换
TCircleLinkListNode * current = &(temp->header);//辅助指针,指向头部结点
for (i = 0; i < pos && node->next != NULL; i++)//通过遍历跳转指向要插入位置的前一个结点处
{
current = current->next;
}
node->next = current->next;//让插入结点的后继指针指向current的后继结点
current->next = node;//让current的后继指针指向插入的结点
if (temp->length == 0)//初始化游标指向
{
temp->slider = node;//指向链表的第一个结点处
}
temp->length++;//长度+1
//头插法(异常处理)
if (current == &temp->header)//如果插入第一个结点时判断当前辅助指针current是否指向头部结点
{
TCircleLinkListNode *last = CircleLinkList_Get(temp, temp->length - 1);//获取链表的尾部结点
last->next = current->next;//让尾部结点的后继指针指向辅助指针current的后继结点处,完成循环链表指向
}
return 0;
}
//获取指定位置的结点
TCircleLinkListNode * CircleLinkList_Get(CircleLinkList * list, int pos)
{
TCircleLinkList *temp = NULL;
int i = 0;
if (list == NULL || pos < 0)
{
printf("LinkList_Get err: list = NULL || pos < 0");
return NULL;
}
temp = (TCircleLinkList *)list;//类型转换
TCircleLinkListNode * current = &(temp->header);//提供辅助指针,并指向头部结点
for (i = 0; i < pos ; i++)
{
current = current->next;
}
return current->next;//返回指定位置的结点
}
//删除指定位置的结点
TCircleLinkListNode * CircleLinkList_Delete(CircleLinkList * list, int pos)
{
TCircleLinkList *temp = NULL;
int i = 0;
if (list == NULL || pos < 0)
{
printf("LinkList_Get err: list = NULL || pos < 0");
return NULL;
}
temp = (TCircleLinkList *)list;//类型转换
TCircleLinkListNode *current = &(temp->header);//提供辅助指针current指向头部结点
TCircleLinkListNode *ret = NULL;//提供辅助指针用来存放临时结点
TCircleLinkListNode *last = NULL;//提供辅助指针用来存放尾部结点
for (i = 0; i < pos; i++)
{
current = current->next;
}
if (current == &(temp->header))//判断辅助指针current是否指向头部结点
{
//获取到尾部结点
last = CircleLinkList_Get(list, CircleLinkList_GetLength(list) - 1);
}
ret = current->next;//辅助指针ret临时存放要删除的结点
current->next = ret->next;//让当前current的后继指针指向要删除的结点的后继节点处,完成删除
temp->length--;//链表长度-1
//如果删除头部结点
if (last != NULL)
{
//让头结点的后继指针指向要删除的的后继结点
temp->header.next = ret->next;
//让尾部结点的后继指针指向要删除结点的后继结点处
last->next = ret->next;
}
//如果删除的结点正好是游标所指处
if (temp->slider == ret)
{
//游标下移
temp->slider = ret->next;
}
//删除结点后链表长度为0
if (temp->length == 0)
{
temp->header.next = NULL;
temp->slider = NULL;
}
return ret;//返回被删除的结点
}
//重置游标
TCircleLinkListNode * CircleLinkList_Reset(CircleLinkList * list)
{
if (list == NULL)
{
printf("LinkList_Reset err: list = NULL");
return NULL;
}
TCircleLinkList *temp = (TCircleLinkList *)list;//类型转换
temp->slider = temp->header.next;//让游标指向第一个结点处
return temp->slider;//返回游标
}
//获取游标当前指向的结点
TCircleLinkListNode * CircleLinkList_Current(CircleLinkList * list)
{
if (list == NULL)
{
printf("LinkList_Current err: list = NULL");
return NULL;
}
TCircleLinkList *temp = (TCircleLinkList *)list;//类型转换
return temp->slider;//返回当前游标
}
//把当前位置返回,并且游标下移
TCircleLinkListNode * CircleLinkList_Next(CircleLinkList * list)
{
if (list == NULL)
{
printf("linkList_Next err: list = NULL");
return NULL;
}
TCircleLinkList *temp = (TCircleLinkList *)list;//类型转换
if (temp->slider == NULL)
{
printf("linkList_Next err: temp->slider = NULL");
return NULL;
}
TCircleLinkListNode *current = temp->slider;//提供辅助指针current指向当前游标的位置
temp->slider = current->next;//游标下移
return current;
}
//删除指定结点
TCircleLinkListNode * CircleLinkList_DeleteNode(CircleLinkList * list, TCircleLinkListNode * node)
{
if (list == NULL || node == NULL)
{
printf("LinkList_DeleteNode err: list = NULL || node = NULL");
return NULL;
}
int i = 0;
TCircleLinkList *temp = (TCircleLinkList *)list;//类型转换
TCircleLinkListNode *current = &temp->header;//提供辅助指针current指向头部结点
TCircleLinkListNode *ret = NULL;//提供辅助指针ret存放临时结点
for (i = 0; i < temp->length; i++)//通过遍历跳转
{
if (current->next == node)//如果辅助指针current的后继指针等于指定删除的结点
{
ret = current->next;//存放要被删除的结点
break;
}
current = current->next;
}
//结点不为空时,作删除操作
if (ret != NULL)
{
CircleLinkList_Delete(temp, i);
}
return ret;//返回被删除的结点
}
//测试代码
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "CircleLinkList.h"
typedef struct tag_Teacher
{
TCircleLinkListNode node;
int age;
char *name;
}Teacher;
int main(int argc, char** argv)
{
Teacher t1, t2, t3, t4, t5;
t1.age = 20;
t1.name = "zhangsan";
t2.age = 21;
t2.name = "lisi";
t3.age = 22;
t3.name = "wangwu";
t4.age = 23;
t4.name = "zhaoliu";
t5.age = 24;
t5.name = "wangqi";
CircleLinkList *list = NULL;
list = CircleLinkList_Creat();
if (list == NULL)
{
printf("LinkList_Creat err: list = NULL");
return -1;
}
int i, ret = 0;
ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t1, CircleLinkList_GetLength(list));
ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t2, CircleLinkList_GetLength(list));
ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t3, CircleLinkList_GetLength(list));
ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t4, CircleLinkList_GetLength(list));
ret = CircleLinkList_inster(list, (TCircleLinkListNode *)&t5, CircleLinkList_GetLength(list));
printf("LinkList length = %d\n", CircleLinkList_GetLength(list));
for (i = 0; i < CircleLinkList_GetLength(list); i++)
{
Teacher *t = (Teacher *)CircleLinkList_Get(list, i);
printf("age = %d,name = %s\n", t->age, t->name);
}
system("pause");
return 0;
}
游标功能
通过在循环链表中增加游标功能可以方便解决约瑟夫问题
1-8围成一圈,从1开始报数报道3的人出列,然后再从下一个人开始从1报数,依次类推,求出出列顺序。
//在CircleLinkList.h中添加一下方法
//重置游标,指向链表的第一个结点处
TCircleLinkListNode *CircleLinkList_Reset(CircleLinkList *list);
//获取当前游标所指的结点
TCircleLinkListNode *CircleLinkList_Current(CircleLinkList *list);
//返回当前游标指向的结点,并游标下移
TCircleLinkListNode *CircleLinkList_Next(CircleLinkList *list);
//删除指定结点
TCircleLinkListNode *CircleLinkList_DeleteNode(CircleLinkList *list, TCircleLinkListNode *node);
#pragma warning(disable : 4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "LinkList.h"
typedef struct MyValue
{
TLinkListNode node;
int val;
}TValue;
int main(int argc, char** argv)
{
TValue v1, v2, v3, v4, v5, v6, v7, v8;
v1.val = 1;
v2.val = 2;
v3.val = 3;
v4.val = 4;
v5.val = 5;
v6.val = 6;
v7.val = 7;
v8.val = 8;
LinkList *list = (LinkList *)LinkList_Creat();
if (list == NULL)
{
printf("LinkList_Creat err: list = NULL");
return -1;
}
int i, ret = 0;
LinkList_inster(list, (TLinkListNode *)&v1, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v2, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v3, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v4, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v5, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v6, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v7, LinkList_GetLength(list));
LinkList_inster(list, (TLinkListNode *)&v8, LinkList_GetLength(list));
for ( i = 0; i < LinkList_GetLength(list); i++)
{
TValue *val = (TValue *)linkList_Next(list);
printf("TValue val[%d] = %d\n", i + 1, val->val);
}
printf("约瑟夫问题求解\n");
LinkList_Reset(list);
while (LinkList_GetLength(list) > 0)
{
TValue *val = NULL;
for (int i = 1; i < 3; i++)
{
linkList_Next(list);
}
val = LinkList_Current(list);
printf("TValue val = %d\n", val->val);
LinkList_DeleteNode(list, (TLinkListNode *)val);
}
LinkList_Destroy(list);
system("pause");
return 0;
}