数据结构之链表 - (通过代码实现方法,熟悉方法的使用)

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。


前言

本篇通过写自己代码书写一些常用的方法,熟悉方法的使用,以至于使用方法时能够更好的操作。如有错误,请在评论区指正,让我们一起交流,共同进步!


本文开始

1. 链表

1.1 什么是链表?

链表:由一个一个节点组成,每个节点分为数据域(储存数据)和地址域(储存地址),连接节点是通过他们的地址。
链表特点:物理上不连续(与顺序表不同空间不是连续的),逻辑上连续;

1.2 链表的分类

链表分类图:

在这里插入图片描述

2. 链表方法的实现

2.1 实现构建思想

构建 MyLinkedList 链表(类),每个链表中一定有节点,而节点在Java中是类,每个类中赋值val,和记录下一个节点的地址next;定义一个head代表头节点的引用,从而操作链表;再在链表中实现常用的方法。

2.2 代码实现

2.2.1 实现方法前的准备工作

首先定义一个类MyLinkedList,在其中定义一个节点类Node;
再定义一个head节点,表示头节点的引用;
为什么使用再定义head : 操作实现链表的时候,我们都从head 开始 遍历至结束。

定义节点时为什么使用static?
这样在操作节点LinkNode时不需要依赖于对象(不需要new对象), 直接类名调用即可;

public class MyLinkedList {
    static class LinkNode {
        public int val;
        public LinkNode next;

        public LinkNode(int val) {
            this.val = val;
        }
    }
    public LinkNode head; //当前链表头结点的引用
 }

2.2.2 链表方法:display() - 打印链表, contains() - 查找链表中key值, size() - 求链表长度

遇到的问题:
遍历列表:涉及循环,为什么不使用head来遍历呢?、
定义变量 cur 来遍历,当head遍历完,head = null ,当再操作链表时无法再找到头部,所以需要一个变量来代替。(链表:这里举例的方法都需要这样)

遍历列表,需要找到结束条件;
这里需要清楚 cur.next != null 与 cur != null 的区别?
cur.next: 循环会停在链表最后一个的位置,指向节点的尾端; (少遍历一个)
cur = null: 所有元素遍历完全,最后指向空;

在这里插入图片描述

//遍历列表
    public void display(){
        LinkNode cur = head;
        //为啥不使用head遍历:head遍历完最后会为null,如果再遍历会找不到链表的头部,
        //所以我都会再定义一个遍历来操作链表,让head起到标记作用
        while(cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;//往后走一步
        }

    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        LinkNode cur = head;
        while (cur != null) {
            if(cur.val == key) { //找到值就返回ture
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //得到单链表的长度:需要遍历完链表
    public int size(){
        int num = 0;
        //num记录个数
        LinkNode cur = head;
        while (cur != null) {
            cur = cur.next;
            num++;
        }
        return num;
    }

2.2.3 头插法-addFirst(), 尾插法-addLast()

头插法:插在头的位置,所以每次需要更新一下头head;
首先生成一个需要插入的节点newLinkNode(new一下), 先连接后面的newLinkNode.next = cur.next,再更新head (head = newLinkNode);

在这里插入图片描述

头插代码:

 //头插法
    public void addFirst(int data){
        LinkNode newLinkNode = new LinkNode(data);
        newLinkNode.next = head;
        head = newLinkNode;
    }

尾插法:查找链表的尾部,每次需要先找到尾部,再插入;
思想:首先生成一个需要插入的节点newLinkNode(new一下),再找到最后一个节点cur.next == null,再连接cur.next = newLinkNode即可;

在这里插入图片描述

尾插代码:

  //尾插法
    public void addLast(int data){
        LinkNode newLinkNode = new LinkNode(data);
        if(head == null) {//如果一个节点也没有,插入的节点就是head
            head = newLinkNode;
            return;
        }
        //寻找链表尾部
        LinkNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = newLinkNode;
    }

2.2.4 addIndex-插入任意位置

思想:首先判断插入的位置是否合法,考虑特殊情况(插入位置为首尾),找到要插入位置的前一个位置prvNode, 再连接;

//任意位置插入,第一个数据节点为0号下标
 public void addIndex(int index,int data){
        checkIndex(index);
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
        LinkNode cur = findIndex(index);
        LinkNode newLinkNode = new LinkNode(data);
        newLinkNode.next = cur.next;//先连后面的
        cur.next = newLinkNode;
    }
    //返回发现的下标的节点地址
    private LinkNode findIndex(int index) {
        LinkNode cur = head;
        int num = 0;
        while (num != index - 1) {
            cur = cur.next;
            num++;
        }
        return cur;
    }
    private void checkIndex(int index) throws IndexOutOfExpection {
        if(index < 0 || index >= size()) {
            throw new IndexOutOfExpection("下标异常");
        }
    }

2.2.5 remove()-删除第一次遇到的值key,removeAllKey()-删除所有的值key

remove思想:考虑特殊情况,没有节点,节点只有一个;再定义一个方法findIndex()找到要删除的前一个节点prev, 再定义要删除的节点del,直接让prev.next = del.next (连接删除节点的下一个节点的地址);

 //删除第一次出现关键字为key的节点
    public void remove(int key){
        //一个节点都没有
        if(head == null) {
            return;
        }
        //有一个节点
        if(head.val == key) {
            head = head.next;
            return;
        }
        //findPrv : 发现要删除的数的前一个节点
        LinkNode prev = findPrv(key);
        //如果没找到返回null
        if(prev == null) {
            return;
        }
        LinkNode del = prev.next;
        prev.next = del.next;
    }
    private LinkNode findPrv(int key) {
        LinkNode cur = head;
        while (cur != null) {
            if(cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

removeAllKey()
思想: 双指针操作, 首先定义两个指针prev,cur; cur指针用于遍历,prev用于记录位置, cur遍历时,如果遇到不是要删除的值,prev = next,跟随next一起走;如果遇到要删除的值,直接让prev指针连接cur指针的后一个节点(如果是连续的,cur会向后走一步直到不是要删除的值)当cur为空时,链表中的key值除头部全部删除完全;可以再判断一下第一个节点位置head.val == key;

//删除所有值为key的节点
    //思想:双指针删除所有key
    //双指针只能删除除head之外所有key
    //if第一个为要删除的key 最后需要单独处理一下,也可以开始时进行循环删除头
    public void removeAllKey(int key){
        if(head == null) {
            return;
        }
        LinkNode prev = head;
        LinkNode cur = head.next;
        while (cur != null) {
            if(cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        //单独处理头部
        if(head.val == key) {
            head = head.next;
        }
    }

总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值