链表是有序的列表,但是它在内存中是存储如下的:
- 链表是以节点的方式存储的,链式存储
- 每个节点包含data域,next域指向下一个节点
- 如上图:链表的各个节点不一定是连续存放的.
- 链表分带头节点的链表和没有头节点的链表(根据实际的需求来确定)
使用带head头的单项链表实现-水浒英雄排行榜管理
1.完成对英雄人物的增删改查操作,注意:删除和修改,查找
2,第一种方式,在添加英雄时直接添加到英雄的尾部,
3.第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
当链表的创建示意图,显示当下链表的分析
1.创建一个head头节点,作用是代表表示单链表的头.
2.后面没添加一个节点我们就加入到链表的最后
3.遍历:通过一个辅助遍历,帮助我们遍历这个链表
4.我们是以上面的题进行的解析,所有羡慕我们的classHeroNode中前三个都是数据,HerNode next 是next 域.
代码如下,第一种(按照顺序添加,直接添加到链表的尾部)
package com.atguigu.linkedlist;
public class SingleLinkedListDemo {
public static void main(String[] args) {
//进行测试
//先创建节点
HeroNode hero1= new HeroNode(1,"宋江","及时雨");
HeroNode hero2= new HeroNode(2,"卢俊义","玉麒麟");
HeroNode hero3= new HeroNode(3,"吴用","智多星");
HeroNode hero4= new HeroNode(4,"林冲","豹子头");
//创建一个链表
SingleLinkedList singleLinkedList=new SingleLinkedList();
//加入
singleLinkedList.add(hero1);
singleLinkedList.add(hero2);
singleLinkedList.add(hero3);
singleLinkedList.add(hero4);
//显示一把
singleLinkedList.show();
}
}
//3,定义一个SilgleLinkedList管理我们的英雄
class SingleLinkedList{
//先初始化一个头节点,头节点不要动,不存放具体数据
private HeroNode head=new HeroNode(0,"","");
//添加节点到单项链表
//思路:当不考虑当前链表的顺序时
//1,我们找到当前链表的最后节点
//2.将最后这个节点的next 指向新的节点
public void add(HeroNode heroNode){
//因为head节点不能动,因此我们需要衣蛾辅助变量 temp
HeroNode temp=head;
//遍历链表找到最后一个
while (true){
//找到链表的最后
if(temp.next==null){
break;
}
//如果没哟找到最后,将temp后移
temp=temp.next;
}
//当退出whle循环时,说明temp指向了链表的最后
//将最后这个节点next指向了新的节点
temp.next=heroNode;
}
/*显示遍历*/
public void show(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode temp=head.next;
while (true){
//判断是否到了链表的最后
if(temp==null){
break;
}
//输出节点的信息
System.out.println(temp);
//将temp后移,一定要后移,不然死循环
temp=temp.next;
}
}
}
//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode{
public int no;
public String name;
public String nickname;
public HeroNode next;//指向下一个节点
//构造器
public HeroNode(int hNo,String hName ,String hNickname){
this.no=hNo;
this.name=hName;
this.nickname=hNickname;
}
//为了显示方便,我们重新toString方法
//toString这里我们不要打出next域,看起来清晰一些
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname +
'}';
}
}
2,根据排名将英雄插入指定的位置.
1.首先找到新添加的节点的位置.是通过辅助变量(指针)
2,新的节点.next=temp.next(就是将新的节点域变成原来那个(temp)节点域)
3,让temp.next=新的节点(原来的节点的next域指向新的新的节点的)
代码实现
package com.atguigu.linkedlist;
public class SingleLinkdeListDemo2 {
public static void main(String[] args) {
//进行测试
//先创建节点
HeroNode1 hero1= new HeroNode1(1,"宋江","及时雨");
HeroNode1 hero2= new HeroNode1(2,"卢俊义","玉麒麟");
HeroNode1 hero3= new HeroNode1(3,"吴用","智多星");
HeroNode1 hero4= new HeroNode1(4,"林冲","豹子头");
//创建一个链表
SingleLinkedList1 singleLinkedList1=new SingleLinkedList1();
//加入按照编号的顺序
singleLinkedList1.addByOrder(hero1);
singleLinkedList1.addByOrder(hero4);
singleLinkedList1.addByOrder(hero2);
singleLinkedList1.addByOrder(hero3);
/* singleLinkedList1.addByOrder(hero3);
这句测试准备添加的英雄已经存在
*/
//显示一把
singleLinkedList1.show();
}
}
//3,定义一个SilgleLinkedList1管理我们的英雄
class SingleLinkedList1{
//先初始化一个头节点,头节点不要动,不存放具体数据
private HeroNode1 head=new HeroNode1(0,"","");
/*第二种添加英雄的方式,更根据排名将英雄插入到指定的位置
* 如果有这个排名,则添加失败,并给出提示*/
public void addByOrder(HeroNode1 heroNode1){
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为单链表,因此我们找的这个temp是位于这个添加位置的前一个节点,否则插入不了
HeroNode1 temp=head;
boolean flag=false;//标识英雄添加的编号是否存在,默认为false
while (true){
if(temp.next==null){//说明到来链表的最后,添加到最后
break;//无论找没有找到,我们都要退出
}
if(temp.next.no>heroNode1.no){//编号大于插入的上一个编号,说明找到了,我们插入,在temp的后面添加
break;
}else if(temp.next.no==heroNode1.no) {//说明希望添加的heroNode编号存在
flag = true;//说明编号存在
break;
}
temp=temp.next;//后移,遍历当前链表
}
//判断flag的值,
if(flag){//flag为真不能添加,说明编号存在
System.out.println("准备插入的英雄的编号"+heroNode1.no+"已经存在了,不能加入");
}else {
//插入到链表中,temp的后面
heroNode1.next=temp.next;
temp.next=heroNode1;
}
}
/*显示遍历*/
public void show(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode1 temp=head.next;
while (true){
//判断是否到了链表的最后
if(temp==null){
break;
}
//输出节点的信息
System.out.println(temp);
//将temp后移,一定要后移,不然死循环
temp=temp.next;
}
}
}
//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode1{
public int no;
public String name;
public String nickname;
public HeroNode1 next;//指向下一个节点
//构造器
public HeroNode1(int hNo,String hName ,String hNickname){
this.no=hNo;
this.name=hName;
this.nickname=hNickname;
}
//为了显示方便,我们重新toString方法
//toString这里我们不要打出next域,看起来清晰一些
@Override
public String toString() {
return "HeroNode1{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
节点的修改
1.找到待修改的节点,同通过遍历
2.temp.name=newHeroNod.name:temp.nickname=newHeroNode=nickname;
/*修改节点的信息,根据no编号来修改,即no编号不能改变*/
//1.根据newHeroNode1的no来修改即可
public void update(HeroNode1 newHeroNode1){
//判断是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//如果不为空,找到需要修改的节点
//1.先定义一个辅助变量temp
HeroNode1 temp=head.next;
boolean flag1=false;//表示是否找到该节点
while (true){
if(temp==null){
break;//已经遍历完链表
}
if(temp.no==newHeroNode1.no){
//找到
flag1=true;
break;
}
temp=temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag1){//ture
temp.name= newHeroNode1.name;;
temp.nickname=newHeroNode1.nickname;
}else {//没有找到编号等于这个的节点
System.out.println("没有找到编号为"+newHeroNode1.no+"节点");
}
}
节点的删除
从单链表中删除一个节点的思路
1.我们先找到需要删除的这个节点的前一个节点temp
2.temp.next=temp.next.next,(这里相当于我们直接跳过了被删除的节点,没有其它的引用指向,会被垃圾回收机制回收).
代码:
/*删除节点*/
//1.head不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
//2.说明我们在比较时,是temp.next.no 和需要删除的节点的no比较
public void del(int no){
HeroNode1 temp=head;
boolean flag=false;//标志是否找到待删除的节点
while (true){
if(temp.next==null){//已经找到最后,都没有找到
break;
}
if(temp.next.no==no){
flag=true;
break;
}
temp=temp.next;//temp后移,遍历
}
//判断flag
if(flag){//true 找到
//可以删除
temp.next=temp.next.next;
}else {
System.out.println("没有找到要删除的节点"+no+"号节点");
}
}
当链表面试题
求当链表中有效节点的个数
/*方法,获取到当链表节点的个数(如果是带头节点的链表,需要不统计头节点)*/
/**
*
* @param head 链表的头节点
* @return 返回的是有效节点的个数
*/
public static int getLength(HeroNode1 head){
if(head.next==null){//链表空
return 0;
}
int length=0;
//定义一个辅助变量
HeroNode1 cur=head.next;//我们这里看出我们没有同意头节点
while (cur!=null){
length++;
cur=cur.next;//遍历
}
return length;
}
求单链表节点倒数第k个节点
/*查找单链表的倒数第k个节点
* 思路:
* 1.写一个方法接受这个这个head节点同时接受一个
* 2,index表示倒数第index节点
* 3,单链表不能逆向遍历,先把链表从头到尾遍历,得到链表的总的长度size
* 4,得到size后,我们从链表的第一个开始遍历,遍历size-index个,就得到了我们倒数第k个
* 5.如果找到了,就返回该节点,找不动返回空*/
public static HeroNode1 findLastIndexNode(HeroNode1 head,int index){
if(head.next==null){
return null;//此时链表为空,我们没有找到
}
//第一次遍历,我们得到链表的格式
int size=getLength(head);
//第二次遍历,我们遍历size-index个,就是我们倒数的第k个节点
//先做一个数据的校验
if(index<=0||index>size){
return null;
}
//定义一个辅助变量,for 循环定位到倒数的index
HeroNode1 cur=head.next;
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(HeroNode1 head){
//如果当前列表为空,或者只有一个节点,无需反转,直接返回
if(head.next==null||head.next.next==null){
return;
}
//先定义一个辅助的指针(变量)
HeroNode1 cur=head.next;
HeroNode1 next=null;//指向当前节点(cur的节点,因为我们取出cur以后,我们没有指向下一个节点的标识)的下一个节点
HeroNode1 reverseHead=new HeroNode1(0,"","");
//变量原来的链表,每遍历一个节点,就将其取出,并方在新的链表reverseHead的最前端
while (cur!=null){
next=cur.next;//这里next保存了当前节点的下一个节点,就是第一次head.next.next.因为我们一旦取出cur,那么cur.next这个节点就没有指向,我们之后找不到了
cur.next=reverseHead.next;//将cur的下一个节点指向新的链表的头部,就是最前端
reverseHead.next=cur;//将这个cur连接到新的链表上
cur=next;//让cur后移
}
//将这个head.next指向reverseHead.next.实现单链表的反转
head.next=reverseHead.next;
}
从尾到头打印链表
思路:上面这个题的要求就是逆序打印单链
2.方式1.先将单链表进行反装,然后遍历即可,这样做的问题是会破坏原来的单链表的结构(不建议)
3,方式2.可以利用栈这个数据结构,将各个节点压入到栈中,利用栈的先进后出的特点,就实现了逆序打印(这样会实现逆序打印,还不破坏单链表的结构)
/*使用方式二,压入栈进行逆向打印*/
public static void reversePrint(HeroNode1 head){
if(head.next==null){
return;//空链表不能打印
}
//创建一个栈,将各个节点压入栈中.
Stack<HeroNode1> stack=new Stack<HeroNode1>();
HeroNode1 cur=head.next;//先保存当前节点
//将链表的所有节点压入栈中,
while (cur!=null){
stack.push(cur);
cur=cur.next;//cur后移,这样就可以压入下一个节点
}
//将栈中的节点进行打印,pop进行出栈
while (stack.size()>0){
System.out.println(stack.pop());//栈的特点是先进入后出
}
}
上面的整体的代码如下:
package com.atguigu.linkedlist;
import jdk.nashorn.internal.objects.NativeUint8Array;
import java.util.Stack;
public class SingleLinkdeListDemo2 {
public static void main(String[] args) {
//进行测试
//先创建节点
HeroNode1 hero1= new HeroNode1(1,"宋江","及时雨");
HeroNode1 hero2= new HeroNode1(2,"卢俊义","玉麒麟");
HeroNode1 hero3= new HeroNode1(3,"吴用","智多星");
HeroNode1 hero4= new HeroNode1(4,"林冲","豹子头");
HeroNode1 hero5= new HeroNode1(5,"林有","有有");
HeroNode1 hero6= new HeroNode1(6,"林无","无无");
//创建一个链表
SingleLinkedList1 singleLinkedList1=new SingleLinkedList1();
SingleLinkedList1 singleLinkedList2=new SingleLinkedList1();
//加入按照编号的顺序
singleLinkedList1.addByOrder(hero1);
singleLinkedList1.addByOrder(hero4);
singleLinkedList1.addByOrder(hero6);
singleLinkedList1.addByOrder(hero2);
singleLinkedList2.addByOrder(hero5);
singleLinkedList2.addByOrder(hero3);
/* singleLinkedList1.addByOrder(hero3);
这句测试准备添加的英雄已经存在
*/
//显示一把
singleLinkedList1.show();
System.out.println("=============");
singleLinkedList2.show();
//测试节点的修改
HeroNode1 newHeroNode= new HeroNode1(2,"卢俊义...","玉麒麟...");
singleLinkedList1.update(newHeroNode);
System.out.println("修改后的链表请情况");
singleLinkedList1.show();
//测试节点的删除
singleLinkedList1.del(1);
singleLinkedList1.del(7);
System.out.println("删除后的链表情况");
singleLinkedList1.show();
//测试单链表的有效节点个数
System.out.println("有效的节点的个数="+SingleLinkedList1.getLength(singleLinkedList1.getHead()));
//测试一下,我们是否得到 了我们的倒数第一个元素
System.out.println("倒数第一个元素是"+SingleLinkedList1.findLastIndexNode(singleLinkedList1.getHead() ,1));
//测试一下单链表的反转
System.out.println("原来链表的情况");
singleLinkedList1.show();
System.out.println("反转单链表");
SingleLinkedList1.reversetList(singleLinkedList1.getHead());
singleLinkedList1.show();
//逆序打印单链表
System.out.println("逆序打印单独链表");
SingleLinkedList1.reversePrint(singleLinkedList1.getHead());
}
}
//3,定义一个SilgleLinkedList1管理我们的英雄
class SingleLinkedList1{
//先初始化一个头节点,头节点不要动, 存放具体数据
private HeroNode1 head=new HeroNode1(0,"","");
public HeroNode1 getHead() {
return head;
}
public void setHead(HeroNode1 head) {
this.head = head;
}
/*第二种添加英雄的方式,更根据排名将英雄插入到指定的位置
* 如果有这个排名,则添加失败,并给出提示*/
public void addByOrder(HeroNode1 heroNode1){
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为单链表,因此我们找的这个temp是位于这个添加位置的前一个节点,否则插入不了
HeroNode1 temp=head;
boolean flag=false;//标识英雄添加的编号是否存在,默认为false
while (true){
if(temp.next==null){//说明到来链表的最后,添加到最后
break;//无论找没有找到,我们都要退出
}
if(temp.next.no>heroNode1.no){//编号大于插入的上一个编号,说明找到了,我们插入,在temp的后面添加
break;
}else if(temp.next.no==heroNode1.no) {//说明希望添加的heroNode编号存在
flag = true;//说明编号存在
break;
}
temp=temp.next;//后移,遍历当前链表
}
//判断flag的值,
if(flag){//flag为真不能添加,说明编号存在
System.out.println("准备插入的英雄的编号"+heroNode1.no+"已经存在了,不能加入");
}else {
//插入到链表中,temp的后面
heroNode1.next=temp.next;
temp.next=heroNode1;
}
}
/*修改节点的信息,根据no编号来修改,即no编号不能改变*/
//1.根据newHeroNode1的no来修改即可
public void update(HeroNode1 newHeroNode1){
//判断是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//如果不为空,找到需要修改的节点
//1.先定义一个辅助变量temp
HeroNode1 temp=head.next;
boolean flag1=false;//表示是否找到该节点
while (true){
if(temp==null){
break;//已经遍历完链表
}
if(temp.no==newHeroNode1.no){
//找到
flag1=true;
break;
}
temp=temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag1){//ture
temp.name= newHeroNode1.name;;
temp.nickname=newHeroNode1.nickname;
}else {//没有找到编号等于这个的节点
System.out.println("没有找到编号为"+newHeroNode1.no+"节点");
}
}
/*删除节点*/
//1.head不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
//2.说明我们在比较时,是temp.next.no 和需要删除的节点的no比较
public void del(int no){
HeroNode1 temp=head;
boolean flag=false;//标志是否找到待删除的节点
while (true){
if(temp.next==null){//已经找到最后,都没有找到
break;
}
if(temp.next.no==no){
flag=true;
break;
}
temp=temp.next;//temp后移,遍历
}
//判断flag
if(flag){//true 找到
//可以删除
temp.next=temp.next.next;
}else {
System.out.println("没有找到要删除的节点"+no+"号节点");
}
}
/*使用方式二,压入栈进行逆向打印*/
public static void reversePrint(HeroNode1 head){
if(head.next==null){
return;//空链表不能打印
}
//创建一个栈,将各个节点压入栈中.
Stack<HeroNode1> stack=new Stack<HeroNode1>();
HeroNode1 cur=head.next;//先保存当前节点
//将链表的所有节点压入栈中,
while (cur!=null){
stack.push(cur);
cur=cur.next;//cur后移,这样就可以压入下一个节点
}
//将栈中的节点进行打印,pop进行出栈
while (stack.size()>0){
System.out.println(stack.pop());//栈的特点是先进入后出
}
}
/*单链表的反转
* */
public static void reversetList(HeroNode1 head){
//如果当前列表为空,或者只有一个节点,无需反转,直接返回
if(head.next==null||head.next.next==null){
return;
}
//先定义一个辅助的指针(变量)
HeroNode1 cur=head.next;
HeroNode1 next=null;//指向当前节点(cur的节点,因为我们取出cur以后,我们没有指向下一个节点的标识)的下一个节点
HeroNode1 reverseHead=new HeroNode1(0,"","");
//变量原来的链表,每遍历一个节点,就将其取出,并方在新的链表reverseHead的最前端
while (cur!=null){
next=cur.next;//这里next保存了当前节点的下一个节点,就是第一次head.next.next.因为我们一旦取出cur,那么cur.next这个节点就没有指向,我们之后找不到了
cur.next=reverseHead.next;//将cur的下一个节点指向新的链表的头部,就是最前端
reverseHead.next=cur;//将这个cur连接到新的链表上
cur=next;//让cur后移
}
//将这个head.next指向reverseHead.next.实现单链表的反转
head.next=reverseHead.next;
}
/*查找单链表的倒数第k个节点
* 思路:
* 1.写一个方法接受这个这个head节点同时接受一个
* 2,index表示倒数第index节点
* 3,单链表不能逆向遍历,先把链表从头到尾遍历,得到链表的总的长度size
* 4,得到size后,我们从链表的第一个开始遍历,遍历size-index个,就得到了我们倒数第k个
* 5.如果找到了,就返回该节点,找不动返回空*/
public static HeroNode1 findLastIndexNode(HeroNode1 head,int index){
if(head.next==null){
return null;//此时链表为空,我们没有找到
}
//第一次遍历,我们得到链表的格式
int size=getLength(head);
//第二次遍历,我们遍历size-index个,就是我们倒数的第k个节点
//先做一个数据的校验
if(index<=0||index>size){
return null;
}
//定义一个辅助变量,for 循环定位到倒数的index
HeroNode1 cur=head.next;
for(int i=0;i<size-index;i++){
cur=cur.next;
}
return cur;
}
/*方法,获取到当链表节点的个数(如果是带头节点的链表,需要不统计头节点)*/
/**
*
* @param head 链表的头节点
* @return 返回的是有效节点的个数
*/
public static int getLength(HeroNode1 head){
if(head.next==null){//链表空
return 0;
}
int length=0;
//定义一个辅助变量
HeroNode1 cur=head.next;//我们这里看出我们没有统计头节点
while (cur!=null){
length++;
cur=cur.next;//遍历
}
return length;
}
/*显示遍历*/
public void show(){
//先判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
//因为头节点不能动,因此我们需要一个辅助变量来遍历
HeroNode1 temp=head.next;
while (true){
//判断是否到了链表的最后
if(temp==null){
break;
}
//输出节点的信息
System.out.println(temp);
//将temp后移,一定要后移,不然死循环
temp=temp.next;
}
}
}
//定义一个HeroNode1,每个HeroNode1对象就是一个节点
class HeroNode1{
public int no;
public String name;
public String nickname;
public HeroNode1 next;//指向下一个节点
//构造器
public HeroNode1(int hNo,String hName ,String hNickname){
this.no=hNo;
this.name=hName;
this.nickname=hNickname;
}
//为了显示方便,我们重新toString方法
//toString这里我们不要打出next域,看起来清晰一些
@Override
public String toString() {
return "HeroNode1{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}