• 数组与链表


    前言

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

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

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

    阅读前的准备工作

    大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. 总结

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

    喜欢我的博客就请点赞+关注一波

  • 相关阅读:
    在终端中输出彩色文字
    卸载PythonToolKit的方法
    几款python集成开发环境
    关于Python中数据对象的可变性
    解决vim无法返回上次的位置
    evince无法返回上次浏览的位置
    解决xfce4桌面图标消失的问题
    xfce4的截屏插件
    linux 查看chm文档的软件
    [MySQL]group by 与 if 的统计技巧
  • 原文地址:https://www.cnblogs.com/liuboren/p/11838437.html
Copyright © 2020-2023  润新知