码迷,mamicode.com
首页 > 其他好文 > 详细

小猪的数据结构学习笔记(五)

时间:2014-10-07 15:14:43      阅读:249      评论:0      收藏:0      [点我收藏+]

标签:数据结构   链表   循环链表   约瑟夫问题   

小猪的数据结构学习笔记(五)

线性表之——循环链表

                          ——转载请注明出处:coder-pig




循环链表知识点归纳:

bubuko.com,布布扣





相关代码实现:

①判断是否为空表:

bubuko.com,布布扣


②单循环链表的存储结构


其实和单链表的结构是一样的!

/*定义循环链表的存储结构*/
typedef struct Cir_List
{
	int data;
	struct Cir_List *next;
}Lnode;


③初始化循环单链表

bubuko.com,布布扣

代码如下:

//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; 			
		} 		
	}		
} 


④遍历循环链表:

bubuko.com,布布扣

代码如下:

/*遍历循环链表*/
void Cir_List_traverse(Lnode *pNode)
{
    Lnode *temp;
    temp = pNode;
    printf("***********当前链表中的元素******************\n");

	do
	{
        printf("%4d ", temp->data);
    }while((temp = temp->next) != pNode);
	printf("\n");
}


⑤往循环链表插入元素:

bubuko.com,布布扣

代码如下:

/*往链表中插入元素*/
//参数依次为插入的表与插入的位置 
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;
    }
} 


⑥删除循环链表中的节点:

bubuko.com,布布扣

代码如下:

//删除循环链表中的某个元素
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作为输入参数,使用不同的方法来解决

约瑟夫问题!




1)使用C语言的普通数组来解决问题:

/*
该代码是使用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;
}

运行截图:

bubuko.com,布布扣




2)静态链表来解决约瑟夫问题

其实这个方法和上面使用数组的方式是类似的!这里是控制指针后移而已!

用循环链表的话,报道的人所在的结点直接给释放


/*
该代码是使用循环链表解决约瑟夫
问题,首先定义一个创建链表的方法,依次
为结点赋值: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;
	
}


运行截图:

bubuko.com,布布扣



3)数学方法来解决约瑟夫问题:

上述两种解决约瑟夫问题的方法其实就是模拟一个报数的过程;

对付一些小的数据量的话还可以,但是如果输入的数据很大的时候

就显得很笨重,于是乎,我们需要使用一些数学策略来解决这个问题了!

代码的核心:for (i=2; i<=n; i++)s=(s+m)%i;

通过这个公式就可以得到最后一个生存的人,至于k‘ = (k + m)%i这条公式

的由来,怎么算的,笔者的数学烂bubuko.com,布布扣,看了别人的解释都不懂,下面贴下百科的解释

能不能看懂就靠你自己了:




bubuko.com,布布扣



代码如下:

#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;
}

运行截图:

bubuko.com,布布扣




约瑟夫问题加强版:

不是固定报数,而是每个人持有一个密码,从第一个人持有的密码开始,把他作为报数的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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!