JAVA实现单链表以及单链表的增、删、改、查、反转、查询倒数第k个结点

一.单链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

结点结构

①数据域,②指针域
在这里插入图片描述
单链表就可以看作是多个结点相连
在这里插入图片描述

二.开始创建链表

1.创建结点类

/**
 * @author Watching
 * * @date 2022/10/24
 * * Describe:单链表
 * <p>
 * 使用单链表存储水浒传108将
 */
public class SinglyLinkedListDemo {
//节点类
class Node {
    public int number;//序号 数据域
    public String name;//姓名 数据域
    public String nickName;//昵称 数据域
    public Node next;//next域 指向下一个节点 指针域

    public Node() {
    }

    //构造器初始化节点
    public Node(int no, String name, String nickName) {
        this.name = name;
        this.number = no;
        this.nickName = nickName;
        this.next = null;
    }

    @Override
    public String toString() {
        return "Node{" +
                "number=" + number +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

2.创建链表类

/**
 * @author Watching
 * * @date 2022/10/24
 * * Describe:单链表
 * <p>
 * 使用单链表存储水浒传108将
 */
public class SinglyLinkedListDemo {

    //链表类
    class SinglyLinkedList {
        private Node node;
        //头节点,不存放具体数据,只是单链表表头
        Node head = new Node(0, "", "");
    }

    //节点类
    class Node {
        public int number;//序号
        public String name;//姓名
        public String nickName;//昵称
        public Node next;//next域 指向下一个节点

        public Node() {
        }

        //构造器初始化节点
        public Node(int no, String name, String nickName) {
            this.name = name;
            this.number = no;
            this.nickName = nickName;
            this.next = null;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "number=" + number +
                    ", name='" + name + '\'' +
                    ", nickName='" + nickName + '\'' +
                    '}';
        }
    }
}

此时链表就已经创建完成了,现在我们需要给链表类编写增加节点,删除节点,修改节点,查找节点方法

2.1 添加节点

    /**
     * 添加节点(尾插法)
     *
     * @param node
     */
    public void add(Node node) {
        Node temp = head;//temp用于遍历链表
        //找到链表的最后一个节点
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;//将新的节点添加到末尾
        //TODO 为什么temp后面添加node会等于head后面添加node
        //temp并不是一个新的节点,temp只是一个引用,它指向了head,所以在后面temp就是head
    }

2.2 删除节点

/**
     *从单链表中删除一个节点的思路
     * 1.我们先找到需要删除的这个节点的前一个节点temp
     * 2.删除操作:temp.next = temp.next.next
     * 3.被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收
     */
    public void delete(int number){
        Node temp = head;
        while(true){
            if(temp.next==null){
                System.out.println("链表中没有对应的节点");
                break;
            }
            if(temp.next.number == number){
                temp.next = temp.next.next;
                System.out.println("已删除:"+number);
                break;
            }
            temp = temp.next;
        }
    }

2.3 修改节点

/**
     * 根据id修改节点信息
     */
    public void update(Node node){
        Node temp = head;
        boolean flag = false;
        while(true){
            if(temp.next==null){
                System.out.println("未找到对应节点");
                break;
            }
            if(temp.next.number == node.number){
                temp.next.nickName = node.nickName;
                temp.next.name = node.name;
                flag = true;
                System.out.println("修改成功:"+node.number);
                break;
            }
            temp = temp.next;
        }
        if(!flag){
            System.out.println("链表中没有对应的节点");
        }
    }

此外 还有按照顺序添加结点,可以通过代码理解一下

2.4 按照结点number的大小顺序添加结点

/**
     * 按照顺序添加节点
     */
    public void addByOrder(Node node) {
        Node temp = head;//创建变量指向头节点
        while (true) {
            if (temp.next == null) {//如果已经到了链表末尾,则直接添加
                temp.next = node;
                break;
            }
            if (temp.next.number > node.number) {//如果遍历到的节点的后一个节点的number大于新添加的节点的number,则将新添加的number插入到其中
                node.next = temp.next;
                temp.next = node;
                break;
            }
            if(temp.next.number == node.number){//如果已经有和新添加的节点一样的number则取消添加
                System.out.println("节点已存在:"+ node.number);
                break;
            }
            temp= temp.next;//遍历节点
        }
    }

2.5 反转链表

思路:
1.先定义一个节点reverseHead = new Node();
2.从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最后端
3.原来的链表的head.next =reverseHead.next

/**
     * 反转链表
     *
     * @param head 被反转链表的头结点
     * @return 思路:
     *
     * 1.先定义一个节点reverseHead = new Node();
     * 2.从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最后端
     * 3.原来的链表的head.next =reverseHead.next
     */
    public Node reverse(Node head) {
        if (head.next == null) {
            System.out.println("链表为空");
        }
        Node reverseHead = new Node();
        Node temp = head;
        //获取有效结点个数,有多少个有效节点,就需要从原head移动结点到新reverseHead结点多少次
        int num = 0;
        while (temp.next != null) {
            num++;
            temp = temp.next;
        }
        int num1 = num;//num1用于遍历到head的最后一个结点
        for (int k = 0; k < num; k++) {
            temp = head;
            //遍历到head的最后一个结点
            for (int i = 0; i < num1; i++) {
                temp = temp.next;
            }
            //将最后一个结点头插法到新的头结点reverseHead后面
            //如果新的头结点后面无数据,则直接插入
            if (reverseHead.next == null) {
                reverseHead.next = temp;
                num1--;
                continue;
            }
            //如果新的头结点后面有数据,则插入末尾
            Node t = reverseHead;
            int l = k;
            while (l != 0) {
                t = t.next;
                l--;
            }
            temp.next = null;
            t.next = temp;
            num1--;
        }
        head.next = reverseHead.next;
        System.out.println("反转完成");
        return head;
    }

2.6 查找单链表中的倒数第k个结点

思路:
1.获取链表中有效结点个数
2.通过有效个数-倒数第k个+1=目标结点的正序序号 如 10个结点,倒数第2个即为9,10-2+1=9
3.遍历链表,获取目标节点

/**
     * 查找单链表中的倒数第k个结点
     *
     * @param head 头结点
     * @param k    倒数第k个
     * @return 实现思路:
     * ①获取链表中有效结点个数
     * ②通过有效个数-倒数第k个+1=目标结点的正序序号  如 10个结点,倒数第2个即为9,10-2+1=9
     * ③遍历链表,获取目标节点
     */
    public Node getLastNode(Node head, int k) {
        Node temp = head;
        int i = 0;
        //1.获取链表中有效结点个数
        while (temp.next != null) {
            temp = temp.next;
            i++;
        }
        //2.获取目标节点的正序序号
        int o = i - k + 1;
        //3.遍历链表,获取目标节点
        temp = head;
        for (int j = 0; j < o; j++) {
            temp = temp.next;
        }
        System.out.println(temp);
        return temp;
    }

三.完整代码


/**
 * @author Watching
 * * @date 2022/10/24
 * * Describe:单链表
 * <p>
 * 使用单链表存储水浒传108将
 */
public class SinglyLinkedListDemo {
    public static void main(String[] args) {

        Node hero1 = new Node(1, "宋江", "及时雨");
        Node hero2 = new Node(2, "卢俊义", "玉麒麟");
        Node hero3 = new Node(3, "吴用", "智多星");
        Node hero4 = new Node(4, "林冲", "豹子头");

        SinglyLinkedList singlyLinkedList = new SinglyLinkedList();
//        singlyLinkedList.add(hero1);
//        singlyLinkedList.add(hero3);
//        singlyLinkedList.add(hero2);
//        singlyLinkedList.add(hero4);

        singlyLinkedList.addByOrder(hero1);
        singlyLinkedList.addByOrder(hero3);
        singlyLinkedList.addByOrder(hero2);
        singlyLinkedList.addByOrder(hero4);
        singlyLinkedList.showList();

        Node hero = new Node(4, "林冲~~", "豹子头~~");

        singlyLinkedList.update(hero);
        singlyLinkedList.showList();

//        singlyLinkedList.delete(1);
//        singlyLinkedList.delete(2);
//        singlyLinkedList.delete(3);
//        singlyLinkedList.delete(4);
//        singlyLinkedList.showList();
        System.out.println("获取第倒数第k个结点");
        singlyLinkedList.getLastNode(singlyLinkedList.head, 1);

        System.out.println("反转链表");
        singlyLinkedList.reverse(singlyLinkedList.head);
        singlyLinkedList.showList();


    }
}

//链表类
class SinglyLinkedList {
    private Node node;
    //头节点,不存放具体数据,只是单链表表头
    Node head = new Node(0, "", "");


    /**
     * 添加节点(尾插法)
     *
     * @param node
     */
    public void add(Node node) {
        Node temp = head;//temp用于遍历链表
        //找到链表的最后一个节点
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;//将新的节点添加到末尾
        //TODO 为什么temp后面添加node会等于head后面添加node
        //temp并不是一个新的节点,temp只是一个引用,它指向了head,所以在后面temp就是head
    }

    /**
     * 添加结点,头插法
     */
    public void HeadAdd(Node node) {
        //如果链表为null,则直接添加新结点到head结点后面
        if (head.next == null) {
            head.next = node;
            return;
        }
        //如果链表不为null,则将新结点添加到头结点后面,其余结点前面
        node.next = head.next;
        head.next = node;
    }


    /**
     * 按照顺序添加节点
     */
    public void addByOrder(Node node) {
        Node temp = head;//创建变量指向头节点
        while (true) {
            if (temp.next == null) {//如果已经到了链表末尾,则直接添加
                temp.next = node;
                break;
            }
            if (temp.next.number > node.number) {//如果遍历到的节点的后一个节点的number大于新添加的节点的number,则将新添加的number插入到其中
                node.next = temp.next;
                temp.next = node;
                break;
            }
            if (temp.next.number == node.number) {//如果已经有和新添加的节点一样的number则取消添加
                System.out.println("节点已存在:" + node.number);
                break;
            }
            temp = temp.next;//遍历节点
        }
    }

    /**
     * 根据id修改节点信息
     */
    public void update(Node node) {
        Node temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                System.out.println("未找到对应节点");
                break;
            }
            if (temp.next.number == node.number) {
                temp.next.nickName = node.nickName;
                temp.next.name = node.name;
                flag = true;
                System.out.println("修改成功:" + node.number);
                break;
            }
            temp = temp.next;
        }
        if (!flag) {
            System.out.println("链表中没有对应的节点");
        }
    }

    /**
     * 从单链表中删除一个节点的思路
     * 1.我们先找到需要删除的这个节点的前一个节点temp
     * 2.删除操作:temp.next = temp.next.next
     * 3.被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收
     */
    public void delete(int number) {
        Node temp = head;
        while (true) {
            if (temp.next == null) {
                System.out.println("链表中没有对应的节点");
                break;
            }
            if (temp.next.number == number) {
                temp.next = temp.next.next;
                System.out.println("已删除:" + number);
                break;
            }
            temp = temp.next;
        }
    }

    /**
     * 展示链表
     */
    public void showList() {
        Node temp = head;
        while (temp.next != null) {
            System.out.println(temp.next);
            temp = temp.next;
        }
    }

    /**
     * 查找单链表中的倒数第k个结点
     *
     * @param head 头结点
     * @param k    倒数第k个
     * @return 实现思路:
     * ①获取链表中有效结点个数
     * ②通过有效个数-倒数第k个+1=目标结点的正序序号  如 10个结点,倒数第2个即为9,10-2+1=9
     * ③遍历链表,获取目标节点
     */
    public Node getLastNode(Node head, int k) {
        Node temp = head;
        int i = 0;
        //1.获取链表中有效结点个数
        while (temp.next != null) {
            temp = temp.next;
            i++;
        }
        //2.获取目标节点的正序序号
        int o = i - k + 1;
        //3.遍历链表,获取目标节点
        temp = head;
        for (int j = 0; j < o; j++) {
            temp = temp.next;
        }
        System.out.println(temp);
        return temp;
    }

    /**
     * 反转链表
     *
     * @param head 被反转链表的头结点
     * @return 思路:
     *
     * 1.先定义一个节点reverseHead = new Node();
     * 2.从头到尾遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead的最后端
     * 3.原来的链表的head.next =reverseHead.next
     */
    public Node reverse(Node head) {
        if (head.next == null) {
            System.out.println("链表为空");
        }
        Node reverseHead = new Node();
        Node temp = head;
        //获取有效结点个数,有多少个有效节点,就需要从原head移动结点到新reverseHead结点多少次
        int num = 0;
        while (temp.next != null) {
            num++;
            temp = temp.next;
        }
        int num1 = num;//num1用于遍历到head的最后一个结点
        for (int k = 0; k < num; k++) {
            temp = head;
            //遍历到head的最后一个结点
            for (int i = 0; i < num1; i++) {
                temp = temp.next;
            }
            //将最后一个结点头插法到新的头结点reverseHead后面
            //如果新的头结点后面无数据,则直接插入
            if (reverseHead.next == null) {
                reverseHead.next = temp;
                num1--;
                continue;
            }
            //如果新的头结点后面有数据,则插入末尾
            Node t = reverseHead;
            int l = k;
            while (l != 0) {
                t = t.next;
                l--;
            }
            temp.next = null;
            t.next = temp;
            num1--;
        }
        head.next = reverseHead.next;
        System.out.println("反转完成");
        return head;
    }

}

//节点类
class Node {
    public int number;//序号
    public String name;//姓名
    public String nickName;//昵称
    public Node next;//next域 指向下一个节点

    public Node() {
    }

    //构造器初始化节点
    public Node(int no, String name, String nickName) {
        this.name = name;
        this.number = no;
        this.nickName = nickName;
        this.next = null;
    }

    @Override
    public String toString() {
        return "Node{" +
                "number=" + number +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值