6-带头单向非循环链表

文章介绍了Java中单链表的实现,包括在链表任意位置插入和删除节点的方法,以及链表头部和尾部的操作。同时,文章对比了动态数组和单链表在查找、修改和插入删除操作上的时间复杂度,强调了两者在不同场景下的适用性。
摘要由CSDN通过智能技术生成

目录

1.车厢类

2.火车类

2.1.在链表任意位置index处插入节点

2.2.在链表头部添加节点

2.3.在链表尾部添加节点

2.4.在链表任意位置index处删除节点

2.5.删除链表头部节点

2.6.删除链表尾部节点

2.7.toString()方法

3.总代码实现

4.动态数组VS单链表

4.1.动态数组

4.2.单链表

4.3.All in all


1.车厢类

/**
 * 车厢类
 */
class Node {
    //存储具体元素
    int data;
    //存储下一个节点的地址
    Node next;

    //构造方法
    public Node(int data) {
        this.data=data;
    }

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

2.火车类

/**
 * 带头单链表(头节点不存储具体元素)
 */
public class SingleLinkedListWithHead {
    //虚拟头节点 在内存中实实在在存在的节点,有一个引用指向
    private Node dummyHead = new Node(-1); //-1 表示这个节点的data值无意义,这个节点就是来连接/脱钩其他节点的。若data不赋值,int默认就是0,而有时0恰好又是需要存储的元素,而负数较少见。
//    只声明一个Node类的引用,无具体节点,在内存中没开空间
//    private Node dummyHead;
    
    //单链表中具体节点的个数(不包含虚拟头节点)
    private int size;
    
    //具体方法实现
    //...
}

2.1.在链表任意位置index处插入节点

/**
 * 在链表任意位置index处插入节点
 * @param index
 * @param data
 */
public void addIndex(int index, int data) {
    if(index < 0 || index > size) {
        System.err.println("add index illegal!");
        return;
    }
       
    //此时无论index为何值,都存在前驱节点
    Node prev = dummyHead;
    for (int i = 0; i < index; i++) {
        prev = prev.next;
    }
        
    //prev就指向了待插入的前驱节点
    Node node = new Node(data);
    node.next = prev.next;
    prev.next = node;
    size++;
}

2.2.在链表头部添加节点

/**
 * 在链表头部添加节点
 * @param data
 */
public void addFirst(int data) {
    addIndex(0, data);
}

2.3.在链表尾部添加节点

/**
 * 在链表尾部添加节点
 * @param data
 */
public void addLast(int data) {
    addIndex(size, data);
}

2.4.在链表任意位置index处删除节点

/**
 * 在链表任意位置index处删除节点
 * @param index
 * @return 返回删除前的节点值
 */
public int removeIndex(int index) {
    if(index < 0 || index >= size) {
        System.err.println("remove index illegal!");
        return -1;
    }
        
    Node prev = dummyHead;
    //先找到待删除节点的前驱
    for (int i = 0; i < index; i++) {
        prev = prev.next;
    }
        
    //prev指向了待删除节点的前驱
    Node node = prev.next;
    prev.next = node.next;
    node.next = null;
    size--;
    return node.data;
}

2.5.删除链表头部节点

/**
 * 删除链表头部节点
 * @return
 */
public int removeFirst() {
    return removeIndex(0);
}

2.6.删除链表尾部节点

/**
 * 删除链表尾部节点
 * @return
 */
public int removeLast() {
    return removeIndex(size - 1);
}

2.7.toString()方法

public String toString() {
    String ret = "";
    Node node = dummyHead.next;
    while (node != null) {
        ret += node.data + "->";
        node = node.next;
    }
    ret += "NULL";
    return ret;
}

3.总代码实现

/**
 * 车厢类
 */
class Node {
    //存储具体元素
    int data;
    //存储下一个节点的地址
    Node next;//next是自定义的Node引用类型,存储Node类型的对象的地址,即车厢地址
    //所有类以及数组都是引用类型,所有引用数据类型存储的都是一个类的对象的地址(起个别名)
    //所有引用类型的默认值都是NULL
    //当new一个对象时,它才有意义

    //构造方法 alt+insert 但此电脑无法使用
    public Node(int data) {
        this.data=data;
    }

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

/**
 * 火车类 带头单链表(头节点不存储具体元素)
 */
public class SingleLinkedListWithHead {
    //虚拟头节点 在内存中实实在在存在的节点,有一个引用指向
    private Node dummyHead = new Node(-1); //-1 表示这个节点的data值无意义,这个节点就是来连接/脱钩其他节点的。若data不赋值,int默认就是0,而有时0恰好又是需要存储的元素,而负数较少见。
//    只声明一个Node类的引用,无具体节点,在内存中没开空间
//    private Node dummyHead;

    //单链表中具体节点的个数(不包含虚拟头节点)
    private int size;

    /**
     * 在链表任意位置index处插入节点
     * @param index
     * @param data
     */
    public void addIndex(int index, int data) {
        if(index < 0 || index > size) {
            System.err.println("add index illegal!");
            return;
        }

        //此时无论index为何值,都存在前驱节点
        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        //prev就指向了待插入的前驱节点
        Node node = new Node(data);
        node.next = prev.next;
        prev.next = node;
        size++;
    }

    /**
     * 在链表头部添加节点
     * @param data
     */
    public void addFirst(int data) {
        addIndex(0, data);
    }

    /**
     * 在链表尾部添加节点
     * @param data
     */
    public void addLast(int data) {
        addIndex(size, data);
    }

    /**
     * 在链表任意位置index处删除节点
     * @param index
     * @return
     */
    public int removeIndex(int index) {
        if(index < 0 || index >= size) {
            System.err.println("remove index illegal!");
            return -1;
        }

        Node prev = dummyHead;
        //先找到待删除节点的前驱
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        //prev指向了待删除节点的前驱
        Node node = prev.next;
        prev.next = node.next;
        node.next = null;
        size--;
        return node.data;
    }

    /**
     * 删除链表头部节点
     * @return
     */
    public int removeFirst() {
        return removeIndex(0);
    }

    /**
     * 删除链表尾部节点
     * @return
     */
    public int removeLast() {
        return removeIndex(size - 1);
    }

    public String toString() {
        String ret = "";
        Node node = dummyHead.next;
        while (node != null) {
            ret += node.data + "->";
            node = node.next;
        }
        ret += "NULL";
        return ret;
    }
}

4.动态数组VS单链表

4.1.动态数组

①查找指定index位置的元素arr[1] -> 时间复杂度是O(1)。

②修改index位置的元素arr[1] = newVal; -> O(1)。

有索引,数据根据下标得到数据以及修改数据。

③在任意位置插入和删除元素

  1. 在数组头部删除元素,在尾部添加元素 -> O(1)。
  2. 一般而言,在数组除了头尾位置插入与删除元素都要进行元素搬移,耗时 -> O(n)。

4.2.单链表

①查找index = x位置的元素 -> O(n)。

②修改index = x位置的元素 -> O(n)。

由于单链表只能从头向后遍历,因此查找指定位置时,需要走n步。

③在单链表中插入和删除元素只需要修改几个引用的指向即可,不牵扯到数据的搬移。

④由于单链表物理不连续,因此需要存储数据时,比数组(需要扩容)要省空间。

4.3.All in all

①一般数据集只是频繁地查找而不添加或者修改,用数组比较高效。

②数据集牵扯到频繁地添加与删除时,用链表更高效。

如果一个节点已知在链表的靠后位置,但单链表只能从前向后遍历,因此还得从头来过。

故引入双向链表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值