数据结构_顺序表、链表(ArrayList、LinkedList的介绍、区别、增删查找等相关操作实现代码)

一、ArrayList与顺序表

1.1线性表

 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列…
 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
在这里插入图片描述
在这里插入图片描述
 链表

1.2 顺序表

 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

1.3 ArrayList

在集合框架中,ArrayList是一个普通的类,实现了List接口
注意:

  1. ArrayList是以泛型方式实现的,使用时必须要先实例化
  2. ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList。
  3. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

扩容

检测是否真正需要扩容,如果是调用grow准备扩容
预估需要库容的大小
 初步预估按照1.5倍大小扩容
 如果用户所需大小超过预估1.5倍大小,则按照用户所需大小扩容
 真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
使用copyOf进行扩容

1.4 ArrayList的缺陷

 由于 ArrayList底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。

1.5 顺序表遍历操作

参考代码

 	/**
     * 遍历顺序表中的元素
     */
    @Override
    public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(this.elem[i]+" ");
        }
        System.out.println();
    }

1.6 顺序表增加操作

参考代码:

/**
     * 新增数组,默认在数组最后新增
     * @param data
     */
    @Override
    public void add(int data) {
        //判断是否为满
        checkCapacity();
        this.elem[this.usedSize] = data;
        this.usedSize++;
    }

    //只为当前类服务,用private
    private void checkCapacity(){
        if(isFull()){
            //扩容
            elem = Arrays.copyOf(elem,usedSize*2);
        }
    }

    @Override
    public boolean isFull() {
    /*    if (usedSize == elem.length){
            return  true;
        }else {
            return false;
        }*/
        return usedSize == elem.length;
    }

1.7 顺序表在指定位置插入操作

参考代码:

/**
     * 检查pos的合法性
     * @param pos
     */
    public void checkPosOnAdd(int pos) throws PosIllegality {
        if (pos < 0 || pos > usedSize){
            System.out.println("不合法!");
            throw new PosIllegality("插入元素下标异常: "+pos);
        }
    }

    @Override
    public void add(int pos, int data) {
        try {
            checkPosOnAdd(pos);
        }catch (PosIllegality e){
            e.printStackTrace();
            return; 
        }
        checkCapacity();

        //从最后一个数据开始向后移动,   //当i<pos就结束
        for (int i = usedSize-1; pos <= i ; i--) {
            this.elem[i+1] = this.elem[i];
        }
        this.elem[pos] = data;
        //存放元素到pos位置
        //usedSize++
        usedSize++;
    }

1.8 顺序表是否包含目标值、获取指定下标的数值

参考代码:

    

    @Override
    public boolean contains(int toFind) {
        if (isEmpty()){
            return  false;
        }
        for (int i = 0; i < usedSize; i++) {
            //如果查找引用类型,重写equals
            if (this.elem[i] == toFind){
                return true;
            }
        }
        return false;
    }

    public boolean isEmpty(){
        return usedSize == 0;
    }

    @Override
    public int indexOf(int toFind) {
        if (isEmpty()){
            return -1;
        }
        for (int i = 0; i < usedSize; i++) {
            //如果查找引用类型,重写equals
            if (this.elem[i] == toFind){
                return i;
            }
        }
        return -1;
    }

    @Override
    public int get(int pos) throws MyArrayListEmpty {
        checkPosOnGetAndSet(pos);
        if (isEmpty()){
            throw new MyArrayListEmpty("获取指定下标元素时"+"顺序表为空!");
        }
        return elem[pos];
    }

    /**
     * 检查pos的合法性
     * @param pos
     */
    public void checkPosOnGetAndSet(int pos) throws PosIllegality {
        if (pos < 0 || pos >= usedSize){
            System.out.println("不合法!");
            throw new PosIllegality("获取元素下标异常: "+pos);
        }
    }

    @Override
    public void set(int pos, int value) {
        checkPosOnGetAndSet(pos);
        this.elem[pos] = value;
    }

    @Override
    public void remove(int toRemove) {
        int index = indexOf(toRemove);
        if (index == -1){
            System.out.println("没有这个数字");
            return;
        }
        for (int i = index; i < usedSize-1 ; i++) {
            this.elem[i] = this.elem[i+1];
        }
        usedSize--;
    }

    @Override
    public int size() {
        return this.usedSize;
    }

    @Override
    public void clear() {
         this.usedSize =0;
    }
}

二、链表

2.1 链表的概念及结构

 链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。
在这里插入图片描述
注意

  1. 链式结构在逻辑上是连续的,但是在物理上不一定连续
  2. 现实中的结点一般都是从堆上申请出来的
  3. 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

2.2 链表的种类

 单向 带头 循环
 单向 带头 非循环
 单向 不带头 循环
 单向 不带头 非循环
 双向 带头 循环
 双向 带头 非循环
 双向 不带头 循环
 双向 不带头 非循环

2.3 链表的插入(前插法、尾插法)

前插法参考代码

 //前插法
    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data); //实例化一个新节点
        if(this.head == null){
            this.head = node;
        }else{
            node.next = this.head; //改变插入节点的next
            this.head = node;  //改变head
        }

    }

尾插法参考代码

/**
     * 如果想让cur停在最后一个节点,cur.next != null
     * 如果想把整个链表的节点都遍历完 :cur !=null
     * 头插法的时间复杂度为O(1)
     * 尾插法的时间复杂度为O(n)
     * @param data
     */
    @Override
    public void addLast(int data) {
        //实例化一个新节点
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        if(this.head == null){
            this.head = node;
        }else {
            //  当head为空时,遍历结束,找到最后一个节点
            while (cur.next != null) {
                cur = cur.next; //cur从当前节点位置,走到下一个节点的位置
            }
            cur.next = node;
        }
    }

2.4 链表节点的删除

    /**
     * 找到关键字的前一个节点的地址
     * @param key
     * @return
     */
    private ListNode findSearch(int key){
        ListNode cur = this.head;
        while (cur.next != null) {
            if (cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return  null;
    }

    @Override
    public void remove(int key) {
        if (this.head == null){
            return;
        }

        if (this.head.val == key){
            this.head = this.head.next;
            return;
        }
        //找到前驱
        ListNode cur = findSearch(key);
        if (cur == null ){
            System.out.println("没有您要删除的数字");
            return;
        }
        ListNode del = cur.next;
        cur.next = del.next;
    }

2.5 删除所有目标关键字的节点

参考代码

    public void removeAllKey(int key) {
        if (this.head  == null){
            return;
        }
        ListNode prev = this.head;  //头节点没有判断
        ListNode cur = this.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;
        }
    }

2.6 链表的反转

参考代码

    public ListNode reverseList(){
        //判断链表是否为空
        if(head == null){
                return null;
        }
        //判断是否为单链表
        if(head.next == null){
            return head;
        }
        ListNode cur = head.next;
        head.next = null;
        while(cur != null){
            ListNode curNext = cur.next;
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }

三、总结

ArrayList和LinkedList的区别:
在这里插入图片描述

  • 37
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1.底层数据结构不同:ArrayList底层是数组LinkedList底层是双向链表。 2.ArrayList的查询效率高,LinkedList的插入删除效率高。ArrayList的查询效率因为底层数组是连续的内存空间,所以可以直接根据索引定位到元素,时间复杂度为O(1),但是插入删除的效率因为需要移动元素,所以时间复杂度为O(n);LinkedList的插入删除效率高,因为只需要修改相邻元素的指针,时间复杂度为O(1),但是查询效率因为需要遍历整个链表,时间复杂度为O(n)。 3.ArrayList的内存空间使用效率高,LinkedList的内存空间使用效率低。ArrayList底层数组是连续的内存空间,可以预先分配一定的空间,避免频繁的扩容,所以内存空间使用效率高;LinkedList因为每个节点都需要保存前后指针,所以内存空间使用效率低。 4.ArrayList的线程不安全,LinkedList的线程不安全。因为ArrayListLinkedList都不是线程安全的,所以在多线程环境下需要注意线程安全问题。 5.ArrayList的迭代器效率高,LinkedList的迭代器效率低。ArrayList的迭代器效率高,因为底层数组是连续的内存空间,所以可以直接通过索引取元素,时间复杂度为O(1);LinkedList的迭代器效率低,因为需要遍历整个链表,时间复杂度为O(n)。 6.ArrayList的随机访问效率高,LinkedList的顺序访问效率高。因为ArrayList底层是数组,可以通过索引直接访问元素,所以随机访问效率高;LinkedList因为是链表,只能顺序访问,所以顺序访问效率高。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值