zoukankan      html  css  js  c++  java
  • 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)最差。
  • 相关阅读:
    PAT 1010. 一元多项式求导 (25)
    PAT 1009. 说反话 (20) JAVA
    PAT 1009. 说反话 (20)
    PAT 1007. 素数对猜想 (20)
    POJ 2752 Seek the Name, Seek the Fame KMP
    POJ 2406 Power Strings KMP
    ZOJ3811 Untrusted Patrol
    Codeforces Round #265 (Div. 2) 题解
    Topcoder SRM632 DIV2 解题报告
    Topcoder SRM631 DIV2 解题报告
  • 原文地址:https://www.cnblogs.com/vision82/p/8423038.html
Copyright © 2011-2022 走看看