基本概念

循环链表的定义:将单链表中最后一个数据元素的next指针指向第一个元素

循环链表拥有单链表的所有操作

创建链表

销毁链表

获取链表长度

清空链表

获取第pos个元素操作

插入元素到位置pos

删除位置pos处的元素

新增功能:游标的定义

在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。

循环链表新操作

将游标重置指向链表中的第一个数据元素

CircleListNode* CircleList_Reset(CircleList* list);



获取当前游标指向的数据元素

CircleListNode* CircleList_Current(CircleList* list);



将游标移动指向到链表中的下一个数据元素

CircleListNode* CircleList_Next(CircleList* list);



直接指定删除链表中的某个数据元素

CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);

// 根据元素的值 删除 元素 pk根据元素的位置 删除 元素

最后加了一个循环链表的应用:求解约瑟夫问题

约瑟夫问题-循环链表典型应用

n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。

代码:

// circlelist.h
// 循环链表API声明

#ifndef _CIRCLELIST_H_
#define _CIRCLELIST_H_

typedef void CircleList;

typedef struct _tag_CircleListNode
{
	struct _tag_CircleListNode *next;
}CircleListNode;

// 创建链表
CircleList* CircleList_Create();

// 销毁链表
void CircleList_Destroy(CircleList* list);

// 清空链表
void CircleList_Clear(CircleList* list);

// 获取链表的长度
int CircleList_Length(CircleList* list);

// 在pos位置插入结点node
int CircleList_Insert(CircleList* list,CircleListNode* node, int pos);

// 获取pos位置的结点
CircleListNode* CircleList_Get(CircleList* list, int pos);

// 删除pos位置的结点
CircleListNode* CircleList_Delete(CircleList* list, int pos);

// 根据结点的值进行数据删除
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);

// 重置游标
CircleListNode* CircleList_Reset(CircleList* list);

// 获取当前游标所指结点
CircleListNode* CircleList_Current(CircleList* list);

// 将原始游标所指结点返回给上层,然后让游标移到下一个结点
CircleListNode* CircleList_Next(CircleList* list);

#endif
// circlelist.cpp
// 循环链表API实现

#include <iostream>
#include <cstdio>
#include "circlelist.h"

typedef struct _tag_CircleList
{
	CircleListNode header;
	CircleListNode *silder;
	int length;
}TCircleList;

// 创建链表
CircleList* CircleList_Create()
{
	TCircleList *ret = (TCircleList *)malloc(sizeof(TCircleList));
	if (ret == NULL) {
		return NULL;
	}

	// 初始化
	ret->header.next = NULL;
	ret->silder = NULL;
	ret->length = 0;

	return ret;
}

// 销毁链表
void CircleList_Destroy(CircleList* list)
{
	if (list == NULL) {
		return;
	}
	free(list);
	return;
}

// 清空链表
void CircleList_Clear(CircleList* list)
{
	if (list == NULL) {
		return;
	}
	TCircleList *tList = (TCircleList *)list;
	tList->header.next = NULL;
	tList->silder = NULL;
	tList->length = 0;

	return;
}

// 获取链表的长度
int CircleList_Length(CircleList* list)
{
	if (list == NULL) {
		return -1;
	}
	TCircleList *tList = (TCircleList *)list;
	return tList->length;
}

// 在pos位置插入结点node
int CircleList_Insert(CircleList* list, CircleListNode* node, int pos)
{
	if (list == NULL || node == NULL || pos < 0) {
		return -1;
	}

	TCircleList *tList = (TCircleList *)list;

	CircleListNode *cur = (CircleListNode *)tList;

	for (int i = 0; i < pos; ++i) {
		cur = cur->next;
	}

	node->next = cur->next;
	cur->next = node;

	// 如果是第一次插入
	if (tList->length == 0) {
		tList->silder = node;
	}

	++tList->length; // 记得长度加1

	// 如果是头插法
	if (cur == (CircleListNode *)tList) {
		// 获取最后一个元素
		CircleListNode *last = CircleList_Get(tList, tList->length - 1);
		last->next = cur->next;
	}

	return 0;
}

// 获取pos位置的结点
CircleListNode* CircleList_Get(CircleList* list, int pos)
{
	// 因为是循环链表,所以这里不需要排除pos>length的情况
	if (list == NULL || pos < 0) {
		return NULL;
	}

	TCircleList *tList = (TCircleList *)list;
	CircleListNode *cur = (CircleListNode *)tList;

	for (int i = 0; i < pos; ++i) {
		cur = cur->next;
	}

	return cur->next;
}

// 删除pos位置的结点
CircleListNode* CircleList_Delete(CircleList* list, int pos)
{
	TCircleList *tList = (TCircleList *)list;
	CircleListNode *ret = NULL;

	if (tList != NULL && pos >= 0 && tList->length > 0) {
		CircleListNode *cur = (CircleListNode *)tList;
		for (int i = 0; i < pos; ++i) {
			cur = cur->next;
		}

		// 若删除头结点,需要求出尾结点
		CircleListNode *last = NULL;
		if (cur == (CircleListNode *)tList)  {
			last = CircleList_Get(tList, tList->length - 1);
		}

		ret = cur->next;
		cur->next = ret->next;

		--tList->length;

		// 若删除头结点
		if (last != NULL) {
			tList->header.next = ret->next;
			last->next = ret->next;
		}

		// 若删除的元素为游标所指的元素
		if (tList->silder == ret) {
			tList->silder = ret->next;
		}

		// 若删除元素后链表长度为0
		if (tList->length == 0) {
			tList->header.next = NULL;
			tList->silder = NULL;
		}
	}

	return ret;
}

// 根据结点的值进行数据删除
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node)
{
	TCircleList *tList = (TCircleList *)list;
	CircleListNode *ret = NULL;

	if (list != NULL && node != NULL) {
		CircleListNode *cur = (CircleListNode *)tList;
		int i = 0;
		for (i = 0; i < tList->length; ++i) {
			if (cur->next == node) {
				ret = cur->next;
				break;
			}

			cur = cur->next;
		}

		// 如果找到
		if (ret != NULL) {
			CircleList_Delete(tList, i);
		}
	}

	return ret;
}

// 重置游标
CircleListNode* CircleList_Reset(CircleList* list)
{
	TCircleList *tList = (TCircleList *)list;
	CircleListNode* ret = NULL;

	if (list != NULL) {
		tList->silder = tList->header.next;
		ret = tList->silder;
	}

	return NULL;
}

// 获取当前游标所指结点
CircleListNode* CircleList_Current(CircleList* list)
{
	TCircleList *tList = (TCircleList *)list;
	CircleListNode* ret = NULL;
	if (list != NULL) {
		ret = tList->silder;
	}

	return ret;
}

// 将原始游标所指结点返回给上层,然后让游标移到下一个结点
CircleListNode* CircleList_Next(CircleList* list)
{
	TCircleList *tList = (TCircleList *)list;
	CircleListNode* ret = NULL;
	if (list != NULL && tList->silder != NULL) {
		ret = tList->silder;
		tList->silder = ret->next;
	}
	return ret;
}
// joseph.h
// 用循环链表API求解约瑟夫问题

#include <cstdio>
#include "circlelist.h"

const int maxp = 8;

struct Person
{
	CircleListNode circlenode;
	int id;
};

void joseph()
{
	Person s[maxp];
	for (int i = 0; i < maxp; ++i) {
		s[i].id = i + 1;
	}

	CircleList *list = NULL;
	list = CircleList_Create();

	// 插入元素
	for (int i = 0; i < maxp; ++i) {
		// 尾插法
		int ret = CircleList_Insert(list, (CircleListNode *)&s[i], CircleList_Length(list));
		if (ret < 0) {
			printf("function CircleList_Insert err: %d\n", ret);
		}
	}

	// 遍历链表
	for (int i = 0; i < CircleList_Length(list); ++i) {
		Person *tmp = (Person *)CircleList_Get(list, i);
		if (tmp == NULL) {
			printf("function CircleList_Get err.\n");
		}
		printf("age: %d\n", tmp->id);
	}

	// 求解约瑟夫问题
	while (CircleList_Length(list) > 0)
	{
		Person* pv = NULL;
		for (int i = 1; i < 3; i++)
		{
			CircleList_Next(list);
		}
		pv = (Person*)CircleList_Current(list);
		printf("%d ", pv->id);
		CircleList_DeleteNode(list, (CircleListNode *)pv); //根据结点的值,进行结点元素的删除
	}
	printf("\n");

	CircleList_Destroy(list);

}
// main.cpp
// 循环链表测试程序

#include <iostream>
#include <cstdio>
#include "circlelist.h"
#include "joseph.h"

const int maxn = 5;

struct Student
{
	CircleListNode circlenode;
	char name[32];
	int age;
};

void play01()
{
	Student s[maxn];
	for (int i = 0; i < maxn; ++i) {
		s[i].age = i + 1;
	}

	CircleList *list = NULL;

	list = CircleList_Create(); // 创建链表

	// 插入元素
	for (int i = 0; i < maxn; ++i) {
		// 尾插法
		int ret = CircleList_Insert(list, (CircleListNode *)&s[i], CircleList_Length(list));
		if (ret < 0) {
			printf("function CircleList_Insert err: %d\n", ret);
		}
	}

	// 遍历链表
	// 这里遍历打印两边,可以证明这是一个循环链表
	for (int i = 0; i < 2 * CircleList_Length(list); ++i) {
		Student *tmp = (Student *)CircleList_Get(list, i);
		if (tmp == NULL) {
			printf("function CircleList_Get err.\n");
		}
		printf("age: %d\n", tmp->age);
	}

	// 删除结点,通过结点位置
	while (CircleList_Length(list)) {
		Student *tmp = (Student *)CircleList_Delete(list, CircleList_Length(list) - 1);
		if (tmp == NULL) {
			printf("function CircleList_Delete err.\n");
		}
		printf("age: %d\n", tmp->age);
	}

	// 销毁链表
	CircleList_Destroy(list);

}

int main()
{
	play01(); // 为了测试数据的生命周期,所以另写一个函数调用运行
	joseph();

	return 0;
}

循环链表设计与API实现的更多相关文章

  1. Spring Boot入门系列(二十一)如何优雅的设计 Restful API 接口版本号,实现 API 版本控制!

    前面介绍了Spring Boot 如何快速实现Restful api 接口,并以人员信息为例,设计了一套操作人员信息的接口.不清楚的可以看之前的文章:https://www.cnblogs.com/z ...

  2. Laravel5设计json api时候的一些道道

    对于返回数据格式没规整的问题 在开发api的时候,这个问题是和客户端交涉最多的问题,比如一个user结构,返回的字段原本是个user_name的,它应该是string类型.但是呢,由于数据库设计这个字 ...

  3. 双向链表设计与API实现

    为什么需要双向链表? 单链表的结点都只有一个指向下一个结点的指针 单链表的数据元素无法直接访问其前驱元素 逆序访问单链表中的元素是极其耗时的操作! 双向链表的定义 在单链表的结点中增加一个指向其前驱的 ...

  4. Flask 学习篇一: 搭建Python虚拟环境,安装flask,并设计RESTful API。

    前些日子,老师给我看了这本书,于是便开始了Flask的学习 GitHub上的大神,于是我也在GitHub上建了一个Flask的项目. 有兴趣可以看看: https://github.com/Silen ...

  5. 设计 REST API 的13个最佳实践

    写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈.编写友好的 restful api 不论对于你的同事,还是将来作为第三方服 ...

  6. 使用 Python 和 Flask 设计 RESTful API

    近些年来 REST (REpresentational State Transfer) 已经变成了 web services 和 web APIs 的标配. 在本文中我将向你展示如何简单地使用 Pyt ...

  7. 13 个设计 REST API 的最佳实践

    原文 RESTful API Design: 13 Best Practices to Make Your Users Happy 写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口 ...

  8. 学习设计接口api(转)

    介绍 先说说啥是 Api 吧,以下摘自百度百科: API (Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于 ...

  9. Spring Boot Security 整合 OAuth2 设计安全API接口服务

    简介 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版.本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAut ...

随机推荐

  1. SpringBatch简介

    spring Batch是一个轻量级的.完善的批处理框架,旨在帮助企业建立健壮.高效的批处理应用.SpringBatch是Spring的一个子项目,使用Java语言并基于Spring框架为基础开发,使 ...

  2. Dubbo框架应用之(四)--Dubbo基于Zookeeper实现分布式实例

    上三篇文章主要是解决了概念性的补充和学习,充分结合实战来深入理解 入门实例解析 第一:provider-提供服务和相应的接口 创建DemoService接口 package com.unj.dubbo ...

  3. Android倒计时器——CountDownTimer

    Android倒计时器--CountDownTimer 说明 第一个参数是倒计时的时间 第二个参数是多长时间执行一次回调 /** * @param millisInFuture The number ...

  4. EBS业务学习之应付管理

    应付款系统是供应链管理的最后一个环节,它使公司能够支付供应商提供的货物和服务的费用.供应链管理的目标是保持低库存量但又有充足的存货以满足要求,仓库中的库存就等于钱,因此,应付款管理的目标是尽可能地推迟 ...

  5. SpriteKit游戏Delve随机生成地牢地图一个Bug的修复

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) Delve是一个很有意思的地牢探险类型的游戏,其中每一关的地图 ...

  6. android 网络获取json并且显示(2)

    1.将要的取得的json数据格式如下: 我们封装之前的类用google提供的JSONArray和JSONObject类对json字符串进行解析. 对于姚明显示每一条数据,我们封装了一个类如下: pub ...

  7. mysql进阶(二十七)数据库索引原理

    mysql进阶(二十七)数据库索引原理 前言   本文主要是阐述MySQL索引机制,主要是说明存储引擎Innodb.   第一部分主要从数据结构及算法理论层面讨论MySQL数据库索引的数理基础.    ...

  8. actionbar详解(二)

    经过前面两篇文章的学习,我想大家对ActionBar都已经有一个相对较为深刻的理解了.唯一欠缺的是,前面我们都只是学习了理论知识而已,虽然知识点已经掌握了,但是真正投入到项目实战当中时会不会掉链子还很 ...

  9. Xcode7.2如何真机调试iOS 9.3的设备

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 本猫的mac系统为10.10,Xcode版本为7.2 本猫将i ...

  10. numpy教程:排序、搜索和计数

    http://blog.csdn.net/pipisorry/article/details/51822775 numpy排序.搜索和计数函数和方法.(重新整合过的) ],, , ], [, , ]] ...