小猪的数据结构学习笔记(五)
线性表之——循环链表
——转载请注明出处: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