小猪的数据结构学习笔记(五)
线性表之——循环链表
——转载请注明出处:coder-pig
其实和单链表的结构是一样的!
/*定义循环链表的存储结构*/
typedef struct Cir_List
{
int data;
struct Cir_List *next;
}Lnode;
代码如下:
//1.循环链表的初始化
//表示一个元素,如果是一个*表示地址,没有*表示二级指针(指向指针的指针)
void Cir_List_init(Lnode **pNode)
{
int item;
Lnode *temp;
Lnode *target;
printf("请输入结点的值,输入0表示输入完毕!\n");
while(1)
{
scanf("%d", &item);
fflush(stdin);
if(item == 0)return;
//循环链表只有一个结点
if((*pNode) == NULL)
{
*pNode = (Lnode *)malloc(sizeof(struct Cir_List));
if(!(*pNode))exit(0);
(*pNode)->data = item;
(*pNode)->next = *pNode;
}
//不止一个结点:
else
{
//找到next指向第一个结点的结点
for(target = (*pNode);target ->next != (*pNode);target = target ->next);
//生成一个新的结点:
temp = (Lnode *)malloc(sizeof(struct Cir_List));
if(!temp)exit(0);
//为新生成的结点赋值,同时把它的头指针指向
temp ->data = item;
temp ->next = *pNode;
target->next = temp;
}
}
}
代码如下:
/*遍历循环链表*/
void Cir_List_traverse(Lnode *pNode)
{
Lnode *temp;
temp = pNode;
printf("***********当前链表中的元素******************\n");
do
{
printf("%4d ", temp->data);
}while((temp = temp->next) != pNode);
printf("\n");
}
代码如下:
/*往链表中插入元素*/
//参数依次为插入的表与插入的位置
void Cir_List_insert(Lnode **pNode , int i)
{
Lnode *temp;
Lnode *target;
Lnode *p;
int item;
int j = 1;
printf("输入要插入结点的值:");
scanf("%d", &item);
if(i == 1)
{ //新插入的结点作为第一个结点
temp = (Lnode *)malloc(sizeof(struct Cir_List));
if(!temp)
exit(0);
temp ->data = item;
/*寻找到最后一个结点*/
for(target = (*pNode); target->next != (*pNode); target = target->next)
;
temp->next = (*pNode);
target->next = temp;
//别忘了这句哦!可以理解为指向第一个节点的头结点
*pNode = temp;
}
else
{
target = *pNode;
//这里找到的是插入节点的前一个节点哦!
for( ; j < (i-1); ++j )
{
target=target->next;
}
temp = (Lnode *)malloc(sizeof(struct Cir_List));
if(!temp)
exit(0);
temp ->data = item;
p = target->next;
target->next = temp;
temp->next = p;
}
}
代码如下:
//删除循环链表中的某个元素
void Cir_List_delete(Lnode **pNode, int i)
{
Lnode *target;
Lnode *temp;
int j = 1;
//删除的是第一个结点
if(i == 1)
{
/*找到最后一个结点*/
for(target = *pNode; target->next != *pNode;target = target->next);
temp = *pNode;
*pNode = (*pNode)->next;
target->next = *pNode;
free(temp);
}
//删除的是其他节点
else
{
target = *pNode;
for( ; j < i-1; ++j )
{
target = target->next;
}
temp = target->next;
target->next = temp->next;
free(temp);
}
}这个比较简单,只需要让指针依次后移,比较是否有与值相同的结点,找到返回下标(1开始)
找不到的话则返回0;
代码如下:
/*返回结点所在位置*/
int Cir_List_search(Lnode *pNode, int elem)
{
Lnode *target;
int i = 1;
for(target = pNode; target->data != elem && target->next != pNode; ++i)
{
target = target->next;
}
/*表中不存在该元素*/
if(target->next == pNode)return 0;
else return i;
}
作为循环链表的一个经典应用例子,问题是这样的:
一堆人围成一个圈,在开始之前规定一个数N,然后依次报数,当报到N的时候,这个人自杀,其他人鼓掌!啪啪啪!
接着由从1开始报数,报到N又自杀..以此类推,知道最后死剩一个人,游戏完结!
要求就是用户输入:N参与人数,M第几个人死,两个参数,返回的是最后一个人!
类似的有跳海问题,猴子选王等题目!下面我们就以题目中的41和3作为输入参数,使用不同的方法来解决
约瑟夫问题!
/*
该代码是使用C语言的数组解决约瑟夫环问题的:
思路是使用一个标记数组,1代表人还没死,0表示死了
1.获取人数,第几个死,初始化标记数组,全部置为1
2.使用一个变量来统计剩下的没死的人数>1作为循环条件
(1)判断标记数组中的元素是否为1,即没死,没死的话继续循环,这里
很巧妙的使用下述代码: if(!tag[i%a])continue;
(2)判断是否移动了n个,就是要杀死一个人,让对应下表设置为0,同时
打印输出,统计人数的变量-1;
否则:判断是否移动n个人的变量++;
3.使用循环遍历数组,找到不为0的元素,打印输出下表即可
*/
#include <stdio.h>
int main()
{
//参数a:人数; b:第b个人死 last剩余人数
int i,j,a,b,last;
int tag[100];
printf("输入参加的人数:\n");
scanf("%d",&a);
printf("输入每隔多少死一个人:\n");
scanf("%d",&b);
printf("初始化\n");
//初始化标记数组
for(i = 0;i < a;i++)
{
tag[i] = 1;
}
//开始报数
j = 1;
last = a;
for(i = 0; last > 1;i++)
{
//如果数组元素为1的话
if(!tag[i%a])continue;
if(j == b){j = 1;tag[i%a] = 0;printf("第%d个人自杀!\n",(i%a)+1);last--;}
else j++;
}
for(i = 0;i < a;i++)if(tag[i])break;
printf("第%d个人生存下来了!\n",i+1);
return 0;
}
其实这个方法和上面使用数组的方式是类似的!这里是控制指针后移而已!
用循环链表的话,报道的人所在的结点直接给释放
/*
该代码是使用循环链表解决约瑟夫
问题,首先定义一个创建链表的方法,依次
为结点赋值:1,2,3...,最后把头结点释放掉;
接着在主方法中传入两个参数,使用循环,对于
报数n的人对应的结点free掉,继续循环;
因为循环链表是类似于一个环的样子,所以不需要判断
指针是否越界这些问题!
*/
#include <stdio.h>
#include <stdlib.h>
//定义循环链表的存储结构
typedef struct node
{
int data;
struct node *next;
}Lnode;
//定义循环链表的初始化方法
Lnode *create(int n)
{
Lnode *p = NULL,*head;
head = (Lnode *)malloc(sizeof(Lnode));
p = head;
Lnode *s;
int i = 1;
if(0 != n)
{
while(i <= n)
{
s = (Lnode *)malloc(sizeof (Lnode));
// 为循环链表初始化,第一个结点为1,第二个结点为2。
s->data = i++;
p->next = s;
p = s;
}
s->next = head->next;
}
free(head);
return s->next;
}
int main()
{
int n,m,i;
printf("请输入M和N的值:\n");
scanf("%d%d",&n,&m);
Lnode *p = create(n);
Lnode *temp;
//为了防止输入报数的人大于参与的人
//球余可以理解为重头开始
m %= n;
while (p != p->next )
{
for (i = 1; i < m-1; i++)
{
p = p->next ;
}
printf("%d->", p->next->data );
temp = p->next ; //删除第m个节点
p->next = temp->next ;
free(temp);
p = p->next ;
}
printf("%d\n", p->data );
return 0;
}运行截图:
上述两种解决约瑟夫问题的方法其实就是模拟一个报数的过程;
对付一些小的数据量的话还可以,但是如果输入的数据很大的时候
就显得很笨重,于是乎,我们需要使用一些数学策略来解决这个问题了!
代码的核心:for (i=2; i<=n; i++)s=(s+m)%i;
通过这个公式就可以得到最后一个生存的人,至于k‘ = (k + m)%i这条公式
的由来,怎么算的,笔者的数学烂
,看了别人的解释都不懂,下面贴下百科的解释
能不能看懂就靠你自己了:
代码如下:
#include <stdio.h>
int main()
{
int n, m, i, s=0;
printf ("N M = "); scanf("%d%d", &n, &m);
for (i=2; i<=n; i++)s=(s+m)%i;
printf ("最后一个剩下的人: %d\n", s+1);
return 0;
}
不是固定报数,而是每个人持有一个密码,从第一个人持有的密码开始,把他作为报数的M值
,从第一个开始报数;当报数到M时,报数的人出列,同时拿出他的密码,把密码作为报数的M值
接着依次下去...直到全部人出列位置
由于时间关系,笔者还没去思考代码怎么写,这里直接贴小甲鱼老师的解决代码:
以后有时间再来纠结这个问题!
代码如下:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NODE_NUM 100
#define TRUE 1U
#define FALSE 0U
typedef struct NodeType
{
int id;
int cipher;
struct NodeType *next;
} NodeType;
/* 创建单向循环链表 */
static void CreaList(NodeType **, const int);
/* 运行"约瑟夫环"问题 */
static void StatGame(NodeType **, int);
/* 打印循环链表 */
static void PrntList(const NodeType *);
/* 得到一个结点 */
static NodeType *GetNode(const int, const int);
/* 测试链表是否为空, 空为TRUE,非空为FALSE */
static unsigned EmptyList(const NodeType *);
int main(void)
{
int n, m;
NodeType *pHead = NULL;
while (1)
{
printf("请输入人数n(最多%d个): ", MAX_NODE_NUM);
scanf("%d", &n);
printf("和初始密码m: ");
scanf("%d", &m);
if (n > MAX_NODE_NUM)
{
printf("人数太多,请重新输入!\n");
continue;
}
else
break;
}
CreaList(&pHead, n);
printf("\n------------ 循环链表原始打印 -------------\n");
PrntList(pHead);
printf("\n-------------删除出队情况打印 -------------\n");
StatGame(&pHead, m);
}
static void CreaList(NodeType **ppHead, const int n)
{
int i, iCipher;
NodeType *pNew, *pCur;
for (i = 1; i <= n; i++)
{
printf("输入第%d个人的密码: ", i);
scanf("%d", &iCipher);
pNew = GetNode(i, iCipher);
if (*ppHead == NULL)
{
*ppHead = pCur = pNew;
pCur->next = *ppHead;
}
else
{
pNew->next = pCur->next;
pCur->next = pNew;
pCur = pNew;
}
}
printf("完成单向循环链表的创建!\n");
}
static void StatGame(NodeType **ppHead, int iCipher)
{
int iCounter, iFlag = 1;
NodeType *pPrv, *pCur, *pDel;
pPrv = pCur = *ppHead;
/* 将pPrv初始为指向尾结点,为删除作好准备 */
while (pPrv->next != *ppHead)
pPrv = pPrv->next;
while (iFlag)
{
for (iCounter = 1; iCounter < iCipher; iCounter++)
{
pPrv = pCur;
pCur = pCur->next;
}
if (pPrv == pCur)
iFlag = 0;
pDel = pCur; /* 删除pCur指向的结点,即有人出列 */
pPrv->next = pCur->next;
pCur = pCur->next;
iCipher = pDel->cipher;
printf("第%d个人出列, 密码: %d\n", pDel->id, pDel->cipher);
free(pDel);
}
*ppHead = NULL;
getchar();
}
static void PrntList(const NodeType *pHead)
{
const NodeType *pCur = pHead;
if (EmptyList(pHead))
return;
do
{
printf("第%d个人, 密码: %d\n", pCur->id, pCur->cipher);
pCur = pCur->next;
}
while (pCur != pHead);
getchar();
}
static NodeType *GetNode(const int iId, const int iCipher)
{
NodeType *pNew;
pNew = (NodeType *)malloc(sizeof(NodeType));
if(!pNew)
{
printf("Error, the memory is not enough!\n");
exit(-1);
}
pNew->id = iId;
pNew->cipher = iCipher;
pNew->next = NULL;
return pNew;
}
static unsigned EmptyList(const NodeType *pHead)
{
if(!pHead)
{
printf("The list is empty!\n");
return TRUE;
}
return FALSE;
}
原文地址:http://blog.csdn.net/coder_pig/article/details/39758471