一篇文章入门单链表+刷题实践【java实现+详细注释】

文章目录

  • 节点定义
  • 链表类
    • 获取链表长度
    • 清空链表
    • 添加节点到链表尾部
    • 根据id来删除节点
    • 根据id来查询节点
    • 修改相同id的节点信息
    • 打印链表
    • 逆序打印
    • 反转链表
  • 测试
    • 代码
    • 打印信息
  • 习题
    • 反转链表
    • 删除链表的节点
    • 删除链表的倒数第k个节点
    • 合并两个排序的链表

节点定义

关于节点里面所存储的信息,需要什么就添加什么,但是next字段是必须的,因为用来串联一个节点和下一个节点。

package com.dam.data_structure.linked_list;

public 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;
    }

    @Override
    public String toString() {
        return "HeroNode [id=" + id + ", name=" + name + ", nickname=" + nickname + "]";
    }

    public HeroNode next(int id, String name, String nickname) {
        next = new HeroNode(id, name, nickname);
        return next;
    }

}

常规添加节点方法,这样很多小伙伴可能会觉得比较麻烦

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

这里提供一个快速添加节点的方法

 public HeroNode next(int id, String name, String nickname) {
     next = new HeroNode(id, name, nickname);
     return next;
 }

使用方式

HeroSingleLinkedList linkedList = new HeroSingleLinkedList();
        链式添加元素
        linkedList.head.next(5, "小明", "小明")
                .next(6, "小花", "小花")
                .next(7, "小华", "小华")
                .next(8, "李华", "李华");

链表类

本文的链表会使用一个空的结点来作为head,该节点上面没有什么实际数据,只是为了让链表的其他方法更加容易实现

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

获取链表长度

注意,计算长度是从head的下一个节点开始计算

    /**
     * 获取链表长度
     *
     * @return
     */
    public int size() {
        int size = 0;
        HeroNode temp = this.head.next;
        while (temp != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }

清空链表

直接重置头节点就行

  /**
     * 清空链表
     */
    public void clear() {
        this.head.next = null;
    }

添加节点到链表尾部

找到链表的最后一个节点,将最后一个节点的next指向要添加的节点即可

  /**
     * 添加元素
     *
     * @param heroNode
     */
    public void add(HeroNode heroNode) {
        HeroNode temp = this.head;
        找到最后一个元素,将heroNode添加即可,最后一个元素的next肯定为null
        while (temp != null) {
            if (temp.next == null) {
                //--if--找到了最后一个元素 添加元素之后即可退出
                temp.next = heroNode;
                break;
            } else {
                //--if--不是最后一个元素,继续往下面寻找
                temp = temp.next;
            }
        }
    }

根据id来删除节点

删除节点一般是遍历到要删除节点的前一个元素,然后将前一个元素的next指向null或者要删除元素的下一个节点

   /**
     * 根据id来删除节点
     *
     * @param id
     */
    public HeroNode deleteById(int id) {
        HeroNode temp = this.head;
        while (temp != null && temp.next != null) {
            if (temp.next.id == id) {
                //记录要删除的元素
                HeroNode record = temp.next;
                if (temp.next.next != null) {
                    //--if--如果还有下下个节点
                    temp.next = temp.next.next;
                } else {
                    //--if--如果没有下下节点,直接赋值为空就行
                    temp.next = null;
                }
                return record;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素,无需删除");
        return null;
    }

根据id来查询节点

   /**
     * 根据id来查询节点
     *
     * @param id
     */
    public HeroNode getById(int id) {
        HeroNode temp = this.head;
        while (temp != null) {
            if (temp.id == id) {
                return temp;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素");
        return null;
    }

修改相同id的节点信息

    /**
     * 根据id匹配来修改节点
     *
     * @param heroNode
     */
    public void update(HeroNode heroNode) {
        HeroNode temp = this.head;
        while (temp != null) {
            if (temp.id == heroNode.id) {
                temp.name = heroNode.name;
                temp.nickname = heroNode.nickname;
                return;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素,无法修改");
    }

打印链表

   /**
     * 打印链表的元素
     */
    public void print() {
        //不打印头节点,从头节点的后一个元素开始
        HeroNode temp = this.head.next;
        while (temp != null) {
            System.out.println(temp.toString());
            temp = temp.next;
        }
    }

逆序打印

利用栈先进后出特性来协助

  /**
     * 逆序打印
     */
    public void reversePrint() {
        //先将所有元素存储到栈中
        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = this.head.next;
        while (temp != null) {
            stack.push(temp);
            temp = temp.next;
        }

        //从栈中取出元素并打印
        while (!stack.isEmpty()) {
            System.out.println(stack.pop().toString());
        }
    }

反转链表

反转链表原理
一篇文章入门单链表+刷题实践【java实现+详细注释】_第1张图片

 /**
     * 原地反转链表
     *
     * @param linkedList
     */
    public static void reverseList(HeroSingleLinkedList linkedList) {
        HeroNode head = linkedList.head;

        //如果只有一个元素或者没有元素,直接退出即可
        if (head == null || head.next == null) {
            return;
        }

        HeroNode cur = head.next;
        HeroNode last = null;
        while (cur != null) {
            HeroNode temp = cur.next;
            cur.next = last;
            last = cur;
            cur = temp;
        }

        head.next = last;
    }

测试

代码

package com.dam.data_structure.linked_list;

import java.util.Stack;

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

    /**
     * 添加元素
     *
     * @param heroNode
     */
    public void add(HeroNode heroNode) {
        HeroNode temp = this.head;
        找到最后一个元素,将heroNode添加即可,最后一个元素的next肯定为null
        while (temp != null) {
            if (temp.next == null) {
                //--if--找到了最后一个元素 添加元素之后即可退出
                temp.next = heroNode;
                break;
            } else {
                //--if--不是最后一个元素,继续往下面寻找
                temp = temp.next;
            }
        }
    }

    /**
     * 根据id来删除节点
     *
     * @param id
     */
    public HeroNode deleteById(int id) {
        HeroNode temp = this.head;
        while (temp != null && temp.next != null) {
            if (temp.next.id == id) {
                //记录要删除的元素
                HeroNode record = temp.next;
                if (temp.next.next != null) {
                    //--if--如果还有下下个节点
                    temp.next = temp.next.next;
                } else {
                    //--if--如果没有下下节点,直接赋值为空就行
                    temp.next = null;
                }
                return record;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素,无需删除");
        return null;
    }

    /**
     * 根据id来查询节点
     *
     * @param id
     */
    public HeroNode getById(int id) {
        HeroNode temp = this.head;
        while (temp != null) {
            if (temp.id == id) {
                return temp;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素");
        return null;
    }

    /**
     * 根据id匹配来修改节点
     *
     * @param heroNode
     */
    public void update(HeroNode heroNode) {
        HeroNode temp = this.head;
        while (temp != null) {
            if (temp.id == heroNode.id) {
                temp.name = heroNode.name;
                temp.nickname = heroNode.nickname;
                return;
            }
            temp = temp.next;
        }
        System.out.println("没有找到相应id的元素,无法修改");
    }

    /**
     * 获取链表长度
     *
     * @return
     */
    public int size() {
        int size = 0;
        HeroNode temp = this.head.next;
        while (temp != null) {
            size++;
            temp = temp.next;
        }
        return size;
    }

    /**
     * 清空链表
     */
    public void clear() {
        this.head.next = null;
    }

    /**
     * 打印链表的元素
     */
    public void print() {
        //不打印头节点,从头节点的后一个元素开始
        HeroNode temp = this.head.next;
        while (temp != null) {
            System.out.println(temp.toString());
            temp = temp.next;
        }
    }

    /**
     * 逆序打印
     */
    public void reversePrint() {
        //先将所有元素存储到栈中
        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = this.head.next;
        while (temp != null) {
            stack.push(temp);
            temp = temp.next;
        }

        //从栈中取出元素并打印
        while (!stack.isEmpty()) {
            System.out.println(stack.pop().toString());
        }
    }

    /**
     * 原地反转链表
     *
     * @param linkedList
     */
    public static void reverseList(HeroSingleLinkedList linkedList) {
        HeroNode head = linkedList.head;

        //如果只有一个元素或者没有元素,直接退出即可
        if (head == null || head.next == null) {
            return;
        }

        HeroNode cur = head.next;
        HeroNode last = null;
        while (cur != null) {
            HeroNode temp = cur.next;
            cur.next = last;
            last = cur;
            cur = temp;
        }

        head.next = last;
    }

    public static void main(String[] args) {
        HeroSingleLinkedList linkedList = new HeroSingleLinkedList();

        链式添加元素
        linkedList.head.next(5, "小明", "小明")
                .next(6, "小花", "小花")
                .next(7, "小华", "小华")
                .next(8, "李华", "李华");

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

        正序打印测试
        System.out.println("正序打印测试---------------------------------------------------------------------------------------");
        linkedList.print();
        System.out.println();

        逆序打印测试
        System.out.println("逆序打印测试---------------------------------------------------------------------------------------");
        linkedList.reversePrint();
        System.out.println();

        获取链表长度测试
        System.out.println("获取链表长度测试---------------------------------------------------------------------------------------");
        System.out.println("链表长度:" + linkedList.size());
        System.out.println();

        反转链表
        System.out.println("反转链表测试---------------------------------------------------------------------------------------");
        HeroSingleLinkedList.reverseList(linkedList);
        linkedList.print();
        System.out.println();

        修改元素
        System.out.println("修改链表元素测试---------------------------------------------------------------------------------------");
        linkedList.update(new HeroNode(4, "武松", "行者"));
        linkedList.print();
        System.out.println();

        根据id查询元素
        System.out.println("根据id查询元素---------------------------------------------------------------------------------------");
        System.out.println("linkedList.getById(2):" + linkedList.getById(2));
        System.out.println();

        删除元素
        System.out.println("删除链表元素测试---------------------------------------------------------------------------------------");
        linkedList.print();
        System.out.println("删除10");
        linkedList.deleteById(10);
        System.out.println("删除1");
        linkedList.deleteById(1);
        linkedList.print();
        System.out.println("删除3");
        linkedList.deleteById(3);
        linkedList.print();
        System.out.println("删除4");
        linkedList.deleteById(4);
        linkedList.print();
        System.out.println("删除2");
        linkedList.deleteById(2);
        linkedList.print();
        System.out.println();
    }
}

打印信息

正序打印测试---------------------------------------------------------------------------------------
HeroNode [id=5, name=小明, nickname=小明]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=1, name=宋江, nickname=及时雨]
HeroNode [id=4, name=林冲, nickname=豹子头]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=3, name=吴用, nickname=智多星]

逆序打印测试---------------------------------------------------------------------------------------
HeroNode [id=3, name=吴用, nickname=智多星]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=林冲, nickname=豹子头]
HeroNode [id=1, name=宋江, nickname=及时雨]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]

获取链表长度测试---------------------------------------------------------------------------------------
链表长度:8

反转链表测试---------------------------------------------------------------------------------------
HeroNode [id=3, name=吴用, nickname=智多星]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=林冲, nickname=豹子头]
HeroNode [id=1, name=宋江, nickname=及时雨]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]

修改链表元素测试---------------------------------------------------------------------------------------
HeroNode [id=3, name=吴用, nickname=智多星]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=武松, nickname=行者]
HeroNode [id=1, name=宋江, nickname=及时雨]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]

根据id查询元素---------------------------------------------------------------------------------------
linkedList.getById(2):HeroNode [id=2, name=卢俊义, nickname=玉麒麟]

删除链表元素测试---------------------------------------------------------------------------------------
HeroNode [id=3, name=吴用, nickname=智多星]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=武松, nickname=行者]
HeroNode [id=1, name=宋江, nickname=及时雨]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]
删除10
没有找到相应id的元素,无需删除
删除1
HeroNode [id=3, name=吴用, nickname=智多星]
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=武松, nickname=行者]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]
删除3
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=4, name=武松, nickname=行者]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]
删除4
HeroNode [id=2, name=卢俊义, nickname=玉麒麟]
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]
删除2
HeroNode [id=8, name=李华, nickname=李华]
HeroNode [id=7, name=小华, nickname=小华]
HeroNode [id=6, name=小花, nickname=小花]
HeroNode [id=5, name=小明, nickname=小明]


Process finished with exit code 0

习题

反转链表

https://leetcode.cn/problems/fan-zhuan-lian-biao-lcof/

/**
 * 注意:head上面也有值,不是单纯作为头节点
 * @param head
 * @return
 */
public ListNode reverseList(ListNode head) {
    ListNode cur = head;
    ListNode last = null;
    while (cur != null) {
        //先将cur后面的部分存储起来
        ListNode temp = cur.next;
        cur.next = last;
        last = cur;
        cur = temp;
    }
    return last;
}

删除链表的节点

https://leetcode.cn/problems/shan-chu-lian-biao-de-jie-dian-lcof/

public ListNode deleteNode(ListNode head, int val) {
    if (head == null) {
        return null;
    }
    if (head.val == val) {
    	//删除头节点,直接返回头节点的下一个节点即可
        return head.next;
    }
    ListNode temp = head;
    while (temp != null && temp.next != null) {
        if (temp.next.val == val) {
            if (temp.next.next != null) {
                temp.next = temp.next.next;
            } else {
                temp.next = null;
            }
            break;
        }
        temp = temp.next;
    }
    return head;
}

删除链表的倒数第k个节点

https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/

public ListNode getKthFromEnd(ListNode head, int k) {
    ListNode fast = head;
    ListNode slow = head;
    //让快指针先走k步
    for (int i = 0; i < k; i++) {
        fast = fast.next;
    }
    //快慢一起走,快走到尽头,慢就是倒数第k个
    while (true) {
        if (fast == null) {
            break;
        }
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

合并两个排序的链表

https://leetcode.cn/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/description/

    /**
     * 借助辅助节点
     *
     * @param l1
     * @param l2
     * @return
     */
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //添加一个辅助节点,主要是方便后面的连接操作
        ListNode merge = new ListNode(-1);
        ListNode temp = merge;
        
        //当l1和l2都不为空的时候,就需要判断哪个更小
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                temp.next = l1;
                l1 = l1.next;
            } else {
                temp.next = l2;
                l2 = l2.next;
            }
            temp = temp.next;
        }
        //当有一个为空之后,后面的值直接和不为空的那个链表片段一样就行
        temp.next = l1 != null ? l1 : l2;

        //注意返回的是辅助节点.next
        return merge.next;
    }

你可能感兴趣的:(数据结构与算法,java,数据结构,链表)