文章目录

  • 第三章 链表
    • 单链表介绍和内存布局
    • 单链表创建和遍历的分析实现
      • 添加(创建)过程
      • 遍历过程
      • 代码实现
    • 单链表按顺序插入节点
    • 单链表节点的修改
    • 单链表节点的删除和小结
    • 单链表面试题
      • 新浪面试题
      • 腾讯面试题
      • 百度面试题
      • 课后练习
    • 双向链表增删改查分析图解及实现
    • 环形链表介绍和约瑟夫问题
    • 约瑟夫问题分析图解和实现
    • 本章导图总结

第三章 链表

本章源码:https://github.com/name365/Java-Data-structure

单链表介绍和内存布局

链表是有序的列表,但是它在内存中是实际存储结构如下:

小结:
1.链表是以节点的方式来存储,是链式存储(即各个节点之间并不一定是连续存储的,而是相互指向的);
2.每个节点包含 data 域:存放数据的域, next 域:指向下一个节点;
3.如图:发现链表的各个节点不一定是连续存储;
4.链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定.

单链表(带头结点) 逻辑结构示意图如下:

单链表创建和遍历的分析实现

  • 先看一个例子

使用带head头的单向链表实现 –水浒英雄排行榜管理

1)完成对英雄人物的增删改查操作, 注: 删除和修改,查找可以考虑学员独立完成,也可带学员完成

2)第一种方法在添加英雄时,直接添加到链表的尾部

3)第二种方式在添加英雄时,根据排名将英雄插入到指定位置
(如果有这个排名,则添加失败,并给出提示)

  • 单链表的创建示意图(添加), 显示单向链表的分析
class HeroNode {int no;String name;String nickName;HeroNode next;
}

添加(创建)过程

  • 先创建一个head 头节点, 作用就是表示单链表的头;

  • 之后每添加一个节点,就直接加入到—》链表的最后。


遍历过程

  • 通过一个辅助变量(临时变量temp)遍历,帮助遍历整个链表。




代码实现

/** 此代码添加数据有问题,待优化*/
public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加数据到链表singk.add(hero);singk.add(hero2);singk.add(hero3);singk.add(hero4);singk.add(hero5); //显示链表singk.list();}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//添加节点到单向链表//思路,当不考虑编号的顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next域指向这个新的节点public void add(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;//遍历链表,找到最后while(true){//找到链表的最后if(temp.next == null){  //判定找到了的条件break;}//如果没有找到,将temp后移temp = temp.next;}//当退出循环时,temp就指向了链表的最后//将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}
}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

单链表按顺序插入节点

  • 需要按照编号的顺序添加
  • 首先找到新添加的节点的位置, 是通过辅助变量(指针), 通过遍历来搞定

  • 新的节点.next = temp.next

  • 将temp.next = 新的节点

  • 代码实现
/** 此代码已优化*/
public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加数据到链表
//      singk.add(hero);
//      singk.add(hero2);
//      singk.add(hero4);
//      singk.add(hero3);
//      singk.add(hero5); //添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero4);singk.addByOrder(hero2);singk.addByOrder(hero5); singk.addByOrder(hero3);
//      singk.addByOrder(hero3);//显示链表singk.list();}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//第一种方式:第一种方法在添加英雄时,直接添加到链表的尾部//添加节点到单向链表,思路,当不考虑编号的顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next域指向这个新的节点public void add(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;//遍历链表,找到最后while(true){//找到链表的最后if(temp.next == null){  //判定找到了的条件break;}//如果没有找到,将temp后移temp = temp.next;}//当退出循环时,temp就指向了链表的最后//将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}//第二种方式在添加英雄时,根据排名将英雄插入到指定位置//(如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置//由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false;    //标识添加的编号是否存在,默认为falsewhile(true){if(temp.next == null){   //说明已经在链表的最后break;  }if(temp.next.id > heroNode.id){ //位置找到了,就在temp的后面插入break;}else if(temp.next.id == heroNode.id){    //说明希望添加的heroNode的编号已然存在flag = true;   //说明编号存在break;}temp = temp.next;   //后移,遍历当前链表}//判断flag的值if(flag){ //不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n",heroNode.id);}else{//插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

单链表节点的修改

public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero4);singk.addByOrder(hero2);singk.addByOrder(hero5); singk.addByOrder(hero3);//显示链表singk.list();//测试修改节点的代码HeroNode hero6 = new HeroNode(5,"渣渣辉","一刀999");singk.update(hero6);//显示链表System.out.println("修改后的链表情况:");singk.list();}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//修改节点的信息,根据id编号来修改,即id编号不改//说明//1.根据newHeroNode 的 id 来修改即可public void update(HeroNode newHeroNode){//判断是否为空if(head.next == null){  //链表为空System.out.println("链表为空!!!");return;}//找到需要修改的节点,根据 id 编号//定义一个临时变量HeroNode temp = head.next;boolean flag = false;  //表示是否找到该节点while(true){if(temp == null){break;    //已经遍历完链表}if(temp.id == newHeroNode.id){//找到了flag = true;break;}temp = temp.next;}//根据flag 判断是否找到要修改的节点if(flag){temp.name = newHeroNode.name;temp.nickName = newHeroNode.nickName;}else{System.out.printf("没有找到编号为 %d 的节点.\n",newHeroNode.id);}}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}//第二种方式在添加英雄时,根据排名将英雄插入到指定位置//(如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置//由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false;    //标识添加的编号是否存在,默认为falsewhile(true){if(temp.next == null){   //说明已经在链表的最后break;  }if(temp.next.id > heroNode.id){ //位置找到了,就在temp的后面插入break;}else if(temp.next.id == heroNode.id){    //说明希望添加的heroNode的编号已然存在flag = true;   //说明编号存在break;}temp = temp.next;   //后移,遍历当前链表}//判断flag的值if(flag){ //不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n",heroNode.id);}else{//插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

单链表节点的删除和小结

从单链表中删除一个节点的思路图解

  1. 我们先找到 需要删除的这个节点的前一个节点 temp

  1. temp.next = temp.next.next

  1. 被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收
public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero4);singk.addByOrder(hero2);singk.addByOrder(hero5); singk.addByOrder(hero3);//显示链表singk.list();//删除一个节点singk.del(2);singk.del(1);singk.del(3);singk.del(4);singk.del(5);System.out.println("删除后的情况:");singk.list();  //显示链表}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//删除节点//思路//1.由于头节点head不能动,所以需要一个辅助变量 temp来找到待删除节点的前一个节点//2.说明我们在比较时,是temp.next.id 和  需要删除的节点的id比较public void del(int id){HeroNode temp = head;boolean flag = false; //标志是否找到带删除的节点while(true){if(temp.next == null){  //已经到链表的最后break;}if(temp.next.id == id){  //找到了带删除的节点的前一个节点flag = true;break;}temp = temp.next; //temp后移,遍历}//判断flagif(flag){    //说明找到了//可以删除temp.next = temp.next.next;}else{System.out.printf("要删除的节点 %d 不存在。\n",id);}}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}//第二种方式在添加英雄时,根据排名将英雄插入到指定位置//(如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置//由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false;    //标识添加的编号是否存在,默认为falsewhile(true){if(temp.next == null){   //说明已经在链表的最后break;  }if(temp.next.id > heroNode.id){ //位置找到了,就在temp的后面插入break;}else if(temp.next.id == heroNode.id){    //说明希望添加的heroNode的编号已然存在flag = true;   //说明编号存在break;}temp = temp.next;   //后移,遍历当前链表}//判断flag的值if(flag){ //不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n",heroNode.id);}else{//插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

单链表面试题

单链表的常见面试题有如下:

1)求单链表中有效节点的个数

2)查找单链表中的倒数第k个结点 【新浪面试题】

3)单链表的反转【腾讯面试题,有点难度】

4)从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】

5)合并两个有序的单链表,合并之后的链表依然有序【课后练习.】

  • 求单链表中有效节点的个数
public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero4);singk.addByOrder(hero2);singk.addByOrder(hero5); singk.addByOrder(hero3);//显示链表singk.list();//测试一下:求单链表中有效节点的个数System.out.println("单链表有效的节点个数 = " + getLength(singk.getHead()));//5}//获取单链表的节点的个数(如果是带头节点的链表,需求不统计头结点)/*** * @Description * @author subei* @date 2020年5月22日下午5:09:46* @param head 链表的头节点* @return 返回的是有效的节点的个数*/public static int getLength(HeroNode head){if(head.next == null){return 0;}int length = 0;//定义一个临时的变量,未统计头节点HeroNode str = head.next;while(str != null){length++;str = str.next;   //即遍历}return length;}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//返回头节点public HeroNode getHead() {return head;}//第一种方式:第一种方法在添加英雄时,直接添加到链表的尾部//添加节点到单向链表,思路,当不考虑编号的顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next域指向这个新的节点public void add(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;//遍历链表,找到最后while(true){//找到链表的最后if(temp.next == null){   //判定找到了的条件break;}//如果没有找到,将temp后移temp = temp.next;}//当退出循环时,temp就指向了链表的最后//将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}//修改节点的信息,根据id编号来修改,即id编号不改//说明//1.根据newHeroNode 的 id 来修改即可public void update(HeroNode newHeroNode){//判断是否为空if(head.next == null){    //链表为空System.out.println("链表为空!!!");return;}//找到需要修改的节点,根据 id 编号//定义一个临时变量HeroNode temp = head.next;boolean flag = false;  //表示是否找到该节点while(true){if(temp == null){break;    //已经遍历完链表}if(temp.id == newHeroNode.id){//找到了flag = true;break;}temp = temp.next;}//根据flag 判断是否找到要修改的节点if(flag){temp.name = newHeroNode.name;temp.nickName = newHeroNode.nickName;}else{System.out.printf("没有找到编号为 %d 的节点.\n",newHeroNode.id);}}//删除节点//思路//1.由于头节点head不能动,所以需要一个辅助变量 temp来找到待删除节点的前一个节点//2.说明我们在比较时,是temp.next.id 和  需要删除的节点的id比较public void del(int id){HeroNode temp = head;boolean flag = false;   //标志是否找到带删除的节点while(true){if(temp.next == null){  //已经到链表的最后break;}if(temp.next.id == id){  //找到了带删除的节点的前一个节点flag = true;break;}temp = temp.next; //temp后移,遍历}//判断flagif(flag){    //说明找到了//可以删除temp.next = temp.next.next;}else{System.out.printf("要删除的节点 %d 不存在。\n",id);}}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}//第二种方式在添加英雄时,根据排名将英雄插入到指定位置//(如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置//由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false;    //标识添加的编号是否存在,默认为falsewhile(true){if(temp.next == null){   //说明已经在链表的最后break;  }if(temp.next.id > heroNode.id){ //位置找到了,就在temp的后面插入break;}else if(temp.next.id == heroNode.id){    //说明希望添加的heroNode的编号已然存在flag = true;   //说明编号存在break;}temp = temp.next;   //后移,遍历当前链表}//判断flag的值if(flag){ //不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n",heroNode.id);}else{//插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

新浪面试题

public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero4);singk.addByOrder(hero2);singk.addByOrder(hero5); singk.addByOrder(hero3);//显示链表singk.list();//测试一下:求单链表中有效节点的个数System.out.println("单链表有效的节点个数 = " + getLength(singk.getHead()));//5//测试一下:查找单链表中的倒数第k个结点 HeroNode res = findLastIndexNode(singk.getHead(), 4);System.out.println("res = "+ res);}//查找单链表中的倒数第k个结点 //思路://1.编写一个方法,接收head节点,同时接收一个index //2.index 表示是倒数第index个节点//3.先把链表从头到尾遍历,得到链表的总的长度 getLength//4.得到size 后,我们从链表的第一个开始遍历 (size-index)个,就可以得到//5.如果找到了,则返回该节点,否则返回nullpublic static HeroNode findLastIndexNode(HeroNode head, int index) {//判断如果链表为空,返回nullif(head.next == null) {return null;//没有找到}//第一个遍历得到链表的长度(即节点个数)int size = getLength(head);//第二次遍历  size-index 位置,即倒数的第K个节点//先做一个index的校验if(index <= 0 || index > size) {return null; }//第一一个临时变量,for 循环定位到倒数的indexHeroNode str = head.next; //3 // 3 - 1 = 2for(int i =0; i< size - index; i++) {str = str.next;}return str;}//获取单链表的节点的个数(如果是带头节点的链表,需求不统计头结点)/*** * @Description * @author subei* @date 2020年5月22日下午5:09:46* @param head 链表的头节点* @return 返回的是有效的节点的个数*/public static int getLength(HeroNode head){if(head.next == null){return 0;}int length = 0;//定义一个临时的变量,未统计头节点HeroNode str = head.next;while(str != null){length++;str = str.next; //即遍历}return length;}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//返回头节点public HeroNode getHead() {return head;}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}//第二种方式在添加英雄时,根据排名将英雄插入到指定位置//(如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置//由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false;   //标识添加的编号是否存在,默认为falsewhile(true){if(temp.next == null){   //说明已经在链表的最后break;  }if(temp.next.id > heroNode.id){ //位置找到了,就在temp的后面插入break;}else if(temp.next.id == heroNode.id){    //说明希望添加的heroNode的编号已然存在flag = true;   //说明编号存在break;}temp = temp.next;   //后移,遍历当前链表}//判断flag的值if(flag){ //不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n",heroNode.id);}else{//插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

腾讯面试题

单链表的反转,效果图如下:

思路:

  1. 先定义一个节点 reverseHead = new HeroNode();

  2. 从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead 的最前端.

  3. 原来的链表的head.next = reverseHead.next

具体如下图:





public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.add(hero);singk.add(hero4);singk.add(hero2);singk.add(hero5); singk.add(hero3);//测试一下:单链表的反转功能System.out.println("原来链表的情况:");singk.list(); //显示链表System.out.println("反转的单链表:");reversetList(singk.getHead());singk.list();   }//单链表反转public static void reversetList(HeroNode head) {//如果当前链表为空,或者只有一个节点,无需反转,直接返回if(head.next == null || head.next.next == null) {return;}//1.定义一个辅助的指针(变量),帮助我们遍历原来的链表HeroNode str = head.next;HeroNode next = null;//指向当前节点[str]的下一个节点HeroNode reverseHead = new HeroNode(0, "", "");//2.遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端while(str != null) { //先暂时保存当前节点的下一个节点,因为后面需要使用next = str.next;//将str的下一个节点指向新的链表的最前端str.next = reverseHead.next;reverseHead.next = str; //将str连接到新的链表上str = next;   //让str后移}//3.将head.next 指向 reverseHead.next, 实现单链表的反转head.next = reverseHead.next;}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//返回头节点public HeroNode getHead() {return head;}//第一种方式:第一种方法在添加英雄时,直接添加到链表的尾部//添加节点到单向链表,思路,当不考虑编号的顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next域指向这个新的节点public void add(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;//遍历链表,找到最后while(true){//找到链表的最后if(temp.next == null){   //判定找到了的条件break;}//如果没有找到,将temp后移temp = temp.next;}//当退出循环时,temp就指向了链表的最后//将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

百度面试题

从尾到头打印单链表

思路:
1. 上面的题的要求就是逆序打印单链表.
2. 方式1: 先将单链表进行反转操作,然后再遍历即可,这样的做的问题是会破坏原来的单链表的结构,不建议
3. 方式2:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果.举例演示栈的使用 Stack

  • 入栈出栈的代码演示
import java.util.Stack;//演示栈Stack的基本使用
public class TestStack {public static void main(String[] args) {Stack<String> stack = new Stack();//入栈stack.add("jack");stack.add("Tom");stack.add("smith");//出栈//出栈顺序:smith-->Tom-->jackwhile(stack.size() > 0){System.out.println(stack.pop()); //pop()就是将栈顶的数据取出}}
}
  • 面试题代码实现
import java.util.Stack;public class SingleLinkedListTest {public static void main(String[] args) {//测试一下//1.创建节点HeroNode hero = new HeroNode(1,"宋江","及时雨");HeroNode hero2 = new HeroNode(2,"卢俊义","玉麒麟");HeroNode hero3 = new HeroNode(3,"吴用","智多星");HeroNode hero4 = new HeroNode(4,"公孙胜","入云龙");HeroNode hero5 = new HeroNode(5,"关胜","大刀");//创建一个链表SingleLinkedList singk = new SingleLinkedList();//添加按照编号的顺序singk.add(hero);singk.add(hero4);singk.add(hero2);singk.add(hero5); singk.add(hero3);System.out.println("原来链表的情况:");singk.list(); //显示链表System.out.println("测试逆序打印的单链表,未改变链表的本身结构:");reversePrint(singk.getHead()); }//方式2://可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果public static void reversePrint(HeroNode head) {if(head.next == null){return;    //空链表,无法打印}//先创建一个栈,将各个节点压入栈中Stack<HeroNode> stack = new Stack<HeroNode>();HeroNode str = head.next;//将链表的所有节点压入栈while(str != null){stack.push(str);  str = str.next;    //str后移,这样就可以压入下一个节点}//将栈中的节点进行打印,pop()出栈while(stack.size() > 0){System.out.println(stack.pop());    //stack的特点是先进后出}}}//定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList{//先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");//返回头节点public HeroNode getHead() {return head;}//第一种方式:第一种方法在添加英雄时,直接添加到链表的尾部//添加节点到单向链表,思路,当不考虑编号的顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next域指向这个新的节点public void add(HeroNode heroNode){//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;//遍历链表,找到最后while(true){//找到链表的最后if(temp.next == null){   //判定找到了的条件break;}//如果没有找到,将temp后移temp = temp.next;}//当退出循环时,temp就指向了链表的最后//将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}//显示链表[遍历]public void list(){//判断链表是否为空if(head.next == null){System.out.println("链表为空!!!");return;}//由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while(true){//判断是否到链表的最后if(temp == null){break;}//如果不为空,输出节点的信息System.out.println(temp);//注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}}//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{public int id;public String name;public String nickName; //别名,昵称public HeroNode next;    //指向下一个节点//构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}//为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

课后练习

合并两个有序的单链表,合并之后的链表依然有序。

public class SingleLinkedListTest {public static void main(String[] args) {// 测试一下// 1.创建节点HeroNode hero = new HeroNode(1, "宋江", "及时雨");HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");HeroNode hero3 = new HeroNode(3, "吴用", "智多星");HeroNode hero4 = new HeroNode(4, "公孙胜", "入云龙");HeroNode hero5 = new HeroNode(5, "关胜", "大刀");HeroNode hero6 = new HeroNode(6, "林冲", "豹子头");HeroNode hero7 = new HeroNode(7, "秦明", "霹雳火");// 创建一个链表SingleLinkedList singk = new SingleLinkedList();// 添加按照编号的顺序singk.addByOrder(hero);singk.addByOrder(hero7);singk.addByOrder(hero5);// 创建另一个链表SingleLinkedList singk2 = new SingleLinkedList();// 添加按照编号的顺序singk2.addByOrder(hero3);singk2.addByOrder(hero6);singk2.addByOrder(hero2);singk2.addByOrder(hero4);// 显示链表singk.list();System.out.println("------------------");singk2.list();//合并后的System.out.println("合并后的:");SingleLinkedList singk3 = singk.merge(singk, singk2);singk3.list();}}// 定义一个SingleLinkedList类,来管理我们的英雄
class SingleLinkedList {// 先初始化一个头节点,头节点不要动(防止后期找不到此链表),不存放具体数据private HeroNode head = new HeroNode(0, "", "");public SingleLinkedList(HeroNode head) {this.head = head;}public SingleLinkedList() {head = new HeroNode();}// 显示链表[遍历]public void list() {// 判断链表是否为空if (head.next == null) {System.out.println("链表为空!!!");return;}// 由于头节点head不能动,所以需要一个辅助变量 tempHeroNode temp = head;while (true) {// 判断是否到链表的最后if (temp == null) {break;}// 如果不为空,输出节点的信息System.out.println(temp);// 注意!!!!将next后移(因为不向后移动,会造成死循环)temp = temp.next;}}// 第二种方式在添加英雄时,根据排名将英雄插入到指定位置// (如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode) {// 由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置// 由于是单链表,而找到temp是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag = false; // 标识添加的编号是否存在,默认为falsewhile (true) {if (temp.next == null) { // 说明已经在链表的最后break;}if (temp.next.id > heroNode.id) { // 位置找到了,就在temp的后面插入break;} else if (temp.next.id == heroNode.id) { // 说明希望添加的heroNode的编号已然存在flag = true; // 说明编号存在break;}temp = temp.next; // 后移,遍历当前链表}// 判断flag的值if (flag) { // 不能添加,说明编号存在System.out.printf("准备插入的英雄编号%d已经存在了。无法加入\n", heroNode.id);} else {// 插入到链表中,temp的后面heroNode.next = temp.next;temp.next = heroNode;}}//合并两个有序的单链表,合并之后的链表依然有序public SingleLinkedList merge(SingleLinkedList list1, SingleLinkedList list2) {if (list1.head.next == null) {return list2;} else if (list2.head.next == null) {return list1;}HeroNode newNode = new HeroNode();HeroNode n1 = newNode;HeroNode l1 = list1.head.next;HeroNode l2 = list2.head.next;while (l1 != null && l2 != null) {if (l1.id < l2.id) {n1.next = l1;l1 = l1.next;n1 = n1.next;} else {n1.next = l2;l2 = l2.next;n1 = n1.next;}}if (l1 == null) {n1.next = l2;}if (l2 == null) {n1.next = l1;}return new SingleLinkedList(newNode);}}// 定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode {public int id;public String name;public String nickName; // 别名,昵称public HeroNode next; // 指向下一个节点// 构造器public HeroNode(int id, String name, String nickName) {super();this.id = id;this.name = name;this.nickName = nickName;}public HeroNode() {}// 为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode [id=" + id + ", name=" + name + ", nickName=" + nickName + "]";}
}

双向链表增删改查分析图解及实现

使用带head头的双向链表实现 –水浒英雄排行榜管理单向链表的缺点分析:

1)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找。

2)单向链表不能自我删除,需要靠辅助节点 ,而双向链表,则可以自我删除,所以前面我们单链表删除时节点,总是找到temp,temp是待删除节点的前一个节点.

示意图如下:

分析 双向链表的遍历,添加,修改,删除的操作思路:

  1. 遍历 方式和 单链表一样,只是可以向前查找,也可以向后查找

  2. 添加 (默认添加到双向链表的最后)

    (1) 先找到双向链表的最后这个节点

    (2) temp.next = newHeroNode

    (3) newHeroNode.pre = temp

  3. 修改 思路和 原来的单向链表的思路一样.

  4. 删除

    (1) 因为是双向链表,因此,我们可以实现自我删除某个节点

    (2) 直接找到要删除的这个节点,比如temp

    (3) temp.pre.next = temp.next

    (4) temp.next.pre = temp.pre;

删除的图示如下:



  • 代码实现:
public class DoubleLinkedListTest {public static void main(String[] args) {// 测试一下// 1.创建节点HeroNode2 hero = new HeroNode2(1, "宋江", "及时雨");HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");HeroNode2 hero4 = new HeroNode2(4, "公孙胜", "入云龙");HeroNode2 hero5 = new HeroNode2(5, "关胜", "大刀");// 创建一个双向链表DoubleLinkedList singk = new DoubleLinkedList();// 添加按照编号的顺序singk.add(hero);singk.add(hero4);singk.add(hero2);singk.add(hero5);singk.add(hero3);System.out.println("原来链表的情况:");singk.list(); // 显示链表// 修改HeroNode2 newHeroNode = new HeroNode2(4, "公孙胜", "--");singk.update(newHeroNode);System.out.println("修改后的链表情况");singk.list();// 删除singk.del(3);System.out.println("删除后的链表情况~~");singk.list();}}// 创建一个双向链表的类
class DoubleLinkedList {// 先初始化一个头节点, 头节点不要动, 不存放具体的数据private HeroNode2 head = new HeroNode2(0, "", "");// 返回头节点public HeroNode2 getHead() {return head;}// 遍历双向链表的方法// 显示链表[遍历]public void list() {// 判断链表是否为空if (head.next == null) {System.out.println("链表为空");return;}// 由于头节点head不能动,所以需要一个辅助变量 temp,找到添加的位置HeroNode2 temp = head.next;while (true) {if (temp == null) { // 判断是否到链表最后break;}// 输出节点的信息System.out.println(temp);temp = temp.next; // 将temp后移}}// 添加一个节点到双向链表的最后.public void add(HeroNode2 heroNode) {// 由于头节点head不能动,所以需要一个辅助变量 tempHeroNode2 temp = head;// 遍历链表,找到最后while (true) {// 找到链表的最后if (temp.next == null) { // 判定找到了的条件break;}// 如果没有找到,将temp后移temp = temp.next;}// 当退出循环时,temp就指向了链表的最后// 将最后这个节点的next --指向--》 新的节点temp.next = heroNode;}// 修改节点的信息,根据id编号来修改,即id编号不改// 说明// 1.根据newHeroNode 的 id 来修改即可public void update(HeroNode2 newHeroNode) {// 判断是否空if(head.next == null){    //链表为空System.out.println("链表为空!!!");return;}//找到需要修改的节点,根据 id 编号//定义一个临时变量HeroNode2 temp = head.next;boolean flag = false; //表示是否找到该节点while(true){if(temp == null){break;    //已经遍历完链表}if(temp.id == newHeroNode.id){//找到了flag = true;break;}temp = temp.next;}// 根据flag 判断是否找到要修改的节点if (flag) {temp.name = newHeroNode.name;temp.nickname = newHeroNode.nickname;} else { // 没有找到System.out.printf("没有找到编号为 %d 的节点,不能修改。\n", newHeroNode.id);}}// 删除节点// 思路// 1.由于头节点head不能动,所以需要一个辅助变量 temp来找到待删除节点的前一个节点// 2.说明我们在比较时,是temp.next.id 和 需要删除的节点的id比较public void del(int id) {HeroNode2 temp = head;boolean flag = false; // 标志是否找到带删除的节点while (true) {if (temp.next == null) { // 已经到链表的最后break;}if (temp.next.id == id) { // 找到了带删除的节点的前一个节点flag = true;break;}temp = temp.next; // temp后移,遍历}// 判断flagif (flag) { // 说明找到了// 可以删除temp.next = temp.next.next;} else {System.out.printf("要删除的节点 %d 不存在。\n", id);}}
}// 定义HeroNode2 , 每个HeroNode 对象就是一个节点
class HeroNode2 {public String nickName;public int id;public String name;public String nickname;public HeroNode2 next; // 指向下一个节点, 默认为nullpublic HeroNode2 pre; // 指向前一个节点, 默认为null// 构造器public HeroNode2(int id, String name, String nickname) {super();this.id = id;this.name = name;this.nickname = nickname;}// 为了显示方便,重写toString方法@Overridepublic String toString() {return "HeroNode2 [id=" + id + ", name=" + name + ", nickname=" + nickname + "]";}}

环形链表介绍和约瑟夫问题


  • Josephu 问题

Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

  • 提示

用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。

  • 示意图说明如下

约瑟夫问题分析图解和实现

构建一个单向的环形链表思路

  1. 先创建第一个节点, 让 first 指向该节点,并形成环形

  1. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.





遍历环形链表

  1. 先让一个辅助指针(变量) curBoy,指向first节点

  2. 然后通过一个while循环遍历 该环形链表即可 curBoy.next == first 结束

  • 代码实现
public class Josepfu {public static void main(String[] args) {//测试构建环形链表和遍历是否正确CircleLinkList cingk = new CircleLinkList();cingk.addBoy(5); //加入五个节点cingk.shoeBoy();    //显示一下}}
//创建一个单项环形链表
class CircleLinkList{//创建一个first节点,当前没有编号private Boy first = null;//添加新节点,构成一个环形的链表public void addBoy(int nums){//nums 进行数据校验if(nums < 1){System.out.println("nums的值不正确");return;}Boy curBoy = null; //辅助变量,帮助构建环形链表//使用循环创建环形链表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.setNext(first);curBoy = boy;}}}//遍历当前环形链表public void shoeBoy(){//判断链表是否为空if(first == null){System.out.println("链表为空");return;}//因为first头节点不能动,因此创建一个辅助指针完成遍历Boy curBoy = first;while(true){System.out.printf("当前节点的编号 %d\n",curBoy.getId());if(curBoy.getNext() == first){  //遍历完成break;}curBoy = curBoy.getNext();    //让curBoy后移 }}
}//创建一个Boy类,表示一个节点
class Boy{private int id;   //编号private Boy next;   //指向下一个节点,默认为nullpublic Boy(int id){this.id = id;}public int getId() {return id;}public void setId(int id) {this.id = id;}public Boy getNext() {return next;}public void setNext(Boy next) {this.next = next;}}

根据用户的输入,生成一个人物出圈的顺序

n = 5, 即有5个人

k = 1, 从第一个人开始报数

m = 2, 数2下

  1. 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.

补充: 人物报数前,先让 first 和 helper 移动 k - 1次

  1. 当人物报数时,让first 和 helper 指针同时 的移动 m - 1 次

  1. 这时就可以将first 指向的人物节点 出圈

first = first .next

helper.next = first

原来first 指向的节点就没有任何引用,就会被回收


综上,出圈的顺序:2->4->1->5->3

  • 代码实现
public class Josepfu {public static void main(String[] args) {//测试构建环形链表和遍历是否正确CircleLinkList cingk = new CircleLinkList();cingk.addBoy(5); //加入五个节点cingk.shoeBoy();    //显示一下//测试一下:判断人物出圈是否正确cingk.countBoy(1, 42, 5); // 2->4->1->5->3}}
//创建一个单项环形链表
class CircleLinkList{//创建一个first节点,当前没有编号private Boy first = null;//添加新节点,构成一个环形的链表public void addBoy(int nums){//nums 进行数据校验if(nums < 1){System.out.println("nums的值不正确");return;}Boy curBoy = null; //辅助变量,帮助构建环形链表//使用循环创建环形链表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.setNext(first);curBoy = boy;}}}//遍历当前环形链表public void shoeBoy(){//判断链表是否为空if(first == null){System.out.println("链表为空");return;}//因为first头节点不能动,因此创建一个辅助指针完成遍历Boy curBoy = first;while(true){System.out.printf("当前节点的编号 %d\n",curBoy.getId());if(curBoy.getNext() == first){  //遍历完成break;}curBoy = curBoy.getNext();    //让curBoy后移 }}//根据用户的输入,计算人物出圈的顺序/*** * @Description * @author subei* @date 2020年5月23日下午4:45:48* @param startId  表示从第几个人物开始数数* @param countNum 表示数几下* @param nums  表示最初有几个人物在圈中*/public void countBoy(int startId,int countNum,int nums){//先对数据节点进行校验if(first == null || startId < 1 || startId > nums){System.out.println("数据有误,请重新输入。");return;}//创建一个辅助指针,帮助人物出圈Boy helper = first;//需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点while(true){if(helper.getNext() == first){  // 说明helper指向最后人物节点break;}helper = helper.getNext();}//人物报数前,先让 first 和 helper 移动 k - 1次for(int j = 0; j < startId - 1; j++) {first = first.getNext();helper = helper.getNext();}       //当人物报数时,让first 和 helper 指针同时 的移动 m - 1 次//通过循环,知道圈中只有一个节点while(true) {if(helper == first) { //说明圈中只有一个人物节点break;}//让 first 和 helper 指针同时 的移动 countNum - 1for(int j = 0; j < countNum - 1; j++) {first = first.getNext();helper = helper.getNext();}//此处first指向的节点,就是要出圈的人物节点System.out.printf("人物%d出圈\n", first.getId());//此处将first指向的人物节点出圈first = first.getNext();helper.setNext(first);}System.out.printf("最后留在圈中的人物的编号:%d \n", first.getId());}
}//创建一个Boy类,表示一个节点
class Boy{private int id;   //编号private Boy next;   //指向下一个节点,默认为nullpublic Boy(int id){this.id = id;}public int getId() {return id;}public void setId(int id) {this.id = id;}public Boy getNext() {return next;}public void setNext(Boy next) {this.next = next;}}

本章导图总结

Java数据结构与算法 day02 链表相关推荐

  1. Java数据结构和算法(四)--链表

    日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要 插入索引后面的所以元素全部后移一位. 而本文会详细讲解链表,可以解 ...

  2. Java 数据结构与算法面试 链表

    手写链表的Java实现 package test;class Node{ // 数据域private Object ele; // 指针域private Node node; // 默认构造器,标准构 ...

  3. 一文通数据结构与算法之——链表+常见题型与解题策略+Leetcode经典题

    文章目录 1 链表 1.1 常见题型及解题策略 1.1.1 LeetCode中关于链表的题目有以下五种类型题: 1.1.2 解题策略 1.2 链表的基本内容 1.2.1 链表的基本结构: 1.2.2 ...

  4. java数据结构与算法之顺序表与链表深入分析

    转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52953190 出自[zejian的博客] 关联文章: java数据结 ...

  5. java数据结构与算法之双链表设计与实现

    转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/53047590 出自[zejian的博客] 关联文章: java数据结 ...

  6. Java数据结构和算法(一)——简介

    本系列博客我们将学习数据结构和算法,为什么要学习数据结构和算法,这里我举个简单的例子. 编程好比是一辆汽车,而数据结构和算法是汽车内部的变速箱.一个开车的人不懂变速箱的原理也是能开车的,同理一个不懂数 ...

  7. JAVA数据结构与算法【简单介绍】

    前几天去面一个大厂,面试官特别好,面试官说到,我们的学习不能本末倒置,数据结构和算法是程序的基础,如果数据结构你没有学好,你真正意义上不算会写代码.你的代码是各处粘贴,杂乱无章的. 由于现在大多用JA ...

  8. java算法概述,Java数据结构与算法基础(一)概述与线性结构

    Java数据结构与算法基础(二)递归算法 Java数据结构与算法基础(一)概述与线性结构 学习目的:为了能更顺畅的读很多底层API代码和拓宽解决问题的思路 一.数据结构概述 1.数据结构是什么?数据与 ...

  9. Java 数据结构与算法

    目录 Java 数据结构与算法 数据结构 数据结构的定义 数据的逻辑结构 数据的物理结构 数据存储结构 数据结构的分类 线性结构 非线性结构 常用的数据结构 数组(Array) 栈( Stack) 队 ...

最新文章

  1. Chino的成绩(chino的公开赛T3)
  2. python如何调用图片-python实现读取并显示图片的两种方法
  3. USB基础---Linux USB驱动层次
  4. java的虚拟机不支持在鲲鹏上_屌炸天,Oracle 发布了一个全栈虚拟机 GraalVM,支持 Python!...
  5. .NET CORE下最快比较两个文件内容是否相同的方法
  6. AngularJS-Hello World
  7. 在危机中呈现转机的网络管理
  8. Spring 子项目及介绍
  9. Spring 注解中@Resource 和 Authwired 的区别
  10. 中国分布式光纤传感(DFOS)行业竞争现状与未来趋势预测报告2022-2027
  11. HTML+CSS三栏式布局(7种)
  12. 测试工程师进阶之测试用例发散思维(一)
  13. k8s之无状态服务Deployment1
  14. 微软各系列软件中 MSDN 、 RTM 、 OEM 、 VOL 各版本的含义和区别
  15. 【安全知识】——常见杀软对应进程名
  16. Linux·启动脚本·启动流程
  17. 产品如何定价?别进误区
  18. Java 8 辣么大(lambda)表达式不慌之—–(四)示例-Collectors中的中的joining 方法和mapping方法
  19. 计算机excel函数应用实例,几个常用函数的功能与应用范例
  20. 哈工大:ChatGPT调研报告

热门文章

  1. 阿里成了冤大头??1亿美元收购的开源项目,核心团队出走造竞品,转头又卖了1个亿...
  2. [小O地图-网抓]-山姆会员商店商品分类及商品信息
  3. EasyUI DataGrid 将table中的数字替换为文字
  4. ai人工智能将替代人类_教AI学习人类如何有效计划
  5. 家庭收支软件(C语言)
  6. Program type already present: com.google.gson.FieldAttributes
  7. 十分钟看懂什么是VoLTE
  8. 波比的w可以挡机器人的q_lol波比W可以挡哪些技能 波比W可以挡石头大招吗
  9. Emscripten 安装 亲测有效
  10. iOS中 为 iOS 建立 Travis CI 韩俊强的博客