单链表算法设计(含大厂面试题)

1.单链表介绍与内存布局

链表是有序的列表,但是它在内存中是存储如下:
在这里插入图片描述
小结:
1)链表是以节点的方式来存储,是链式存储
2)每个节点包含data域,next域: 指向下一个节点.
3)如图:发现链表的各个节点不一定是连续存储.
4)链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

单链表(带头结点)逻辑结构示意图如下
在这里插入图片描述

2.单链表的应用示例

使用带head头的单向链表实现-水浒英雄排行榜管理
1)完成对 英雄人物的增删改查操作,注: 删除和修改,查找.
2)第一 种方法在添加英雄时,直接添加到链表的尾部
3)第二种方式在添加英雄时,根据排名将英雄插入到指定位置
(如果有这个排名,则添加失败,并给出提示)

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

单链表的创建示意图,显示单向链表的分析

class HeroNode{
    
    
	int no;			//编号
	String name;
	String nickname;	//昵称
	HeroNode next;
}

添加(创建)
1.先创建一个head头节点,作用就是表示单链表的头
2.后面我们每添加一个节点,就直接加入到链表的最后
遍历:
1.通过一个辅助变量遍历,帮助遍历整个链表
在这里插入图片描述
单链表判断最后一个结点的方法:判断结点的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

    @Override
    public String toString() {
    
    
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}
//定义SingleLinkedList来管理我们的英雄
class SingleLinkedList{
    
    
    //先初始化头结点,头结点不要动,不存放具体数据
    private HeroNode head=new HeroNode(0,"","");
    //添加结点到单向链表
    /*
    添加元素的时候,最重要的就是要找到链表的最后一个结点。(尾插法)。
    然后让最后一个结点的next域指向最新的结点。
    * */
    //思路:当不考虑编号顺序时
    //1.找到当前链表的最后结点
    //2.将最后结点的next域指向新的结点即可
    public void add(HeroNode heroNode){
    
    
        //因为head结点不能动,因此我们需要一个辅助变量
        HeroNode temp=head;
        //遍历链表,找到最后
        while(true){
    
    
            //找到链表的最后
            if(temp.next==null){
    
    
                break;
            }
            //如果没有找到最后,就将temp后移
            temp=temp.next;
        }
        //当退出while循环时,temp就指向了最后
        //将最后这个结点的next指向新的结点
        temp.next=heroNode;
    }
    //显示链表[遍历]
    public void list(){
    
    
        //先判断链表是否为空
        if(head.next==null){
    
    
            System.out.println("链表为空");
            return ;
        }
        //因为头结点不能动,我们需要辅助变量来比那里
        HeroNode temp=head.next;
        //如果temp所指结点不为空,那么我们就可以打印
        while(temp!=null){
    
    
            System.out.println(temp);   //输出结点信息
            temp=temp.next;     //一定小心,要后移,否则死循环
        }
    }

}
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(hero4);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        //显示一把
        singleLinkedList.list();
    }
}

在这里插入图片描述
此时的顺序是按照插入顺序来输入的,如何按照编号顺序来输出呢?

2.2 单链表按照顺序插入信息

在这里插入图片描述
需要按照编号的顺序添加
1.首先找到新添加的节点的位置,是通过辅助变量(指针),通过遍历来搞定
2.新的节点.next =temp.next
3.将temp.next=新的节点

//第二种方式在添加英雄时,根据排名将英雄插入到指定位置
    // (如果有这个排名,则添加失败,并给出提示)
    public void OrderAdd(HeroNode heroNode){
    
    
        //因为头结点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
        //因为单链表,因为我们找的temp是位于添加位置的前一个结点,否则插入不了
        HeroNode temp=head;
        //如果temp.next==null,说明temp已经在链表的最后,说明添加的结点就是位于链表的最后
        while(temp.next!=null){
    
    
            //位置找到,就在temp的后边
            if(temp.next.no>heroNode.no){
    
    
                break ;
            }else if(temp.next.no==heroNode.no){
    
    
               //不能添加,说明编号存在
                System.out.println("准备插入的英雄的编号已经存在,不能加入");
                return;
            }
            temp=temp.next;         //遍历当前链表
        }
        heroNode.next=temp.next;
        temp.next=heroNode;
        return;
    }
//先创建结点
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.OrderAdd(hero1);
singleLinkedList.OrderAdd(hero4);
singleLinkedList.OrderAdd(hero2);
singleLinkedList.OrderAdd(hero3);

在这里插入图片描述

2.3 单链表结点的修改

//修改结点的信息,根据no编号来修改,即no编号不能改
    //说明:1.根据newHeroNode的no来修改
    public void update(HeroNode newHeroNode){
    
    
        //判断是否空
        if(head.next==null){
    
    
            System.out.println("链表为空");
            return;
        }
        //找到需要修改的结点,根据no编号
        //定义一个辅助变量
        HeroNode temp=head.next;
        while(temp!=null){
    
    
            if(temp.no==newHeroNode.no){
    
    
                System.out.println("找到了");
                temp.nickname=newHeroNode.nickname;
                temp.name=newHeroNode.name;
                return;
            }
            temp=temp.next;
        }
        //已经遍历完链表,但是还没找到
        System.out.printf("没有找到编号%d的结点",newHeroNode.no);
    }
//先创建结点
        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.OrderAdd(hero1);
        singleLinkedList.OrderAdd(hero4);
        singleLinkedList.OrderAdd(hero2);
        singleLinkedList.OrderAdd(hero3);

        //测试修改结点的代码
        HeroNode newHeroNode=new HeroNode(2,"小卢","玉麒麟~~");
        singleLinkedList.update(newHeroNode);

在这里插入图片描述

2.4 单链表结点的删除

在这里插入图片描述
从单链表中删除一个节点的思路
1.我们先找到需要删除的这个节点的前一个节点temp
2. temp.next = temp.next.next
3.被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收

//删除结点
    //思路
    //1.head不动,因此我们需要一个temp辅助结点找到待删除结点的前一个结点
    //2.说明我们在比较时,是temp.next.no和需要删除的结点的no进行比较
    public void delete(int no){
    
    
        HeroNode temp=head;
        //当遍历到链表的最后,就必须退出了
        while(temp.next!=null){
    
    
            //注意,我们要找的是待删除结点的前一个结点
            if(temp.next.no==no){
    
    
                temp.next=temp.next.next;      //删除结点
                return ;
            }
            temp=temp.next;     //后移才能实现遍历
        }
        //说明一直都没有找到
        System.out.printf("没有找到指定的要删除的编号为%d\n",no);
    }
		//测试删除结点
        singleLinkedList.list();
        singleLinkedList.delete(3);
        System.out.println("删除后");
        //显示一把
        singleLinkedList.list();

在这里插入图片描述

3.大厂面试题

3.1.查找单链表中的倒数第k个结点(新浪面试题)

链表中倒数第k个结点

3.2.单链表的反转(腾讯面试题)

反转链表

3.3.从尾到头打印单链表(百度面试题)

从尾到头打印链表