Java链表

链表概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。相较于顺序表,链表更适合数据的插入与删除。
链表的结构一共有八种,分别为:单向与双向,带头与不带头,循环与非循环,无头单向非循环链表,无头双向链表(LinkedList底层实现就是无头双向循环链表)。

1. 无头单向非循环链表

虽然无头单向非循环链表的结构很简单,通常不会单独用来存储数据,但在笔试面试中常出现,接下来将对无头单向非循环链表以及里面的方法进行实现。

定义链表的节点和对象

首先定义MyLinkedList用于实现对链表进行操作的方法,再在其中定义一个内部类ListNode —— 用于存放节点数据和指向下一个节点的引用变量,然后在MyLinkedList类中定义一个ListNode类的成员变量head —— 用于标记链表的头

public class MyLinkedList {

    static class ListNode{
        public int val;
        public ListNode next;

        public ListNode (int val) {
            this.val = val;
        }
    }

    private ListNode head;
}

实现add方法:插入元素

头插法

先用data值创建一个新节点,让新节点指向head,再把head设置为新节点

    public void addFirst(int data) {
        ListNode list = new ListNode(data);
        if(head == null) {
            head = list;
            return;
        }

        list.next = head;
        head = list;
    }

尾插法

先判断当前链表是否为空,若为空,直接让head指向新节点即可,若不为空,因为head是一直指向链表头节点的,不好改变,所以则需定义一个ListNode的引用变量cur,让其从头遍历到链表最后一个节点,再令最后一个节点的next指向新节点

    public void addLast(int data) {
        ListNode list = new ListNode(data);
        ListNode cur = this.head;
        
        if(this.head == null){
            this.head = list;
        }else{
            while (cur.next != null){
                cur = cur.next;
            }
            cur.next = list;
        }
    }

指定位置插入元素

首先要判断指定插入的位置是否合法,保证插入的位置在链表长度范围内,这时先写个size方法获取链表长度,进行判断位置合法性,若不合法则报出异常,此处自定义了一个异常。
若index = 1,则和头插法进行的操作一样,直接调用头插法就好。
同理,index为链表长度时,直接调用尾插法就好。

    //第一个数据节点为0号下标
    public void addIndex(int index,int data) throws OutOfIndexException {

        if(index < 0 || index > this.size()) {
            throw new OutOfIndexException("下标错误");
        }
        if(index == 1) {
            addFirst(data);
            return;
        }
        if(index == this.size()) {
            addLast(data);
            return;
        }

        ListNode list = new ListNode(data);
        ListNode cur = head;
        for (int i = 1; i < index; i++) {
            cur = cur.next;
        }
        
        list.next = cur.next;
        cur.next = list;
    }
    //得到单链表的长度
    public int size() {

        if(head == null) {
            return 0;
        }
        
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

打印链表

遍历链表一遍打印

    //打印链表
    public void display() {

        if(head == null) {
            return;
        }

        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println( );
    }

查找包含关键字key的节点是否在单链表当中

如果链表为空,则返回false,如果不为空,则遍历链表一遍查找key的值。

    public boolean contains(int key) {

        if(head == null) {
            return false;
        }
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

删除第一次出现关键字为key的节点

首先进行两个判断:
1.链表为空,若为空进入不了循环,什么也不干
2.值为key的节点是否是head,如果是head直接让head指向下一个节点即可;如果不是head,需要两个引用变量,一个指向值为key的节点(cur),一个指向cur的前一个节点(pre),遍历链表,如果找到key,则直接让pre指向cur的下一个节点即可。

    public void remove(int key) {

        ListNode cur = head;
        ListNode pre = cur;
        while(cur != null) {

            if(cur.val == key) {
                if(cur != head) {
                    pre.next = cur.next;
                }else {
                    head = head.next;
                }
                return;
            }else {
                pre = cur;
                cur = cur.next;
            }
        }
    }

删除所有值为key的节点

和前者删除节点相似,只是删除一个节点之后不return,继续遍历链表,删除值为key的节点

    public void removeAllKey(int key) {
        ListNode cur = head;
        ListNode pre = cur;
        while(cur != null) {
            if(cur.val == key) {
                if(cur != head) {
                    pre.next = cur.next;
                }else {
                    head = head.next;
                }
            }

            pre = cur;
            cur = cur.next;
        }
    }

清空链表

直接将head置为null即可。

    public void clear() {
       this.head = null;
    }

2. 无头双向链表节点

和单链表相似,不过是双链表内部类ListNode中多了一个变量,用来指向当前节点的前一个节点。

定义链表节点和对象

和单链表类似,多了个pre指向前一个节点,last指向链表最后一个节点

    static class ListNode{

        public int val;
        public ListNode next;
        public ListNode pre;

        public ListNode (int val) {
            this.val = val;
        }
    }
    
    private ListNode head;
    private ListNode last;

实现add方法:插入元素

头插法

因为多了last和pre,所以相较于单链表,需要多考虑last和pre的指向。

    //头插法
    public void addFirst(int data) {

        ListNode list = new ListNode(data);

        if(head == null) {
            head = list;
            last = list;
            return;
        }

        list.next = head;
        head.pre = list;
        head = list;
    }

尾插法

首先判断链表为不为空,若为空从,则新节点就是最后一个节点;若不为空,则直接让last的next指向新节点,再让last等于新的尾节点。

    public void addLast(int data) {

        ListNode list = new ListNode(data);

        if(head == null) {
            head = list;
            last = list;
            return;
        }

        last.next = list;
        list.pre = last;
        last = list;
    }

指定位置插入

前面和单链表类似,只是到后面更新节点时,cur指的是index位置的前一个节点,方便更新前一个和后一个节点的指向。

    public void addIndex(int index,int data) throws OutOfIndexException {

        if(index < 0 || index > this.size()) {
            throw new OutOfIndexException("下标错误");
        }
        if(index == 1) {
            addFirst(data);
            return;
        }
        if(index == this.size()) {
            addLast(data);
            return;
        }

        ListNode list = new ListNode(data);
        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.pre.next = list;
        list.pre = cur.pre;
        list.next = cur;
        cur.pre = list;
    }
    
    //得到单链表的长度
    public int size() {

        if(head == null) {
            return 0;
        }

        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

查找包含关键字key的节点是否存在链表中

和单链表一样,都是从头至尾遍历一遍链表查找key值得节点。

    public boolean contains(int key) {

        if(head == null) {
            return false;
        }
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

删除第一次出现关键字为key的节点

双向链表需要考虑3个点:
1.链表是否为空
2. 出现关键词key的节点是否为头节点
3. 出现关键词key的节点是否为尾节点

    public void remove(int key) {

        ListNode cur = head;
        while(cur != null) {

            if(cur.val == key) {
                if(cur != head) {
                    if(cur != last) {
                        cur.next.pre = cur.pre;
                        cur.pre.next = cur.next;
                    }else {
                        cur.pre.next = null;
                        last = cur.pre;
                    }
                }else {
                    cur.next.pre = null;
                    head = cur.next;
                }
                return;
            }else {
                cur = cur.next;
            }
        }
    }

删除所有值为key的节点

和前者类似

    public void removeAllKey(int key) {
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                if(cur != head) {
                    if(cur != last) {
                        cur.next.pre = cur.pre;
                        cur.pre.next = cur.next;
                    }else {
                        cur.pre.next = null;
                        last = cur.pre;
                    }
                }else {
                    if(head.next != null) {
                        cur.next.pre = null;
                    }
                    head = cur.next;
                }
            }
            cur = cur.next;
        }
    }

打印链表

这里和单链表一样

    public void display() {

        if(head == null) {
            return;
        }

        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println( );
    }

清空链表

这里不可像单链表那样直接,因为每一个节点都可以指向前一个节点和后一个节点,需要置空每一个节点。

    public void display() {

        if(head == null) {
            return;
        }

        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println( );
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值