【算法通关村第一关——链表青铜挑战笔记】

链表

链表的概念

链表是一种物理存储单元上非连续、非顺序的存储结构。由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。

每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

头指针:是指向链表中第一个结点的指针
首元结点:是指链表中存储的第一个数据元素的结点
头结点:是在链表的首元结点之前附设的一个结点

链表的特点

  • 不支持随机访问:访问链表只能通过头指针(即储存空间的首地址)进入链表,并通过每个结点的指针域依次向后顺序扫描其余结点,访问储存的所有数据
  • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻

单链表

如下图:

 链表的定义

根据面向对象的理论,在JAVA里规范的链表该这样定义

代码如下:

/**
 * 在算法中最常用的链表定义方式
 */
public class Node {
    public int val;
    public Node next;

    public Node(int val,Node next) {
        this.val = val;
        this.next = next;
    }

}

遍历链表

对于单链表来说,一定都是从头节点开始逐个向后访问

如下图:

代码如下:

  /**
     * 遍历单链表方法1
     **/
    public void loop1(Consumer<Integer> consumer) {
        Node cur = null;
        cur = head;
        while ( cur != null) {
            consumer.accept(cur.var);
            cur = cur.next;   //将当前节点更新为下一节点
        }

    }
    /**
     * 遍历单链表方法2
     **/
    public void loop2(Consumer<Integer> consumer) {
        Node cur = null;
        for (cur = head; cur != null; cur = cur.next) {
            consumer.accept(cur.var);
        }
    }
    /**
     * 遍历单链表方法3
     **/
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node cur = head;
            @Override
            public boolean hasNext() {  //是否有下一个元素
                return cur != null;
            }

            @Override
            public Integer next() {     //返回当前值,指向下一个节点
                int v = cur.var;
                cur = cur.next;
                return v;
            }
        };
    }

链表插入

单链表插入需要考虑3中情况:分别是头部、中部、尾部

表头插入

创建一个新结点,将新节点的指针域指向head(表头节点) 即newNode.next=head。之后我们遍历就需要从newNode开始遍历,但是我们更习惯从head开始,所以我们在更新一下表头节点即head=newNode就行了。

如下图:

    /**
     * 表头插入
     * @param val 待插入的值
     **/
    public void addFirst(int val) {
        
        head = new Node(var, head);
    }

 表中间插入

在链表中间插入时,我们需要新遍历找到要插入的位置,找到它的前驱节点和后驱节点并插入。

例如下图中,我们需要在a2节点和a3节点中插入,给newNode接上两条线,为此我们需要得到a3前驱节点a2的指针域,将a2的指针域赋值给newNode即newNode.next = node(a2).next,然后再将a2的指针域连接到newNode上即node(a2).next=newNode就完成了,由于每个节点只有一个指针,a2连接a3的线将自动断开。

切记一定要先执行newNode.next = a2.next 然后再执行a2.next = newNode , 如果先执行a2.next = newNode  的话,那么就会找不到newNode的后继结点,也就是会造成结点的丢失。

代码如下:

 /**
     * 按索引找节点
     **/
    private Node findNode(int index) {
        int i = 0;  //索引值从0开始
        for (Node cur = head; cur != null; cur = cur.next, i++) {
            if(i == index){
                return  cur;
            }
        }
        return null;
    }
   
    //异常
    private IllegalArgumentException Illegalindex(int index) {
        return new IllegalArgumentException(String.format("索引值 [%d] 不合法%n", index));
    }

    public void insert(int index,int val) {
        if (index == 0) {
            addFirst(val);
            return;
        }
        Node prev = findNode(index - 1); //上一节点
        if (prev == null) { //找不到
            throw Illegalindex(index);
        }
        Node newNode = new Node(val, prev.next);
        prev.next = newNode;
    }

}

 表尾插入

首先我们需要遍历找到最后一个节点,再将最后一个节点an的指针域连接newNode就行了即node(an).next=newNode。

如下图:

代码如下:

/**
     * 遍历找到最后一个节点
     **/
    private Node findLast() {
        if (head == null) {    //判断是否为空链表
            return null;
        }
        Node cur = null;
        cur = head;
        while (cur.next != null) {
            cur = cur.next;     //更新当前节点为下一节点
        }
        return cur;
    }
    /**
     * 表尾插入
     **/
    public void addLast(int val) {

        Node last = findLast(); //赋值为最后一个节点
        if (last == null) {
            addFirst(val);  //使用表头插入的逻辑
            return;
        }

        last.next = new Node(val,null);
    }

链表删除

删除同样分为:删除表头节点、删除中间节点和尾部节点

表头删除

表头删除,只需要将头节点(head)向后移动一次,原来的节点将被jvm回收。

代码如下:

  /**
     * 删除头结点
     **/
    public void removeFirst() {
        if (head == null) {
            throw Illegalindex(0);
        }
        head = head.next;
    }

 中间节点删除

删除中间节点时,也要用cur.next来比较,例如将节点a3删除,需要将a2的指针域更新为a4的数据域即cur.next=cur.next.next

如下图:

 代码如下:

 /**
     * 按索引删除结点
     **/
    public void remove(int index) {
        if (index == 0) {
            removeFirst();
            return;
        }

        Node prev = findNode(index - 1);    //上一节点
        if (prev == null) {
            throw Illegalindex(0);
        }
        Node remove = prev.next;    // 被删节点
        if (remove == null) {
            throw Illegalindex(0);
        }
        prev.next=remove.next;

    }

表尾删除

表尾删除,将尾节点的前驱节点的指针域变为空就行即cur.next=null

如下图:

完整代码:


import java.util.Iterator;
import java.util.function.Consumer;

/**
 * @ClassName SinglyLinkedList
 * @Description TODO
 * @Author lxy23
 * @Date 2023/7/20 14:08
 * @Version 1.0
 */
public class SinglyLinkedList implements Iterable<Integer> { //整体

    private Node head = null;   //表头

    //定义节点类
    private static class Node {

        int val;    //数据域:存放数据
        Node next;  //指针域:指向下一个节点

        public Node(int val, Node next){
            this.val = val;
            this.next = next;
        }
    }

    /**
     * 表头插入
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 14:36
     * @param val 待插入的值
     **/
    public void addFirst(int val) {
        //创建应该新节点newNode.next=head head=newNode
        head = new Node(val, head);
    }
    /**
     * 遍历单链表方法1
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 14:44
     * @return
     **/
    public void loop1(Consumer<Integer> consumer) {
        Node cur = null;
        cur = head;
        while ( cur != null) {
            consumer.accept(cur.val);
            cur = cur.next;   //将当前节点更新为下一节点
        }

    }
    /**
     * 遍历单链表方法2
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 14:44
     * @return
     **/
    public void loop2(Consumer<Integer> consumer) {
        Node cur = null;

        for (cur = head; cur != null; cur = cur.next) {
            consumer.accept(cur.val);
        }
    }
    /**
     * 遍历单链表方法3
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 14:44
     * @return
     **/
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node cur = head;
            @Override
            public boolean hasNext() {  //是否有下一个元素
                return cur != null;
            }

            @Override
            public Integer next() {     //返回当前值,指向下一个节点
                int v = cur.val;
                cur = cur.next;
                return v;
            }
        };
    }
    /**
     * 遍历找到最后一个节点
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 15:55
     * @return unit3_链表就这些题.单链表.SinglyLinkedList.Node
     **/
    private Node findLast() {
        if (head == null) {    //判断是否为空链表
            return null;
        }
        Node cur = null;
        cur = head;
        while (cur.next != null) {
            cur = cur.next;     //更新当前节点为下一节点
        }
        return cur;
    }
    /**
     * 表尾插入
     * @author: lxy23
     * @description:
     * @Date:  2023/7/20 16:44
     * @param val
     **/
    public void addLast(int val) {

        Node last = findLast(); //赋值为最后一个节点
        if (last == null) {
            addFirst(val);  //使用表头插入的逻辑
            return;
        }

        last.next = new Node(val,null);
    }
    /**
     * 找到需要插入位置的节点d的方法
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 17:13
     * @param index
     * @return unit3_链表就这些题.单链表.SinglyLinkedList.Node
     **/
    private Node findNode(int index) {
        int i = 0;  //索引值从0开始
        for (Node cur = head; cur != null; cur = cur.next, i++) {
            if(i == index){
                return  cur;
            }
        }
        return null;
    }
    /**
     * 取得该索引值位置节点的值
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 17:37
     * @param index
     * @return int
     **/
    public int get(int index) {
        Node cur = findNode(index);
        if (cur == null) {  //没找到抛出异常
            throw Illegalindex(index);
        }
        return cur.val;
    }

    private IllegalArgumentException Illegalindex(int index) {
        return new IllegalArgumentException(String.format("索引值 [%d] 不合法%n", index));
    }

    public void insert(int index,int val) {
        if (index == 0) {
            addFirst(val);
            return;
        }
        Node prev = findNode(index - 1); //上一节点
        if (prev == null) { //找不到
            throw Illegalindex(index);
        }
        Node newNode = new Node(val, prev.next);
        prev.next = newNode;
    }
    /**
     * 删除头结点
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 18:05
     **/
    public void removeFirst() {
        if (head == null) {
            throw Illegalindex(0);
        }
        head = head.next;
    }

    /**
     * 按索引删除结点
     * @author: lxy23
     * @description: TODO
     * @Date:  2023/7/20 18:05
     **/
    public void remove(int index) {
        if (index == 0) {
            removeFirst();
            return;
        }

        Node prev = findNode(index - 1);    //上一节点
        if (prev == null) {
            throw Illegalindex(0);
        }
        Node remove = prev.next;    // 被删节点
        if (remove == null) {
            throw Illegalindex(0);
        }
        prev.next=remove.next;

    }


}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值