数据结构——单链表

目录

1. 链表介绍

2. 单链表应用实例

2.1 添加节点到链表尾部

(1)思路分析

(2)代码实现

2.2 按照顺序添加节点

(1)思路分析

(2)代码实现

2.3 修改节点信息

(1)思路分析

(2)代码实现

2.3 删除节点

(1)思路分析

(2)代码实现

2.4 单链表常见题目

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

(2)查找单链表中的倒数第 k 个结点

(3)单链表的反转

(4)逆序打印单链表


1. 链表介绍

(1)链表是以节点的方式来存储,是链式存储。

(2)每个节点包含data域:存放数据,next域:指向下一个节点。

(3)最后一个节点的next为null,表示链表结束。

(4)链表是有序的列表,但是它在内存中不一定是连续存储。如下图:

(5)链表分为:带头节点的链表 和 不带头节点的链表,根据实际需求确定。

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

2. 单链表应用实例

使用带有head头的单向链表实现“角色排行榜”,完成对角色排行榜的增删改查操作。

2.1 添加节点到链表尾部

(1)思路分析

1.先创建一个head头节点,表示单链表的头部。

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

3.通过一个辅助变量遍历整个链表。

4.示意图:

(2)代码实现

节点类

//定义节点,每个HeroNode对象就是一个节点
class HeroNode {
    public int id; //角色的编号
    public String name; //角色的名字
    public String nickname; //角色的昵称
    public HeroNode next; //指向下一个节点

    public HeroNode(int id, String name, String nickname) {
        this.id = id;
        this.name = name;
        this.nickname = nickname;
    }

    //重写toString,方便显示信息
    @Override
    public String toString() {
        return "id=" + id + ", name=" + name + ", nickname=" + nickname;
    }
}

链表

//定义链表
class SingleLinkedList {
    //初始化头节点,不存放具体数据,不能改变头节点
    private HeroNode head = new HeroNode(0, "", "");

    public HeroNode getHead() {
        return head;
    }

    //添加节点到单向链表尾部,不考虑顺序
    public void add(HeroNode heroNode) {
        HeroNode 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;
    }
    HeroNode temp = head.next; //指针
    while (true) {
        //判断是否到链表最后
        if (temp == null) {
            break;
        }
        System.out.println(temp); //输出节点信息
        temp = temp.next; //temp后移
    }
}

2.2 按照顺序添加节点

(1)思路分析

1.先通过遍历,找到新添加节点的前一个位置,通过指针记录位置。

2.让新节点指向下一个节点。

3.让上一个节点指向新节点。

4.示意图:

(2)代码实现

(直接在SingleLinkedList类中添加方法)

//按照顺序,插入节点到指定位置
public void addByOrder(HeroNode heroNode) {
    HeroNode temp = head; //指针,表示添加位置的前一个节点
    boolean flag = false; //表示添加的编号是否存在
    //遍历链表,找添加的位置
    while (true) {
        //判断是否到链表最后
        if (temp.next == null) {
            break;
        }
        //判断位置找到的情况
        if (temp.next.id > heroNode.id) {//找到位置,直接插入
            break;
        } else if (temp.next.id == heroNode.id) {//已经存在
            flag = true;
            break;
        }
        temp = temp.next; //temp后移
    }
    //插入到链表中
    if (flag) {//判断编号是否存在
        System.out.println("当前id" + heroNode + "已经存在!");
    } else {
        heroNode.next = temp.next; //新节点指向下一个节点
        temp.next = heroNode; //当前节点指向新节点
    }
}

2.3 修改节点信息

(1)思路分析

1.先通过遍历,找到指定节点的位置,通过指针记录位置。

2.修改相关信息。

(2)代码实现

(直接在SingleLinkedList类中添加方法)

//修改节点信息,根据id编号查找
public void update(HeroNode newHeroNode) {
    //判断链表是否为空
    if (head.next == null) {
        System.out.println("链表为空!");
        return;
    }
    HeroNode temp = head.next; //指针,指向修改的节点
    boolean flag = false; //表示是否找到该节点
    //遍历链表,找指定节点
    while (true) {
        //判断是否到链表最后
        if (temp == null) {
            break;
        }
        //判断位置找到的情况
        if (temp.id == newHeroNode.id) {//找到指定id
            flag = true;
            break;
        }
        temp = temp.next; //temp后移
    }
    //修改节点信息
    if (flag) { //找到
        temp.name = newHeroNode.name;
        temp.nickname = newHeroNode.nickname;
    } else { //没找到
        System.out.println("没有找到" + newHeroNode.id + "编号");
    }
}

2.3 删除节点

(1)思路分析

1.先通过遍历,找到待删除节点的前一个位置,通过指针记录位置。

2.将这个节点的next直接指向下下个节点

3.被删除的节点不会有任何引用指向,会被垃圾回收机制回收。

4.示意图:

(2)代码实现

(直接在SingleLinkedList类中添加方法)

//删除节点,根据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后移
    }
    //删除节点
    if (flag) { //找到
        temp.next = temp.next.next; //指向删除节点的后一个节点
    } else {
        System.out.println("要删除的" + id + "号节点不存在");
    }
}

2.4 单链表常见题目

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

//获取单链表中节点的个数,不算头节点
//head 头节点
public static int getLength(HeroNode head) {
    //判断链表是否为空
    if (head.next == null) {
        return 0;
    }
    int length = 0;//记录节点个数
    HeroNode pointer = head.next; //指针,指向第一个节点
    //遍历链表
    while (pointer != null) {
        length++;
        pointer = pointer.next;
    }
    return length;
}

(2)查找单链表中的倒数第 k 个结点

//查找倒数第k个节点
//head 头节点,k 倒数第几个节点
public static void findLastIndex(HeroNode head, int k) {
    //判断链表是否为空
    if (head.next == null) {
        System.out.println("链表为空");
        return;
    }
    int length = 0;//记录节点个数
    HeroNode pointer = head.next; //指针,指向第一个节点
    while (pointer != null) {//遍历链表
        length++;
        pointer = pointer.next;
    }
    if (k <= 0 || k > length) {//校验
        System.out.println("找不到");
        return;
    }
    int sum = length - k; //表示整数第几个节点
    pointer = head.next; //重置指针
    while (sum > 0) {//遍历链表,找指定节点
        sum--;
        pointer = pointer.next;
    }
    System.out.println("倒数第"+ k + "个节点为" + pointer);
}

(3)单链表的反转

思路分析:

1.先定义一个新链表的头节点。

2.从头遍历原链表,每遍历一个节点,就放在新链表的第一个位置。

3.让原链表的头结点指向新链表头节点。

//单链表的反转
public static void reverseList(HeroNode head) {
    //判断链表是否为空,或只有一个节点
    if (head.next == null || head.next.next == null) {
        return;
    }
    HeroNode pointer = head.next; //指针,指向第一个节点
    HeroNode next = null; //存放指针的下一个节点
    HeroNode reverseHead = new HeroNode(0, "", "");//新的链表
    //遍历原链表,每遍历一个节点,就放在新链表的前面
    while (pointer != null) {
        next = pointer.next;//先暂时保存当前节点的下一个节点
        pointer.next = reverseHead.next;//下一个节点指向新链表的第一个节点
        reverseHead.next = pointer;//新头节点指向当前节点
        pointer = next;//指针后移
    }
    head.next = reverseHead.next; //原头节点指向新头节点
}

(4)逆序打印单链表

思路分析:

1.可以使用上面的反转链表再打印,但这样会破坏原本的链表结构。

2.使用栈(先进后出)的特点进行逆序打印。

3.先遍历链表,将每个节点加入到栈中,然后遍历栈,进行打印。

//逆序打印单链表,使用栈(先进后出)完成
public static void reversePrint(HeroNode head) {
    //判断链表是否为空
    if (head.next == null) {
        System.out.println("链表为空");
        return;
    }
    Stack<HeroNode> stack = new Stack<HeroNode>();//创建栈
    HeroNode pointer = head.next; //指针,指向第一个节点
    //遍历链表,将每个节点加入到栈中
    while (pointer != null) {
        stack.add(pointer); //使用add方法入栈
        pointer = pointer.next; //指针后移
    }
    //遍历栈,打印节点信息
    while (stack.size() > 0)  {
        System.out.println(stack.pop()); //使用pop方法出栈
    }
}

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值