【数据结构】链表

目录

一、线性表接口

二、单链表

2.1 单链表的结构定义

2.2 头插法

2.3中间位置的插入

2.4尾插法

2.5遍历链表

2.6查询线性表中是否包含指定元素

 2.7返回索引为index的元素值

2.8修改索引为index位置的元素为新值,返回修改前的元素值

2.9删除链表中索引为index的元素,返回删除前的元素值

2.10删除第一个值为val的元素

2.11在表中删除所有值为val的元素

三、带头单链表dummyHead

3.1结构定义

3.2中间位置的插入

3.3头插法和尾插法

 3.4遍历带头链表

3.5 在表中删除所有值为val的元素

 四、用递归写链表的方法

4.1链表指定位置的插入

4.2链表的正向输出

4.3链表的反向输出

五、双向链表

5.1结构定义

5.2头插法和尾插法

5.3在指定位置插入结点 

 5.4包含、查找指定索引值、修改指定索引值。

 5.5删除操作

六、ArrayList和LinkedList的区别


数组这种结构适用于频繁查询低频插入和修改的场景,若频繁插入和删除时,由于需要进行元素的搬移以及扩容等操作,浪费空间,性能开销较大,因此引入了链表。

链表是逻辑连续的,不是物理连续的。

物理连续指的是前一个元素一定是位于后一个元素之前,典型的就是数组。

逻辑连续是指每一个结点之间通过一个“钩子”连接,没有这个钩子,每个节点之间是彼此独立的毫无关系。优点就是可以在任意结点之前或者之后插入新的结点,对其他的节点并不造成影响。不需要考虑空间是否够用以及扩容问题,不会造成空间的浪费。

由于链表可以根据是否带头结点、是否循环、是否双向,排列组合有8中结构,只需要考虑三种结构:带向不带头的单链表(单向链表),单项带头链表,双向不带头链表。


一、线性表接口

定义线性表接口Seqlist。定义接口带来的好处:就是可以以非常低的成本来更换具体的子类。

public interface SeqList {
    void add(int val);
    // 在索引为index的位置插入新元素
    void add(int index,int val);
    // 查询线性表中是否包含指定元素val
    boolean contains(int val);
    // 返回索引为index的元素值
    int get(int index);
    // 修改索引为index位置的元素为新值,返回修改前的元素值
    int set(int index,int newVal);
    // 删除线性表中索引为index的元素,返回删除前的元素值
    int removeByIndex(int index);
    // 删除第一个值为val的元素
    void removeByValueOnce(int val);
    // 在线性表中删除所有值为val的元素
    void removeAllValue(int val);
}

二、单链表

有两个特殊的结点:头结点:只有头结点没有前驱。尾结点:只有尾结点没有后继。

单链表只能从前向后遍历,无论是插入还是删除方法在链表中都要找到操作位置的前驱结点。

2.1 单链表的结构定义

val表示该结点中存储的数值,next属性保存下一个结点的地址,若没有下一个结点则为null。

size表示该链表中保存的有效元素个数,head 保存了第一个结点的地址。

public class MyLinkedList implements SeqList {
    private int size;
    private Node head;
    private  class Node {
        private int val;
        private Node next;
        public Node(int val) {
            this.val = val;
        }
    }
}

2.2 头插法

必须先让新结点先挂在原先头结点的前面,然后再让head指向新的结点。

    public void addFirst(int val) {
        Node node = new Node(val);
        node.next = head;
        head = node;
        size ++ ;
    }

2.3中间位置的插入

首先要排除不合法的情况,然后根据index的特殊索引选择头插法,然后剩下的情况再继续分析,首先需要遍历该列表停在index位置的前驱节点,然后将该位置的next指向新的node,将node的next修改为原位置的next,最后size++。

    public void add(int index, int val) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("add index illegal!");
        }
        if (index == 0) {
            addFirst(val);
            return;
        }
        Node prev = head;
        for (int i = 1; i <index; i++) {
            prev = prev.next;
        }
        Node node = new Node(val);
        node.next = prev.next;
        prev.next = node;
        size++;
    }

2.4尾插法

尾插法不必全部写出,在按照索引插入中,index == size时就是尾插, 并且同样是有前驱节点,尾结点本来指向的就是空,所以新的尾结点也应该指向空。所以尾插法中写入 add(size,val)即可。

    public void add(int val) {
        add(size,val);
    }

2.5遍历链表

不能直接使用head进行遍历,遍历一次之后,头结点的地址就找不到了。所以创建一个n使他等于head,用n进行遍历。

    public String toString() {
        StringBuilder sb = new StringBuilder();
        for(Node n = head;n!=null;n = n.next){
            sb.append(n.val);
            sb.append("->");
            if(n.next == null){
                sb.append("NULL");
            }
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.add(3);
        myLinkedList.add(5);
        myLinkedList.addFirst(7);
        myLinkedList.add(2,10);
        System.out.println();
    }

 调用三种添加方法,尾插3,头插1,尾插5,头插7,在索引为3的位置插入4。最后的结果应为1,3,5,4,7。

402e48a38a364180836a97bcbd9980d7.png

2.6查询线性表中是否包含指定元素

创建一个Node类型的n使他等于head,然后遍历整个链表,当第一次遇见n的val等于指定元素,即返回true。如果遍历到最后还是没有,则返回false。遍历到最后意味着n==null。

    public boolean contains(int val) {
        for(Node n = head;n!= null;n = n.next){
            if(n.val == val){
                return true;
            }
        }
        return false;
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.add(3);
        myLinkedList.addFirst(1);
        myLinkedList.add(5);
        myLinkedList.add(7);
        myLinkedList.add(3,4);
        System.out.println(myLinkedList.toString());
        System.out.println(myLinkedList.contains(40));
        System.out.println(myLinkedList.contains(4));
    }

77184a55a1594a11a71e0794ff97b8b0.png

 2.7返回索引为index的元素值

首先要创建一个rangeCheck来判断index是否合法。然后遍历链表,找到索引为index的结点,返回该结点的值。

    public int get(int index) {
        if(!rangCheck(index)){
            throw new IllegalArgumentException("get index illegal!");
        }
        Node node = head;
        for(int i = 0;i<index;i++){
            node = node.next;
        }
        return node.val;
    }

    private boolean rangCheck(int index) {
        return index >= 0 && index < size;
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.add(3);
        myLinkedList.addFirst(1);
        myLinkedList.add(5);
        myLinkedList.add(7);
        myLinkedList.add(3,4);
        System.out.println(myLinkedList.toString());
        System.out.println(myLinkedList.get(2));
    }

9e365547745a41df85a5e455532baa4f.png

2.8修改索引为index位置的元素为新值,返回修改前的元素值

    public int set(int index, int newVal) {
        if(!rangCheck(index)){
            throw new IllegalArgumentException("get index illegal!");
        }
        Node node = head;
        for(int i = 0;i<index;i++){
            node = node.next;
        }
        int temp = node.val;
        node.val = newVal;
        return temp;
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.add(3);
    
  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世界级白日梦冠军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值