数组与链表

简介: 数组与链表

前言

数组和链表是两种数据结构,数组非常简单易用但是它有两个非常大的缺点,一个是数组一旦创建无法扩展,另一个则是数组的查找和删除的速度很慢.

链表改善了一些数组的缺点,但是同样的链表自身也存在一些自己的缺点.

本篇博客将为大家介绍一下这数组和链表特点及各自的优缺点.

阅读前的准备工作

大O表示法,一种粗略的评价计算机算法效率的方法.后面的内容会用到表示效率的方法.

1. 数组

我们按数组中的数组是否排序对数组进行划分,将数组分为无序数组和有序数组.无序数组中的数组是无序的,而有序数组中的数据则是升序或者降序排序的.

1.1 无序数组

因为无序数组中的数据是无序的,往数组中添加数据时不用进行比较和移动数据,所以往无序数组里面添加数据很快.无论是添加第一个数据还是第一万个数据所需的时间是相同的,效率为O(1).

至于查找和删除速度就没有那么快了,以数组中有一万个数据项为例,最少需要比较1次,最多则需要比较一万次,平均下来需要比较5000次,即N/2次比较,N代表数据量,大O表示法中常数可以忽略,所以效率为O(N).

结论:

  1. 插入很快,因为总是将数据插入到数组的空余位置.
  2. 查找和删除很慢,假设数组的长度为N,那么平均的查找/删除的比较次数为N/2,并且还需要移动数据.

1.2 有序数组

无序数组中存放的数据是无序的,有序数组里面存放的数据则是有序的(有可能是升序有可能是降序).

因为有序数组中的数据是按升序/降序排列的,所以插入的时候需要进行排序并且移动数据项,所有有序数组的插入速度比无序数组慢. 效率为O(N).

删除速度和无序数组一样慢 效率为O(N).

有序数组的查找速度要比无序数组快,这是因为使用了一个叫做二分查找的算法.

二分查找: 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列.

有一个关于二分查找的形象类比 -> 猜数游戏

假设要在0-100之间猜一个数,那么你第一个要猜的数字就是100的一半50的时候,你的朋友会告诉你这个数字比要猜的数字是大还是小,如果比数字大,你接下来要猜的数字就是50的一半25,你的朋友说比这个数字要大,那么你下面要猜的数字就是25-50中间的那个数37,以此类推...

使用二分查找可极大的提高查找的效率,假设一个有序数组有十亿个数据,那么查找到所需的数字,最多只需比较30次.

有序数组使用二分查找的效率为O(logN).有序数组也可以通过二分查找来新增和删除数据以提高效率,但是依然需要在新增/删除后移动数据项,所以效率依然会有影响.

总结:

  1. 有序数组的查找速度比无序数组高,效率为O(logN)
  2. 有序数组的删除和新增速度很慢,效率为O(N)

1.3 数组总结

数组虽然简单易用,但是数组有两个致命的缺点:

  1. 数组存储的数量有限,创建的过大浪费资源,创建的过小溢出
  2. 数组的效率比其他数据结构低
  • 无序数组插入效率为O(1)时间,但是查找花费O(N)时间
  • 有序数组查找花费O(logN)时间,插入花费O(N)时间
  • 删除需要移动平均半数的数据项,所以删除都是O(N)的时间

2. 链表

数组一经创建大小就固定住了,无法修改,链表在这方面做出了改善,只要内存够用就可以无限制的扩大.

链表是继数组之后应用最广泛的数据结构.

2.1 链表的特点

链表为什么叫链表呢? 因为它保存数据的方式就像一条锁链

链表保存数据的方式很像上面的这一条锁链,每一块锁链就是一个链节点,链节点保存着自己的数据同时通过自己的next()方法指向下一个链节点. 链表通过链节点不断地调用next()方法就可以遍历链表中的所有数据.

在链表中,每个数据项都被包含在"链节点"(link)中,一个链结点是某个类的对象,这个类可以叫做Link.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.

每个Link对象中都包含一个对下一个链结点引用的字段(通常叫做next).

链表本身的对象中有一个字段指向对第一个链结点的引用.

数据与链表查找数据的区别: 在数组中查找数据就像在一个大仓库里面一样,一号房间没有,我们去二号房间,二号房间没有我们去三号房间,以此类推.. 按照地址找完所有房间就可以了.

而在链表中查找数据就像单线汇报的地下工作者,你是孤狼你想要汇报点情报给你的顶级上司毒蜂,但是你必须先报告给你的接头人猪刚鬣,猪刚鬣在报告给它的单线接头人土行孙,最后由土行孙报告给毒蜂.只能一个找一个,这样最终完成任务.

2.2 Java代码

链节点类:

 
/**
 * @author liuboren
 * @Title: 链节点
 * @Description:
 * @date 2019/11/20 19:30
 */
public class Link {
    //  保存的数据
    public int data;
 
    // 指向的下一个链节点
    public Link nextLink;
 
    public Link(int data) {
        this.data = data;
    }
 
    public int getData() {
        return data;
    }
 
    public void setData(int data) {
        this.data = data;
    }
 
    public Link getNextLink() {
        return nextLink;
    }
 
    public void setNextLink(Link nextLink) {
        this.nextLink = nextLink;
    }
}

链表类

 
/**
 * @author liuboren
 * @Title: 链表类
 * @Description:
 * @date 2019/11/20 19:31
 */
public class LinkList {
    private Link first;
 
    public LinkList() {
        first = null;
    }
 
    // 新增链节点方法
    public void insertFirst(int data) {
        Link link = new Link(data);
        link.setNextLink(first);
        first = link;
    }
}

在新增节点的时候,新增的link的next方法指向原来的first节点,并将链表类的first指向新增的节点.

2.4 其他链表

刚刚介绍的链表是单向链表,只能从后往前遍历,其他的链表还有双端链表、双向链表、有序链表.

再简单介绍一下双端链表吧.

双端链表就是在单向链表的基础上,新增一个成员变量指向链表的最后一个对象.

双端链表代码:

/**
 * @author liuboren
 * @Title: 链表类
 * @Description:
 * @date 2019/11/20 19:31
 */
public class LinkList {
    private Link first;
    private Link last;
 
    public LinkList() {
        first = null;
    }
 
    public boolean isEmpty() {
        return first == null;
    }
 
    // 新增链节点方法
    public void insertFirst(int data) {
        Link newLink = new Link(data);
        newLink.setNextLink(first);
        if (isEmpty()) {
            last = newLink;
        }
        first = newLink;
 
    }
}

双向链表则是可以从first和last两个方向进行遍历,有序链表的数据都是按照关键字的顺序排列的,本文不再展开了.

2.5 链表的效率

链表的效率:

  • 表头插入和删除速度都很快,花费O(1)的时间.
  • 平均起来,查找&删除&插入在制定链节点后面都需要搜索一半的链节点需要O(N)次比较,虽然数组也需要O(N)次比较,但是链表让然要快一些,因为不需要移动数据(只需要改变他们的引用)

3. 总结

链表解决了数组大小不能扩展的问题,但是链表自身依然存在一些问题(在链表的链节点后面查找&删除&插入的效率不高),那么有没有一种数据结构即拥有二者的优点又改善了二者的缺点呢,答案是肯定的,下篇博客将为您介绍这种优秀的数据结构,敬请期待.

相关文章
|
1月前
|
存储 算法 搜索推荐
探索常见数据结构:数组、链表、栈、队列、树和图
探索常见数据结构:数组、链表、栈、队列、树和图
101 64
|
5月前
|
Java
环形数组链表(java)
环形数组链表(java)
|
1月前
|
存储
一篇文章了解区分指针数组,数组指针,函数指针,链表。
一篇文章了解区分指针数组,数组指针,函数指针,链表。
18 0
|
3月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
61 0
|
3月前
|
存储 Java 开发者
揭秘!HashMap底层结构大起底:从数组到链表,再到红黑树,Java性能优化的秘密武器!
【8月更文挑战第24天】HashMap是Java集合框架中的核心组件,以其高效的键值对存储和快速访问能力广受开发者欢迎。在JDK 1.8及以后版本中,HashMap采用了数组+链表+红黑树的混合结构,实现了高性能的同时解决了哈希冲突问题。数组作为基石确保了快速定位;链表则用于处理哈希冲突;而当链表长度达到一定阈值时,通过转换为红黑树进一步提升性能。此外,HashMap还具备动态扩容机制,当负载因子超过预设值时自动扩大容量并重新哈希,确保整体性能。通过对HashMap底层结构的深入了解,我们可以更好地利用其优势解决实际开发中的问题。
101 0
|
3月前
|
存储 Java 程序员
"揭秘HashMap底层实现:从数组到链表,再到红黑树,掌握高效数据结构的秘密武器!"
【8月更文挑战第21天】HashMap是Java中重要的数据结构,采用数组+链表/红黑树实现,确保高效查询与更新。构造方法初始化数组,默认容量16,负载因子0.75触发扩容。`put`操作通过计算`hashCode`定位元素,利用链表或红黑树处理冲突。`get`和`remove`操作类似地定位并返回或移除元素。JDK 1.8优化了链表转红黑树机制,提升性能。理解这些原理能帮助我们更高效地应用HashMap。
45 0
|
3月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
45 0
|
4月前
|
存储
数组与链表有什么区别
数组与链表有什么区别
|
5月前
|
Java
数组链表(java)
数组链表(java)
|
5月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表