当前位置 博文首页 > ice_elephant的博客:c/c++数据结构算法之循环链表

    ice_elephant的博客:c/c++数据结构算法之循环链表

    作者:[db:作者] 时间:2021-09-16 15:51

    循环链表这个问题的引出是来源于 Joseph问题:
    

    有 10 个小朋友按编号顺序 1,2,。。。,10 顺时针方向围成一圈。从 1 号开 始顺时针方向 1,2,。。。,9 报数,凡报数 9 者出列(显然,第一个出圈为 编号 9 者)。 最后一个出圈者的编号是多少?第 5个出圈者的编号是多少?

    解决这个问题用到了循环链表
    在这里插入图片描述
    在这里插入图片描述
    循环链表和单链表唯一的区别就是,头节点初始化的时候它的next结点不再赋值为NULL,而是指向了它自己,其它的增删查改并没有什么太大的区别一下是具体实现过程`

    /**
    1、初始化
    2、前插
    3、后插
    7、遍历
    
    
    ***///
    
    //单项链表
    
    #include<stdio.h>
    #include<Windows.h>
    #include<iostream>
    
    using namespace std;
    
    typedef int Elemdata;
    
    typedef struct _LinkList {
    	Elemdata data;
    	_LinkList* next;
    
    }List, Node;
    
    //循环表初始化
    bool initlist(List*& L) {
    
    	L = new List;
    	if (!L) return false;
    	L->next = L;
    	L->data = -1;//这里给头节点的data初始化为1但是我们
    	//并不会用它来存数据,只是给它一个标志数据-1也,可以不初始化
    	return true;
    }
    
    //循环链表前插法			
    bool insert_front(List*& L, Node* node) {//为什么这里只用传一个一级指针,一级指针不是只是只能值得传入不能将值带出的吗
    
    	if (!L || !node)return false;
    
    	node->next = L->next;
    	L->next = node;
    
    	return true;
    
    }
    //循环链表尾插法
    
    bool insert_end(List*& L, Node* node) {
    
    	if (!L || !node)return	false;
    
    	//找到最后一个结点
    	Node* last = L;
    
    	while (last->next!=L) {
    		last = last->next;
    
    	}
    
    	node->next = last->next;
    
    	last->next = node;
    	
    	return true;
    
    }
    
    //解决约瑟问题
    //循环从链表中踢出元素、
    //这个程序的终止条件很关键,按照这个报数的规则所有的元素必须要被删除掉
    //但是循环链表结束不能机械的设置为NULL,因为报数的时候肯定要循环往复进行
    //但是删除元素的个数肯定是固定的元素的个数
    void getJfo(List*& L, int interval) {
    	//防御性编程,interval 是指间隔数,间隔肯定要大于1才能删除
    	//L结点必须存在,并且它里面肯定要存元素否则没有元素可以删除
    	if (interval < 1 || !L->next||!L) return;
    	
    	int order = 0;
    	int times = 0;
    
    	Node* q, * d;
    	q = L;
    
    
    	//计算链表中的元素个数
    	Node* CountList = L;
    	int num = 0;
    
    	while (CountList ->next != L) {
    		CountList = CountList->next;
    		num++;
    
    	}
    //计算个数,就是我们需要删除元素的个数,当删除的个数为0自动结束程序
    	while (num!=0) {
    		num--;
    	
    
    		
    		for (int i = 0; i < interval - 1; i++) {
    			//找到要删除元素的前一个结点
    			q = q->next;
    			
    			//q结点不断的指向下一个结点,因为我们设置的头节点并不储存数据
    			//所以当到头节点,自动指向下一个结点不能让头结点参与报数
    			if (q == L) {
    				q = q->next;
    			}
    
    			times++;
    
    
    
    			if (times == interval - 1) {
    
    				//找到要删除的前一个结点
    				d = q->next;
    
    				
    			
    				if (d == L) {
    				//头节点不参与报书,也不能删除头节点。当删除的结点是头节点	
    				//自动指向下一个结点	
    					d = q->next->next;
    				}
    				q->next = d->next;
    
    
    				order++;
    		cout << "第" << order << "个出圈的元素是:" << d->data << endl;
    				
    				//删除结点后自动清0,继续每interval踢出一个结点
    				times = 0;
    				delete d;
    
    
    			}
    
    
    		}
    
    	}
    	//结束之后我们让L结点恢复原位,仍然指向它自己,因为L结点自身不会被删除
    	//它要像火车头一样继续驱动,而如果不设置为L->next=L;
    	//明明没有元素,L->next已经delete掉了,但是L->next仍然会指向那个被	     
    	//delete掉了的地址但是那个结点已经不存在了
    	//再访问那个地址就会报错
    	//所以要这么一行代码,否则这个L结点后面就不能用了
    	L->next = L;
    
    }
    
    //遍历链表
    void initPrint(List*& L) {
    
    	if (!L)return;
    
    	List* p = L->next;
    
    	while (p) {
    
    
    		printf("%d\t", p->data);
    		p = p->next;
    	}
    
    	printf("\n");
    
    }
    int main() {
    
    	List* L = NULL;
    
    	if (initlist(L)) {
    
    		printf("链表初始化成功!\n");
    	}
    	else {
    		printf("链表初始化失败!\n");
    	}
    
    	//前面插入法
    	printf("请输入5个插入的值:");
    	for (int i = 0; i < 5; i++) {
    
    		Node* node = new Node;
    
    		//scanf("%d", &node->data);
    		cin>>node->data;
    
    		insert_front(L, node);
    
    	}
    
    	getJfo(L, 9);
    
    	printf("请输入5个插入的值:");
    	for (int i = 0; i < 5; i++) {
    
    		Node* node = new Node;
    
    		//scanf("%d", &node->data);
    		cin >> node->data;
    
    		insert_end(L, node);
    
    	}
    
    	getJfo(L, 9);
    
    
    	system("pause");
    	return 0;
    
    }
    
    
    
    cs