你们好,我是阿濠,今篇内容跟你们分享的是数据结构之链表,很高兴分享到segmentfault与你们一块儿学习交流,初次见面请你们多多关照,一块儿学习进步.
链表是有序的列表
,某种程度上避免数组的缺陷,即分配数组时须要开辟一串连续的内存空间
,但鱼和熊掌不可兼得,链表也牺牲了一些数组的优势:链表不能经过下标快速查询,
因此考虑是否须要链表的时候须要先考虑是否算法是否常常须要查询和遍历前端
好比说单链表它在内中存储图示:面试
介绍小结:
1)链表是以节点的方式来存储,它是以链式存储
2)每一个节点包含data域、next域 指向下一个节点. .
3)如图发现链表的各个节点地址不必定是连续存放,好比150地址下一节点指向110地址
4)链表分带头节点的链表和没有头节点的链表,根据实际的需求来肯定算法
题目:使用带head头
的单向链表
实现-水浒108位英雄排行榜管理数据库
1)完成对英雄人物的增删改查操做,注: 删除和修改,查找
2)第一种方法在添加英雄时,不按照排名直接添加到链表的尾部
3)第二种方式在添加英雄时,根据排名将英雄插入到指定位置
(若是有这个排名,则添加失败,并给出提示)segmentfault
第一种方法实现:建立示例图(添加/建立)显示思路分析数组
1.先建立一个head头节点,做用就是表示单链表的头
2.后面咱们每添加一个节点,就直接加入到链表的最后数据结构
遍历的时候
1.经过一个辅助指针,帮助遍历整个链表ide
英雄代码编写以下:学习
//定义英雄节点,每个herNode对象就是一个节点 public class HeroNode { public int no;//编号 public String name;//名称 public String nickname;//外号昵称 public HeroNode next; //指向下一个节点 //构造器 public HeroNode(int no, String name, String nickname) { this.no = no; this.name = name ; this .nickname = nickname; } @Override public String toString() { return "HeroNode [ no="+no+"],name=["+name+"],nickname=["+nickname+"]"; } }
链表管理英雄节点编写以下:测试
//链表管理英雄节点 public class SingleLinkedList { //先初始化一一个头节点,头节点不要动,不存放具体的数据 private HeroNode head = new HeroNode(0, "", ""); //添加节点到单项链表里 public void add(HeroNode heroNode) { //思路以下: //1.找到当前链表的最后节点 //2.找到最后节点的next指向新添加的节点 //3.使用辅助指向head,而后遍历找到最后节点 HeroNode temp=head; while(true){ //找到节点的next指针为空时退出 if(temp.next == null){ break; } //当前节点的next!=null 则日后移 temp=temp.next; } //break结束循环则表明找到 temp.next=heroNode; } //显示链表[遍历] public void list() { //判断链表是否为空 if(head.next == null) { System.out.println("链表为空"); return; } //分析思路提到头节点不能动,须要使用临时遍历辅助 HeroNode temp=head.next; while (true){ //若是临时遍历的下一节点指向为空 //则表明后面没有值则结束循环 if(temp == null){ break; } //不然则输出信息,并指向下一个节点继续 //由于重写了toString方法直接能够输出temp System.out.println(temp); //将temp指向下一个节点 temp=temp.next; } } }
Demo测试数据代码编写以下:
//进行建立节点数据进行测试 HeroNode heroOne=new HeroNode(1,"宋江","及时雨"); HeroNode heroTwo=new HeroNode(2,"卢俊义","玉麒麟"); HeroNode heroThree=new HeroNode(3,"吴用","智多星"); HeroNode heroFour=new HeroNode(4,"林冲","豹子头"); //建立链表 SingleLinkedList singleLinkedList = new SingleLinkedList(); //加入英雄节点 singleLinkedList.add(heroOne); singleLinkedList.add(heroTwo); singleLinkedList.add(heroThree); singleLinkedList.add(heroFour); //循环遍历链表 singleLinkedList.list(); 结果以下: HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[林冲],nickname=[豹子头]
此时显示的结果顺序是正确的,若我是修改一下代码,此时显示顺序是正确的嘛?
singleLinkedList.add(heroOne); singleLinkedList.add(heroFour); singleLinkedList.add(heroThree); singleLinkedList.add(heroTwo); 运行结果以下: HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=4],name=[林冲],nickname=[豹子头] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟]
第一个使用是松江,第二个就变成林冲了,为何?
由于是按照添加的顺序来显示的,说明没有考虑编号排序
那么第一种方法添加到链表尾部已完成,如今按照第二种方法来完成添加
第二种方法实现:建立示例图(添加/建立)显示思路分析
须要按照编号的顺序添加
1.首先找到新添加的节点的位置,经过辅助变量(指针),须要遍历搞定
2.新的节点.next = 原来 temp.next
3.将temp.next=新的节点
(好比说添加新节点数据3 它是存放在数据2后的,即节点数据2的next指针,本来指向节点数据4,如今变成新节点数据3的next节点指向节点数据4,而节点数据2的next节点变成指向新节点数据3)
链表管理英雄节点添加新方法代码编写以下:
//第二种方式在添加英雄时,根据排名将英雄插入到指定位置 //(若是有这个排名,则添加失败,并给出提示) //思路判断 //1.判断是否temp遍历已经是链表最后 //2.找到插入节点的合适位置 //3.判断插入节点是否已有对应的节点 public void addBy0rder (HeroNode heroNode) { //由于头节点不能动,所以咱们仍然经过一个辅助指针(变量)来帮助找到添加的位置 //由于单链表,由于咱们找的temp是位于添加位置的前一个节点,不然插入不了 HeroNode temp = head; boolean flag = false; // 标志添加的编号是否存在,默认为false while (true) { if (temp.next == null) { //说明temp已经在链表的最后直接添加 break; } if (temp.next.no > heroNode.no) { //temp的下一个节点的编号>添加节点的编号 //表明位置找到,就在temp的后面插入 //好比说节点2 的next指向 节点4 //此时插入节点3 那么temp指向节点,此时temp的next是节点4 //节点4的编号>节点3 表明节点3插入在节点2以后,节点4 以前 break; } else if (temp.next.no == heroNode.no) { //说明但愿添加的heroNode的编号已然存在 flag = true; break; } //若三个条件都没有知足则进行日后移 temp = temp.next; } //判断flag的值,若为true则表明节点已存在 if (flag){ System.out.printf("插入的该英雄编号 %d 已存在,不能再次添加了\n",heroNode.no); }else{ //插入到链表中,temp的后面 //新的节点下一节点指向本来temp的下一节点 heroNode.next = temp.next; //temp的下一节点指向新增的节点 temp.next = heroNode; } }
如今咱们再次添加测试数据看看可否成功呢?
//加入按照编号的顺序 singleLinkedList.addBy0rder(heroOne); singleLinkedList.addBy0rder(heroFour); singleLinkedList.addBy0rder(heroThree); singleLinkedList.addBy0rder(heroTwo); 运行结果以下: HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[林冲],nickname=[豹子头]
若此时我再添加一次豹子头林冲呢?
singleLinkedList.addBy0rder(heroFour); 运行结果以下: 插入的该英雄编号 4 已存在,不能再次添加了
根据第二方法的代码实现,咱们就能在内存中就将顺序排好,这是比数据库中还快
假如咱们给出一个新的节点,完成对原有节点进行名称、昵称修改,那么如何操做呢?
思路分析:
1.根据编号修改
2.编号不变,名称,昵称可变
链表管理英雄节点添加新方法代码编写以下:
//修改节点的信息,根据no编号来修改,即no编号不能改。 //说明根据newHeroNode的no来修改便可 public void update(HeroNode newHeroNode) { //判断是否空 //由于head是没有数据的 if (head.next == null) { System.out.println("链表为空~"); return; } //找到须要修改的节点,根据no编号 //定义一个辅助变量 HeroNode temp = head.next; boolean flag = false; while(true){ if (temp == null) { break; //已经遍历完链表 } //根据temp的值 if(temp.no == newHeroNode.no) { //找到 flag = true; break; }//没有找到日后移 temp = temp. next; } if(flag){ //原节点进行修改信息 temp.name=newHeroNode.name; temp.nickname=newHeroNode.nickname; }else{ //没有找到的时候仍然为false System.out.printf("没有找到编号 %d 的节点,不能修改\n",newHeroNode.no) ; } }
如今咱们测试数据看看可否成功呢?好比将林冲修改成小白,豹子头改成小白龙
HeroNode hero=new HeroNode(4,"小白","小白龙"); singleLinkedList.update(hero); //显示数据 singleLinkedList.list(); 运行结果以下: 修改信息后的数据-------------- HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[小白],nickname=[小白龙]
假设我如今有节点数据如上,若我想删除数据4,该怎么操做嘛?
思路分析:
1.仍然须要temp辅助节点,找到须要删除节点的前一个节点temp
2.修改temp.next=temp.next.next
(好比说节点2的next节点是节点4 修改成节点2的next节点的next节点,便是节点4的next节点)
链表管理英雄节点添加新方法代码编写以下:
//思路 //1. head 不能动,所以咱们须要-个temp辅助节点找到待删除节点的前一个节点 //2.说明咱们在比较时,是temp .next.no和须要删除的节点的no比较 public void del(int no) { HeroNode temp = head; boolean flag = false; //标志是否找到待删除节点的 while(true) { if(temp.next == null ) { //已经到链表 的最后 break; } if(temp.next.no == no) { //找到的待删除节点的前一个节点temp flag = true; break; } temp = temp.next; //temp后移遍历 } //判断flag if(flag) { //找到 . //能够删除 temp.next = temp. next. next; }else { System .out .printf("要删除的%d节点不存在",no); } }
如今咱们测试数据看看可否成功呢?好比将宋江删除
//删除一个节点 singleLinkedList.del(1); System .out .println("删除后的链表状况--------------"); singleLinkedList.list(); 运行结果以下: HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[小白],nickname=[小白龙] 删除后的链表状况-------------- HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[小白],nickname=[小白龙]
1)求单链表中节点的个数
2)查找单链表中的倒数第k个结点[ 新浪面试题]
3)单链表的反转[腾讯面试题]
4)从尾到头打印单链表[百度,要求方式1:反向遍历。方式2: Stack栈]
5)合并两个有序的单链表,合并以后的链表依然有序
题目一:
思路分析:
获取到单链表的节点的个数(若是是带头结点的链表,需求不统计头节点)
/** * @param head 链表的头节点 * @return 返回的就是有效节点的个数 */ public static int getLength(HeroNode head) { if(head.next == null) { //空链表 return 0; } int length = 0; //定义一个辅助的变量 HeroNode cur = head .next ; while(cur != null) { length++; cur = cur.next; //遍 历 } return length; }
题目二:
思路分析:
1.编写一个方法,接收head节点,同时接收一个index
2.index 表示是倒数第index个节点
3.先把链表从头至尾遍历,获得链表的总的长度getLength
4.获得size后,咱们从链表的第-个开始遍历(size-index)个,就能够获得
5.若是找到了,则返回该节点,不然返回nu111
//查找单链表中的倒数第k个结点[新浪面试题] public static HeroNode findLastIndexNode(HeroNode head, int index) { //判断若是链表为空,返回null if (head.next == null) { return null;//没有找到 } //第一个遍历获得链表的长度(节点个数) int size = getLength(head); //第二次遍历size-index 位置,就是咱们倒数的第K个节点 //先作index的校验 // 不能说一共才5个,你要求找倒数第六个数或者负数 if (index <= 0 || index > size) { return null; //没有找到 } //定义给辅助变量,for循环定位到倒数的index HeroNode cur = head.next; //好比说有效数据为三个 找倒数第一个 // 3-1 = 2 二次就找到了 for (int i = 0; i < size - index; i++) { cur = cur.next; } return cur; }
题目三:
思路分析:
1.先定义一个节点reverseHead =new HeroNode();
2.从头至尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端.
3.原来的链表的head.next =reverseHead.next
//将单链表反转 public static void reversetList(HeroNode head) { //若是当前链表为空,或者只有一个节点,无需反转,直接返回 //链表里只有一个节点时,它的next节点也是空的 if (head.next == null || head.next.next == null) { return; } //定义一个辅助的指针(变量),帮助咱们遍历原来的链表 HeroNode cur = head.next; HeroNode next = null;//指向当前节点[cur]的下一个节点. HeroNode reverseHead = new HeroNode(0,"",""); //遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端 //动脑筋 while(cur != null) { next = cur.next;//先暂时保存当前节点的下一个节点,由于后面须要使用 cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端 reverseHead.next = cur; //将cur 链接到新的链表上 cur = next;//icur后移 } //将head. next指向reverseHead.next, 实现单链表的反转 head.next = reverseHead. next; }
题目四:
思路分析:
要求就是逆序打印单链表.
1.方式1:先将单链表进行反转操做,而后再遍历便可,这样的作的问题是会破坏原来的单链表的结构,不建议
2.方式2:能够利用栈
这个数据结构,将各个节点压入到栈中
,而后利用栈的先进后出
的特色,就实现了逆序打印的效果
//演示栈Stack的基本使用 public class TestStack { public static void main(String[] args) { Stack<String> stack = new Stack(); //入栈 stack.add("jack"); stack.add("tom"); stack.add("smith"); //出栈 while (stack.size() > 0) { System.out.println(stack. pop());//pop就是将栈顶的数据取出 } } } 运行结果以下: smith tom jack
那么咱们发现栈是符合这个要求的,可使用栈进行逆序打印链表
//方式2:链表结构顺序没有改变,使用逆序打印 //能够利用栈这个数据结构,将各个节点压入到栈中,而后利用栈的先进后出的特色,就实现了逆序打印的效果 public static void reversePrint(HeroNode head) { if(head.next == null) { return;//空链表,不能打印 } //建立要给一个栈,将各个节点压入栈 Stack<HeroNode> stack = new Stack<HeroNode>(); HeroNode cur = head.next; //将链表的全部节点压入栈 while(cur != null) { stack.push(cur); cur = cur.next; //cur后移,这样就能够压入下一个节点 } //将栈中的节点进行打印,pop出栈 while (stack.size() > 0) { System.out. println(stack.pop()); } }
刚刚经过示例认识到单链表,可是单链表相比双链表也是有些缺点的
1)单向链表:查找的方向只能是一个方向
,而双向链表能够向前或者向后查找
。
2)单向链表不能自我删除
,须要靠辅助节点
,而双向链表,则能够自我删除
,因此前面咱们单链表删除时节点,老是须要找到temp,而temp是待删除节点的的前一个节点
3)示意图理解区别
分析双向链表的遍历,添加,修改,删除的操做思路
1>:遍历方法和单链表同样
,只是能够向前,也能够向后查找
2>:添加:(默认添加)到双向链表的最后
,须要先找到找到双向链表的最后这个节点
2.1>:最后节点temp.next = 新节点 newHeroNode
2.2>:新节点 newHeroNode.pre = 最后节点 temp
3>:修改思路原理和单链表同样
4>: 删除:由于是双向链表 能够实现自我删除某个节点,直接找到须要删除的节点temp
4.1>:temp.pre.next=temp.next;
4.2>:temp.next.pre=temp.pre;
题目:使用带head头
的单向链表
实现-水浒108位英雄排行榜管理
英雄代码编写以下:
//定义英雄节点,每个herNode对象就是一个节点 public class HeroNode2 { public int no; public String name; public String nickname; public HeroNode2 next; //指向下一个节点 默认为null public HeroNode2 pre; //指向上一个节 默认为null //构造器 public HeroNode2(int no, String name, String nickname) { this.no = no; this.name = name ; this .nickname = nickname; } @Override public String toString() { return "HeroNode [ no="+no+"],name=["+name+"],nickname=["+nickname+"]"; } }
双链表管理英雄节点代码编写以下:
package com.bdqn.it.test; public class DouobleLikedList { //先初始化一一个头节点,头节点不要动,不存放具体的数据 private HeroNode2 head = new HeroNode2(0, "", ""); //显示链表[遍历] public void list() { //判断链表是否为空 if (head.next == null) { System.out.println("链表为空"); return; } //分析思路提到头节点不能动,须要使用临时遍历辅助 HeroNode2 temp = head.next; while (true) { //若是临时遍历的下一节点指向为空 //则表明后面没有值则结束循环 if (temp == null) { break; } //不然则输出信息,并指向下一个节点继续 //由于重写了toString方法直接能够输出temp System.out.println(temp); //将temp指向下一个节点 temp = temp.next; } } //添加节点 public void add(HeroNode2 heroNode) { HeroNode2 temp = head; while (true) { //找到节点的next指针为空时退出 if (temp.next == null) { break; } //当前节点的next!=null 则日后移 temp = temp.next; } //break结束循环则表明找到 temp.next = heroNode; //新增节点的前一个节点为当前最后一个节点 heroNode.pre=temp; } //修改节点的信息,根据no编号来修改,即no编号不能改。 //说明根据newHeroNode的no来修改便可 public void update(HeroNode2 newHeroNode) { //判断是否空 //由于head是没有数据的 if (head.next == null) { System.out.println("链表为空~"); return; } //找到须要修改的节点,根据no编号 //定义一个辅助变量 HeroNode2 temp = head.next; boolean flag = false; while (true) { if (temp == null) { break; //已经遍历完链表 } //根据temp的值 if (temp.no == newHeroNode.no) { //找到 flag = true; break; }//没有找到日后移 temp = temp.next; } if (flag) { temp.name = newHeroNode.name; temp.nickname = newHeroNode.nickname; } else { //没有找到的时候仍然为false System.out.printf("没有找到编号 %d 的节点,不能修改\n", newHeroNode.no); } } //从双向链表中删除节点 //思路 //1.对于双向链表,咱们能够直接找到要删除的这个节点 //2.找到后,自我删除便可 public void del(int no) { if (head.next == null) { //空链表 System.out.println("链表为空,没法删除!"); } boolean flag = false; //标志是否找到待删除节点的 //直接指向删除的节点 HeroNode2 temp = head.next; while (true) { if (temp == null) { //空链表 //已经到了链表最后的后一个 break; } if (temp.no == no) { //找到的待删除节点的前一个节点temp flag = true; break; } temp = temp.next; //temp后移遍历 } //判断flag if (flag) { //找到 . //能够删除 temp.pre.next = temp.next; if(temp.next!=null){ //若删除的节点是最后,则会出现空指针异常 temp.next.pre=temp.pre; } } else { System.out.printf("要删除的%d节点不存在", no); } } }
如今咱们添加测试数据看看可否操做成功?
//建立节点作数据测试 HeroNode2 heroOne=new HeroNode2(1,"宋江","及时雨"); HeroNode2 heroTwo=new HeroNode2(2,"卢俊义","玉麒麟"); HeroNode2 heroThree=new HeroNode2(3,"吴用","智多星"); HeroNode2 heroFour=new HeroNode2(4,"林冲","豹子头"); //建立链表 DoubleLinkedList doubleLinkedList = new DoubleLinkedList(); //加入 doubleLinkedList.add(heroOne); doubleLinkedList.add(heroTwo); doubleLinkedList.add(heroThree); doubleLinkedList.add(heroFour); System.out.println("当前双链表数据-------------"); doubleLinkedList.list(); System.out.println("修改英雄4,当前链表状况--------------"); HeroNode2 hero=new HeroNode2(4,"小白","小白龙"); doubleLinkedList.update(hero); doubleLinkedList.list(); //删除一个节点 doubleLinkedList.del(1); System .out .println("删除英雄1,当前链表状况--------------"); doubleLinkedList.list(); 运行结果以下: 当前双链表数据------------- HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[林冲],nickname=[豹子头] 修改英雄4,当前链表状况-------------- HeroNode [ no=1],name=[宋江],nickname=[及时雨] HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[小白],nickname=[小白龙] 删除英雄1,当前链表状况-------------- HeroNode [ no=2],name=[卢俊义],nickname=[玉麒麟] HeroNode [ no=3],name=[吴用],nickname=[智多星] HeroNode [ no=4],name=[小白],nickname=[小白龙]
Josephu问题为:
设编号为1,2 ... n的n我的围坐一圈,约定编号为k
(1<=k<=n)的人从1开始
报数数到m
的那我的出列,它的下一位又从1开始
报数,数到m
的那我的又出列,依次类推,直到全部人出列为止
示意图分析思路:
由此产生一个出队编号的序列。
提示:用一个不带头节点
的循环链表
来处理Josephu问题
1.先构成一个有n个节点的单循环链表
,而后由k结点
起从1开始
计数计到m时
,对应节点
从链表中删除
2.再从被删除
节点的下一个节点
又从1开始
计数,直到最后
一个节点
从链表中删除
结束。
构建一个单向的环形链表思路
1.先建立第一个节点,让first指向该节点,并造成环形.
2.后面当咱们每建立一个新的节点,就把该节点,加入到已有的环形链表中便可.
遍历环形链表
1.先让一个辅助指针(变量)curBoy,指向first节点
2.而后经过一个while循环遍历该环形链表便可curBoy.next
丢手绢小孩代码编写以下:
//建立- -个Boy类,表示一个节点 public class Boy { private int no;//编号 private Boy next; //指向下一个 节点,默认null public int getNo() { return no; } public void setNo(int no) { this.no = no; } public Boy getNext() { return next; } public void setNext(Boy next) { this.next = next; } }
单向环形链表管理节点代码编写以下:
//建立管理环形的单向链表 class CircleSingleLinkedList { //建立一个first节点,当前没有编号 private Boy first = null ; //添加小孩节点,构建成一个环形的链表 public void createLinkedList(int nums){ //num 作一个数据校验 if(nums < -1){ System.out.println("num的值不正确"); return; } Boy curBoy = null; //辅助指针,帮助构建环形链表 //使用for来建立咱们的环形链表 for(int i = 1; i <= nums; i++) { //根据编号,建立小孩节点 Boy boy = new Boy(i); //若是是第一个小孩 if (i == 1) { first = boy; first.setNext(first); //构成环 curBoy = first; //让curBoy指向第一个小孩 } else { curBoy.setNext(boy);//当前boy指向新的小孩 boy.setNext(first);//新小孩指向第一个小孩 curBoy = boy;//当前boy=新小孩 } } } //遍历当前的环形链表 public void showBoy() { //判断链表是否为空 if(first == null) { System. out. println("没有任何小孩~"); return; } //由于first不能动,所以咱们仍然使用一个辅助指针完成遍历 Boy curBoy = first; while(true) { System.out. printf("小孩的编号%d \n", curBoy. getNo()); //说明已经遍历完 if(curBoy. getNext() == first) {毕 break; } //curBoy后移 curBoy = curBoy. getNext(); } } }
先让咱们完成第一步,看看有多少个小孩参加玩丢手绢的游戏?
//测试-把看看构建环形链表,和遍历是否ok CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList(); circleSingleLinkedList.createLinkedList(5);//加入5个小孩玩游戏 circleSingleLinkedList.showBoy(); 运行结果以下: 小孩的编号1 小孩的编号2 小孩的编号3 小孩的编号4 小孩的编号5
那么如何按照报数出序列呢?
代码实现根据用户输入计算出圈顺序
//根据用户的输入,计算出小孩出圈的顺序 /** * @param startNo 表示从第几个小孩开始数数 * @param countNum 表示数几下 * @param nums 表示最初有多少小孩在圈中 */ public void countBoy(int startNo, int countNum, int nums) { //先对数据进行校验 //校验环形链表是否有数据,空链表没法出 //校验从第几个开始小于1,从-1个小孩开始没法出 //校验从第几个开始大于环形链表里的值,从5个小孩里第6个没法出 if (first == null || startNo < 1 || startNo > nums) { System.out.println("参数输入有误,请从新输入"); return; } //建立要给辅助指针,帮助完成小孩出圈 Boy helper = first; //需求建立-个辅助指针(变量) helper ,事先应该指向环形链表的最后这个节点 while (true) { //说明he1per指向最后小孩节点 if (helper.getNext() == first) { break; } //后移不断找到最后节点 helper = helper.getNext(); } //报数前先让first和helper 移动 startNo - 1 次 //若从二个小孩开始此时first 指向第一个小孩 //则须要移动 2 - 1 次 将first指向要报数的小孩 for (int j = 0; j < startNo - 1; j++) { first = first.getNext(); helper = helper.getNext(); } //此时first 指向报数的小孩 while(true) { //说明圈中只留下最后一个小孩 if(helper == first) { break; } //好比说每一个三 就出圈 此时first 指向的是报数的小孩 则表明=1 //只须要 3 - 1 则找到要出圈的小孩 而后出圈直到只有一个节点 //让 first 和 helper 指针同时的移动 countNum - 1 for(int j=0;j<countNum-1;j++){ first = first.getNext(); helper = helper .getNext(); } //这时first指向的节点,就是要出圈的小孩节点 System.out.printf("小孩%d出圈 \n",first.getNo()); //将小孩出圈 first=first.getNext(); helper.setNext(first); } System.out.printf("最后留在圈中的小孩 %d \n",first.getNo()); }
来看看刚刚添加五个小孩的出圈顺序吧
//测试一把小孩出圈是否正确 //一共有五个小孩 //出圈规则:从第一个小孩开始数二下出圈 circleSingleLinkedList.countBoy(1, 2, 5); 运行结果以下: 小孩2出圈 小孩4出圈 小孩1出圈 小孩5出圈 最后留在圈中的小孩 3