单链表相关(附面试题) -Java数据结构和算法

下面是关于开发中常用的数据结构:链表 基本操作的介绍:


前言

         链表和数组的区别在哪呢?大家知道,数组是连续的内存空间;而链表是按照节点的方式存放数据的,节点不一定是连续的。下面是对其详细介绍:



一、链表介绍

         1.链表以节点方式来存储数据
         2.每个节点包含data域、next域(指向下个节点)
         3.如下图所示,链表的各个节点不一定是连续的(可以通过next找到下个节点,没有必要必须连续)

在这里插入图片描述

在这里插入图片描述

二、单链表的相关操作

         我们利用单链表实现水浒英雄排行榜:

1.单链表的创建

“英雄节点类” 的代码如下:

class HeroNode{
    public int no;			//编号
    public String name;		//名字
    public String nickname;	//绰号
    public HeroNode next;	//指向下个节点

    //构造器
    public HeroNode(int n , String name , String nickname){
        this.no = n;
        this.name = name;
        this.nickname = nickname;
    }
 //  为了显示方便,重写toString()方法
   @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

1.1 不考虑排名,直接添加英雄节点:

1.找到当前链表的最后节点(遍历)
2.将最后节点的 next 指向新的节点即可

代码如下:

//添加节点到单向链表
//思路:当不考虑编号时候
//1.找到当前链表的最后节点
//2.将最后节点的 next 指向新的节点
public void add(HeroNode heroNode){
    //因为head节点不能动,因此我们需要一个辅助的遍历temp
    HeroNode temp = head;
    while(temp.next != null){
        temp = temp.next;//找到最后一个节点
    }
    temp.next = heroNode;
}

1.2 按照排名(编号),顺序添加英雄节点

按照编号的顺序添加:
 1. 找到新添加的节点的位置(添加节点的前一个位置),通过辅助指针temp(原来的数据肯定也是顺序的,比较一下就行)
 2. 先令新的节点.next = temp.next;
 3. 再将 temp.next = 新的节点
(注意找的temp是添加位置的前一个节点)

在这里插入图片描述


代码如下:
//按顺序添加节点
public void addByOrder(HeroNode heroNode){
    HeroNode p = head; //不能改变头结点,所以用临时节点接收
    boolean alreadyExist = false;//加入英雄的编号是否已经存在
    while(true){
        if(p.next == null){//如果p下面已经为空(找到了最后)
            break;
        }
        if(p.next.no > heroNode.no){//下一个节点就是要找的,已经找到,退出循环即可
            break;
        }
        if(p.next.no == heroNode.no){//希望添加的已经存在
            alreadyExist = true;
            break;
        }
        p = p.next;
    }
    if(alreadyExist == false){
    //加入链表(p的后面)
        heroNode.next = p.next;//先将新节点指向要添加的位置
        p.next = heroNode;	//再将添加的前一个位置的next指向新节点即可
        return;
    }else{
        System.out.println("添加失败,已经存在");
        return;
    }
}

2.遍历单链表

1.判断是否为空,空链表的话直接返回
2.不为空,循环输出链表即可(利用上面的toString()方法)

代码如下:

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);//打印节点信息(自动调用toString()方法)
        temp = temp.next;
    }
}

3.根据编号修改节点信息

根据编号修改,所以编号不可以变(编号要是变了就相当于添加)
1. 判断链表是否为空,为空处理即可
2. 链表不为空,则遍历查找到编号对应的节点即可
3. 找到之后更新节点信息即可

代码如下:

public void update(HeroNode newHeroNode){
    if(head.next == null){
        System.out.println("链表为空");
        return;
    }
    HeroNode p = head.next;
    boolean flag = false;//是否找到该节点
    while(true){
        if(p.no == newHeroNode.no){
            flag = true;//找到了
            break;
        }
        if(p == null){
            break;//找到最后一个节点了,之恩那个退出循环(没找到)
        }
        p = p.next;
    }
    if(flag){//找到了就更新信息即可
        p.name = newHeroNode.name;
        p.nickname = newHeroNode.nickname;
    }else{//没有找到,输出提示
        System.out.println("未找到要修改的英雄,请重试");
        return;
    }
}

4.删除节点

删除节点的思路:
1. 我们先找到 待删除结点的前一个结点temp
2. temp.next = temp.next.next;
3. 被删除的节点,将不会有其它的引用指向,会被垃圾回收机制(gc)回收(不用手动释放)

在这里插入图片描述

代码如下:

//head不能动,辅助接点p
//我们比较的时候,p.next.no 和 待删除的节点的no值进行比较
public void delate(int no){
    if(head.next == null){
        System.out.println("链表为空,删除失败....");
        return;
    }

    boolean flag = false;//标志是否找到
    HeroNode p = head;

    while(true){
        if(p.next == null){//已经到链表最后了
            break;
        }

        if(p.next.no == no){
            flag = true;//找到了带删除节点的前一个节点p,令flag = true即可
            break;
        }
        p = p.next;//p后移,遍历
    }
    if(flag){//找到,进行删除即可
        p.next = p.next.next;
        return;
    }else{
        System.out.println("删除失败,未找到...");
        return;
    }
}



总结

1. 头结点:不存储有效数据,只用来表示单链表的表头;
2. 添加节点、删除节点,对于单链表来说都是找到待处理数据的前一个位置进行操作;
3. 最后注意一下插入数据的操作顺序(不能先断开链表内部的联系),需要先将更新的数据指向链表,然后再断开链表内部的联系;

面试题

点击蓝色字体即可跳转


       1. 新浪面试题:查找单链表中倒数第K个节点
       2. 腾讯面试题:单链表的逆置
       3. 百度面试题:逆序打印单链表


完整测试代码

只进行了部分测试,测试数据可以自己补齐

package 链表;

import java.time.chrono.IsoChronology;
import java.util.Stack;

/**
 * @author Weilei Zhang
 * @create 2021-02-28 22:25
 */
public class SignalLinckedList {
    public static void main(String[] args) {
        //测试:
        //先创建几个节点
        HeroNode hero1 = new HeroNode(1 , "宋江" , "及时雨");
        HeroNode hero2 = new HeroNode(2,  "卢俊义" , "玉麒麟");
        HeroNode hero3 = new HeroNode(3,  "吴用" , "智多星");

        //创建给链表
        SignalLinkedListDemo signalLinckedListDemo = new SignalLinkedListDemo();
        //加人
        signalLinckedListDemo.add(hero1);
        signalLinckedListDemo.add(hero2);
        signalLinckedListDemo.add(hero3);
//        signalLinckedListDemo.addByOrder(hero4);
//        signalLinckedListDemo.delate(8);
//        signalLinckedListDemo.update(hero4);
        signalLinckedListDemo.show();
    }
}






class SignalLinkedListDemo{

//    先初始化一个头结点,不要动头节点,不存放具体的数据
    private HeroNode head = new HeroNode(0 , "" , "");

    public HeroNode getHead(){
        return head;
    }

    //添加节点到单向链表
    //思路:当不考虑编号时候
    //1.找到当前链表的最后节点
    //2.将最后节点的 next 指向新的节点
    public void add(HeroNode heroNode){
        //因为head节点不能动,因此我们需要一个辅助的遍历temp
        HeroNode temp = head;
        while(temp.next != null){
            temp = temp.next;
        }
        temp.next = heroNode;
    }


    //按顺序添加节点
    public void addByOrder(HeroNode heroNode){
        HeroNode p = head;
        boolean alreadyExit = false;
        while(true){
            if(p.next == null){//如果p下面已经为空(找到了最后)
                break;
            }
            if(p.next.no > heroNode.no){
                break;
            }
            if(p.next.no == heroNode.no){//希望添加的已经存在
                alreadyExit = true;
                break;
            }
            p = p.next;
        }
        if(alreadyExit == false){
            heroNode.next = p.next;
            p.next = heroNode;
            return;
        }else{
            System.out.println("添加失败,已经存在");
            return;
        }
    }


    //修改节点的信息,根据编号来修改(所以要求编号不可以变)
    //1.根据newHeroNode的 no 属性来修改即可
    public void update(HeroNode newHeroNode){
        if(head.next == null){
            System.out.println("链表为空");
            return;
        }

        HeroNode p = head.next;
        boolean flag = false;
        while(true){
            if(p.no == newHeroNode.no){
                flag = true;
                break;
            }
            if(p == null){
                break;
            }
            p = p.next;
        }
        if(flag){
            p.name = newHeroNode.name;
            p.nickname = newHeroNode.nickname;
        }else{
            System.out.println("未找到要修改的英雄,请重试");
            return;
        }
    }

    //删除节点

    public void delate(int no){
        if(head.next == null){
            System.out.println("链表为空,删除失败....");
            return;
        }

        boolean flag = false;
        HeroNode p = head;

        while(true){
            if(p.next == null){
                break;
            }

            if(p.next.no == no){
                flag = true;
                break;
            }
            p = p.next;
        }
        if(flag){
            p.next = p.next.next;
            return;
        }else{
            System.out.println("删除失败,未找到...");
            return;
        }
    }


    //显示链表
    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.next;
        }
    }

}


class HeroNode{
    public int no;
    public String name;
    public String nickname;
    public HeroNode next;

    //构造器
    public HeroNode(int n , String name , String nickname){
        this.no = n;
        this.name = name;
        this.nickname = nickname;
    }


    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}
程序 = 数据结构 + 算法  程序是为了解决实际问题而存在的。然而为了解决问题,必定会使用到某些数据结构以及设计一个解决这种数据结构算法。如果说各种编程语言是程序员的招式,那么数据结构算法就相当于程序员的内功。编程实战算法,不是念PPT,我们讲的就是实战与代码实现与企业应用。程序 = 数据结构 + 算法                ——图灵奖得主,计算机科学家N.Wirth(沃斯)作为程序员,我们做机器学习也好,做python开发也好,java开发也好。有一种对所有程序员无一例外的刚需 —— 算法数据结构日常增删改查 + 粘贴复制 + 搜索引擎可以实现很多东西。同样,这样也是没有任何竞争力的。我们只可以粘贴复制相似度极高的功能,稍复杂的逻辑没有任何办法。语言有很多,开发框架更是日新月异3个月不学就落后我们可以学习很多语言,很多框架,但招聘不会考你用5种语言10种框架实现同一个功能。真正让程序员有区分度,企业招聘万年不变的重点 —— 算法数据结构算法代表程序员水平的珠穆朗玛。如果说各种编程语言是程序员的招式,那么数据结构算法就相当于程序员的内功。 想写出精炼、优秀的代码,不通过不断的锤炼,是很难做到的。 开这个系列的目的是为了自我不断积累。不积跬步无以至千里嘛。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值