• Java数据结构和算法( 二 ) ## 数组


    目录

    • 数组综述
    • Java中的数组
      • 创建数组
      • 访问数组
      • 初始化数组
      • 有序数组
      • 线性查找
      • 二分查找
      • 有序数组的优缺点
    • 大O表示法(order of)
    • 为什么不用数据解决一切

    - 小结

    数组综述

        数组是最广泛的数据存储结构,其中还有一种特殊数组(有序数组),下面讲解下插入、查询、删除相关的注意事项

    • 插入: 新数据总是插在数组第一个空位上,由于已有数据项个数已知,所以空位的具体位置很容易得知,然而查询和删除却没有那么快。当不允许插入重复值的模式下,还需要将算法计算是否存在数据项。
    • 查找: 若是无重复项模式,必须平均搜索一半的数据项来查找特定数据项。找数组头部的数据项快,找数组尾部的数据项慢。若重复模式,必须从头查找到尾。
    • 删除: 只有找到特定数据项后才能进行删除操作(要么该数据项被替换,要么变为空)。删除算法中假设不允许有洞(一个或多个空单元后面存在非空数据项),如果删除算法允许有洞,则其他算法变得很复杂,因为要判断非空降低效率。因此非空数据项必须连续,不能有洞。在无重复项模式下查找平均N/2个数据项+移动剩下的N/2个数据项来补洞。总共是N步。
    • 重复值问题:
      • 重复项模式下查找算法: 重复项使查找算法复杂,匹配上一个后,还得考虑是否继续寻找可能的匹配,直到最后一个数据项(所有蓝眼睛的人和第一个蓝眼睛的人的区别)。通常操作需要N步。
      • 重复项模式下插入算法: 和不重复项模式一样,只需要一步。
      • 重复项模式下删除算法:若是删除第一个特定的数据项,操作步骤平均是N/2次查找+N/2移动,若是删除所有特定的数据项,需要检查N个数据项+移动多余N/2个数据项,操作的平均时间取决于重复项的个数和分布。

    表格区别重复与无重复模式的区别

    操作不允许重复允许重复
    查找 N/2次比较 N次比较
    插入 无比较、一次移动 一次移动
    删除 N/2次比较、N/2次移动 N次比较、多于N/2次移动

    Java中的数组

    创建数组

    int[] intArray = new int[]{};
    int[] intArray = new int[2];

    访问数组

    数组数据项通过下标访问,下标从0开始,止于长度减1。若下标不在这个范围访问数组,则抛出Array Index Out of Bounds的运行时错误。

    初始化数组

    int[] intArray = {1,2,3,4,5};
    int[] intArray = new int[]{1,2,3,4,5};

    代码示例

    github 地址

    public class HightArray {
        /** 声明一个数组 */ private long[] a;
    
        /** 声明变量记录数据项个数 */ private int nElemts;
    
        /** 构造方法用于初始化数组和数据项总数 */
        public HightArray(int max){ a = new long[max]; nElemts = 0; }
    
        public void insert(long value){ a[nElemts] = value; nElemts ++ ; }
    
        public boolean find(long searchKey){
            int j;
    
            for (j=0;j<nElemts;j++){ if(a[j]==searchKey){ break; } }
    
            return j == nElemts;
        }
    
        public boolean delete(long value){
            int j;
    
            for (j=0; j<nElemts; j++){ if(a[j]==value){ break; } }
    
            if(j == nElemts) { return false; }
            else {
                for (int k=j; k<nElemts; k++){ a[k] = a[k+1]; }
                nElemts--;
    
                return true;
            }
    
        }
    
        public void display(){                   System.out.println(Arrays.stream(a).mapToObj(String::valueOf).collect(Collectors.joining(" ")));
        }
    }

    测试类>>>

    public class HightArrayTest {
    
        @Test public void test(){
            int maxSize = 100;
    
            HightArray hightArray = new HightArray(maxSize){{
                insert(77); insert(99); insert(44); insert(55); insert(66);
                insert(22); insert(88); insert(11); insert(00); insert(33);
            }};
    
            hightArray.display();
    
            int searchKey = 35;
            System.out.println(hightArray.find(searchKey)?"Found ":"Can't find " + searchKey);
    
            hightArray.delete(00); hightArray.delete(55); hightArray.delete(99);
    
            hightArray.display();
    
        }
    }
    有序数组

        数组中的数据项按照关键字升序排列。当向此数组中插入数据项时,需要为插入操作找到正确位置,在稍小位置和稍大位置之间,然后将稍大位置至末尾的数据项往后移动一位腾出位置。
        这中顺序排列的好处就是可以通过二分查找显著提高查询速度,但是降低了插入的速度,毕竟要腾出坑位。

    线性查找

        线性查找就是依次向后,寻找匹配。而在有序数组中当匹配到一个合适的数据项就退出查找。

    二分查找

        二分查找就是一半一半的找,这种查询比线性查询快很多,尤其对大数组来说。

    有序数组代码示例

    github 地址

    public class OrderArray {
        private long[] a;
    
        private int nElems;
    
        public OrderArray(int maxSize){ a = new long[maxSize]; nElems = 0; }
    
        public int size(){ return nElems; }
    
        public void insert(long value){
            int j ;
            for (j = 0; j < nElems; j++) { if(a[j] > value) { break; } }
    
            for(int k=nElems; k>j; k--){ a[k] = a[k-1]; }
    
            a[j] = value;
    
            nElems++;
        }
    
        public int find(long searchKey){
            int lowerBound = 0, upperBound = nElems -1, curIndex;
    
            while (true){
                if(lowerBound > upperBound){ return nElems; }
    
                curIndex = (lowerBound + upperBound)/2;
                if(a[curIndex] == searchKey) { return curIndex; }
                else if (a[curIndex] < searchKey){ lowerBound = curIndex + 1; }
                else { upperBound = curIndex - 1; }
            }
        }
    
        public boolean delete(long value){
            int j = find(value);
            if(j == nElems) { return false; }
    
            for (int k = j; k < nElems; k++){ a[k] = a[k+1]; }
            nElems--;
            return true;
        }
    
        public void display(){
            System.out.println(Arrays.stream(a).mapToObj(String::valueOf).collect(Collectors.joining(" ")));
        }
    }

    测试类>>>

    public class OrderArrayTest {
        @Test public void test(){
            int maxSize = 100;
            OrderArray orderArray = new OrderArray(maxSize){{
                insert(77); insert(99); insert(44); insert(55); insert(22);
                insert(88); insert(11); insert(00); insert(66); insert(33);
            }};
    
            int searchKey = 55;
            System.out.println(orderArray.find(searchKey)!=orderArray.size()?"Found":"Can't find" + searchKey);
    
            orderArray.display();
    
            orderArray.delete(00); orderArray.delete(55); orderArray.delete(99);
    
            orderArray.display();
        }
    }
    有序数组的优缺点
    • 优点: 二分查找比无序数据快。
    • 缺点: 插入需要查找,查找后需要将靠后的数据项整体往后移动。删除操作都需要补洞。

    大O表示法(order of)

    算法运行时间
    线性查找(从头到尾) O(N)
    二分查找(一半一半) O(LogN)
    无序数组插入(仅一次) O(1)
    有序数组插入(查询+移动) O(N)
    无序数组删除(查询+补洞) O(N)
    有序数组删除(查询+补洞) O(N)

    为什么不用数据解决一切

        一个无序数组插入(O(1)时间),查询却花费(O(N)时间)。一个有序数组查询花费(O(LogN)时间),但是插入缺花费(O(N)时间)。而删除都需要花费(O(N)时间)。所以需要一种数据结构插入、查询、删除都很快,理论上(O(1)或者O(LogN)时间),而且数组一旦创建,其大小被固定,扩容不便,若空间大了又会浪费。

    小结

    • 无序数组可以快速插入,但查询和删除比较慢。
    • 有序数组可以使用二分法查找。
    • 线性查找需要的时间和数据项个数成正比。
    • 二分法查找需要的时间和数据项个数的对数成正比。
    • O(1)算法最好、O(LogN)次之、O(N)一般、O(N*N)最差。
  • 相关阅读:
    以太网数据帧最小64字节
    网络基础协议之ARP
    Windows FAT32转换NTFS
    Java面试红宝书(尼恩编著)
    死磕设计模式1:Builder (构建者模式)
    Zookeeper 分布式锁 (图解+秒懂+史上最全)
    TCP/IP协议 (图解+秒懂+史上最全)
    Java高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计模式
    Java高并发核心编程(卷1):NIO、Netty、Redis、ZooKeeper
    ThreadLocal(史上最全)
  • 原文地址:https://www.cnblogs.com/vision82/p/8423038.html
Copyright © 2020-2023  润新知