精华内容
下载资源
问答
  • 在VBScript中,数组有两种类型:静态数组和动态数组。 1、静态数组 静态数组在编译时开辟内存区,数组大小在运行时不可改变。 定义一个一维数组mmArray(3) Dim mmArray(3) mmArray(0)=1 mmArray(1)=3 mmArray...

    数组是具有相同名字的一组变量,数组中包含多个元素,由不同的下标值区分数组的各个元素。在VBScript中,数组有两种类型:静态数组和动态数组。

    1、静态数组

    静态数组在编译时开辟内存区,数组大小在运行时不可改变。

    定义一个一维数组mmArray(3)

    Dim mmArray(3)
    mmArray(0)=1
    mmArray(1)=3
    mmArray(2)=5
    mmArray(3)=7

    其中mmArray是数组名,数组的下界为0,上界为3,数组元素从mmArray(0)到mmArray(3),共有4个元素。

    也可以这样定义:

    Dim MyArray
    MyArray=Array(1,3,5,7)

    n=10
    Dim MyArray(n) '这样定义数组错误
    定义静态数组,必须确定数组的上界,数组的上界不能为变量。因为静态数组是在编译时就开辟内存区的。

    2、动态数组

    动态数组是运行时大小可变的数组。当程序没运行时,动态数组不占内存,在程序运行时才开辟内存。

    动态数组的定义一般分两步:

    首先,用Dim语句声明一个括号内不包含下标的数组。
    然后,在使用数组之前用ReDim语句根据实际需要重新定义下标值。

    也可以用Redim语句直接定义数组。例如

    ReDim a(2)
    a(0)=1
    a(1)=2
    a(2)=3

    ReDim语句的格式为:ReDim [Preserve] 变量(下标)

    例如定义动态数组arrOrg
    Dim arrOrg()
    ReDim arrOrg(3)'在使用arrOrg之前,必须用ReDim语句分配实际的元素个数

    可以用ReDim不断改变元素数目
    Dim ArrOrg()
    ReDim arrOrg(3)
    ReDim arrOrg(5)
    ReDim arrOrg(2)

    每次执行ReDim语句时,存储在数组中的当前值都会全部丢失。如果希望改变数组大小而又不丢失数组中的数据,要使用关键字Preserve。

    <%
    ReDim a(2)
    a(0)=1
    a(1)=2
    a(2)=3
    ReDim Preserve a(1)
    a(1)=9
    For n=0 To UBound(a)
      Response.Write a(n) & "<br>"
    Next
    %>

    ReDim Preserve arrOrg(Ubound(arrOrg)+1)
    以上代码将数组扩大一个元素,现有元素值不变。Ubound()函数返回数组的上界。

    注意:在用ReDim语句重新定义数组时,只能改变数组元素的个数,而不能改变数组的维数。

    3、为什么要使用动态数组?

    当程序没运行时,动态数组不占内存,在程序运行时才开辟内存。
    动态数组是运行时大小可变的数组。
    使用动态数组的优点是可以根据用户需要,有效利用存储空间。

    如果你将某数据存入数组中,在不确定有多少个的情况下,使用动态数组将是非常好的办法。
    比如,有1亿个数据。其中仅需100个符合要求的数据放到数组中。选用动态数组的方法将是明智的。

    下例将1000内被54整除的数放到某数组中。

    Dim MyArray()'声明一个括号内不包含下标的数组。
    Dim i,j
    j=0
    For i=0 To 1000
      IF i Mod 54=0 Then
        ReDim Preserve MyArray(j)
        MyArray(j)=i
        j=j+1
      End IF
    Next
    '输出
    Response.Write "1000内被54整除的数共有:" & Ubound(MyArray)+1 & "个,分别是:<p>"
    For i=0 to Ubound(MyArray)
      Response.Write MyArray(i) & "<br>"
    Next

    这个例子说明,你不确定数组元素个数,但如果你直接定义Dim MyArray(1000),是极其愚蠢的。
    一是这样定义将浪费存储空间。本例中实际需要的数组元素为19个
    二是这样的代码执行效率低,造成资源浪费。比如对这元素重新排序。直接定义Dim MyArray(1000),将要遍历1000次。

     可扩展数组,程序代码如下

    Dim MyArray()
    For i=0 To 10
      ReDim Preserve MyArray(i)
      MyArray(i)=i
    Next

     将一个字符串分割并返回分割结果的数组,程序代码如下

    Dim tmpStr,MyArray
    tmpStr="1,3,5,7,9"
    MyArray=Split(tmpStr,",")
    For N=0 to Ubound(MyArray)
      Response.Write MyArray(N) & "<br>"
    Next


    在Application和Session中使用数组(Session使用方法与Application相同)

    以Application为例,将数组放到全局缓存中。程序代码如下

    Application.Lock
    Application("appArray")=MyArray
    Application.Unlock

    更改全局缓存数组时,必须先将全局缓存数组赋给变量,更改该变量某一元素值,然后再将该变量放到全局缓存中。而不能直接修改全局缓存某一元素而改变该元素的值。请分析以下例子:

    <%
    Dim MyArray,tmpArr
    MyArray=array(1,2,3)
    Application.Lock
    Application("appArray")=MyArray
    Application.Unlock
    Response.Write Application("appArray")(1) & "<br>"
    '以下方法是错误的
    Application.Lock
    Application("appArray")(1)=9
    Application.Unlock
    Response.Write Application("appArray")(1) & "<br>"
    '以下方法是正确的
    tmpArr=Application("appArray")
    tmpArr(1)=9
    Application.Lock
    Application("appArray")=tmpArr
    Application.Unlock
    Response.Write Application("appArray")(1) & "<br>"
    %>
    结果是:
    2
    2
    9

    以下为实例分析:

    05年给网吧写的一个“万象2003数据库操作”的程序,其中涉及到下面一个功能:
    开卡上机操作:双击列表中的一台机器,在弹出的窗口中,输入押金(金额),点击确定。网吧中这样的开卡上机是很频繁的,那么我需要查看最近10条开卡记录。这段代码我是用缓存数组来解决的,代码如下:

    下面这段代码是开卡后将数据写入缓存,为了描述方便,这里只记录卡号,将机器编号,金额,标准费率,开卡时间等省略掉

    为了便于演示,卡号sCardID由系统生成

    将以下代码保存为app.asp文件并执行,用户每次刷新页面(刷新间隔时间至少间隔一秒以上为宜)即可看到效果。

    <%
    'sCardID为当前卡号,这里就用现行时间代替
    Dim i,k,sCardID
    sCardID=Now
    Dim CardLogArr(9)
    IF IsArray(Application("CardLogArr")) Then
      For i=1 to 9
        CardLogArr(i)=Array(Application("CardLogArr")(i-1)(0)+1,Application("CardLogArr")(i-1)(1))
      next
      CardLogArr(0)=Array(1,sCardID)
    Else
      CardLogArr(0)=Array(1,sCardID)
      For k=1 to 9
        CardLogArr(k)=Array(2,"-")
      Next
    End IF
    Application.Lock
    Application("CardLogArr")=CardLogArr
    Application.UnLock
    %>
    <!-- ######以下为显示部分###### -->
    <table width=370 border=0>
      <tr bgcolor="#FAFAFA">
        <td width="15%" height="20" align=center>序号</td>
        <td width="20%">卡号</td>
      </tr>
      <%
      IF IsArray(Application("CardLogArr")) Then
        For i = 0 To 9
          Response.Write  "<tr>" & _
          "<td height=20 align=center>" & Application("CardLogArr")(i)(0) & "</td>" & _
          "<td>" & Application("CardLogArr")(i)(1) & "</td>" & _
          "</tr>"
        Next
      End IF
      %>
    </table>

    有用的数组函数

    Ubound(arrayName)函数,这个函数是返回数组的下标,也就是数组最后一个元素的标记。
    Lbound(arrayName)函数,这个函数是返回数组的上标,也就是数组第一个元素的标记。
    Split(string,splitby)函数,返回基于0的一维数组,其中包含指定数目的子字符串。string是一个字符串,sqlitby是分隔符

    进一步分析实例

    如果说缓存是一种技术的话,那么数组就是一种算法。利用好数组,某些看似复杂的问题便迎刃而解。

    例1:
    <%
    Dim CardLogArr(9),i
    IF IsArray(Application("CardLogArr")) Then
      For i = 1 To 9
        CardLogArr(i) = Array(Application("CardLogArr")(i-1)(0) 1,Application("CardLogArr")(i-1)(1),Application("CardLogArr")(i-1)(2),Application("CardLogArr")(i-1)(3),Application("CardLogArr")(i-1)(4))
      Next
      CardLogArr(0) = Array(1,sCardID,Pwd,strMoney,UserName)
    Else
      CardLogArr(0) = Array(1,sCardID,Pwd,strMoney,UserName)
      For i = 1 To 9
        CardLogArr(i) = Array(2,"-","-","-","-")
      Next
    End IF
    Application.Lock
    Application("CardLogArr") = CardLogArr
    Application.UnLock
    %>


    分析:
    先定义变量,此时程序的目的只需要10个数据,故定义数组元素10个的固定数组。
    然后判断Application("CardLogArr")是否为数组,第一次执行,Application("CardLogArr")为假,执行IF语句中的Else语句。
    这时,将一数组赋给CardLogArr数组的第一个元素。即CardLogArr(0)。注意,此时CardLogArr(0)为数组CardLogArr的一个元素。而这个元素也是一个数组。
    下面循环九次类似,目的是填充数据。OK,第一次执行后,此时的Application("CardLogArr")就是一个数组了。只要Application("CardLogArr")不丢失,以后执行IF语句中的Then语句。
    那么Then语句中循环9次又是为什么呢?请看程序的功能就能清楚。
    当10个数据填充满时,第11个数据该放哪?最终目的是,第11个数据应放在第一个。那么原第一个数据,就放到第二个,依此类推。当然原第10个数据就得丢掉。
    现在,明白Then语句的功能了吗?

    Ubound函数
    功能:返回指定数组维数的最大可用下标。
    语法:UBound(ArrayName[, dimension])
    参数
    ArrayName 必选项。数组变量名,遵循标准变量命名约定。
    Dimension 可选项。指定返回哪一维上界的整数。1 表示第一维,2 表示第二维,以此类推。如果省略 dimension 参数,则默认值为 1。

    说明
    UBound 函数与 LBound 函数一起使用,用于确定数组的大小。使用 LBound 函数可以确定数组某一维的下界。所有维的下界均为 0。
    举例:
    Dim a(10)
    Ubound(a) = 10

    Dim A(100,3,4)
    UBound(A,1) 100
    UBound(A,2) 3
    UBound(A,3) 4

    Dim a(10)
    a(0) = Array(0,1,2,3,4,5,6,7,8,9)

    LBound 函数
    功能:返回指定数组维的最小可用下标。
    语法:LBound(ArrayName[, Dimension])

    参数
    ArrayName 数组变量名,遵循标准变量命名约定。
    Dimension 指明要返回哪一维下界的整数。使用 1 表示第一维,2 表示第二维,以此类推。如果省略 dimension 参数,默认值为 1。

    说明
    LBound 函数与 UBound 函数共同使用以确定数组的大小。使用 UBound 函数可以找到数组某一维的上界。任一维的下界都是 0。


    ReDim 语句
    在过程级中声明动态数组变量并分配或重新分配存储空间。

    ReDim [Preserve] varname(subscripts) [, varname(subscripts)] . . .

    参数
    Preserve 当更改现有数组最后一维的大小时保留数据。
    varname 变量名,遵循标准变量命名约定。
    subscripts 数组变量的维数,最多可以声明 60 维数组。subscripts 参数语法格式如下:
    upper [,upper] . . .
    数组的下界总是零。

    说明
    ReDim 语句通常用于指定或修改动态数组的大小,这些数组已用带有空括号的 Private、Public 或 Dim 语句(没有维数下标)正式声明过。可以重复使用 ReDim 语句更改数组维数和元素数目。
    如果使用了 Preserve 关键字,就只能调整数组最后维的大小,并且不能改变数组的维数。
    比如定义一个一维数组Dim a()
    ReDim Preserve a(19)是可以的
    Redim Preserve a(20)也是可以的
    但Redim Preserve a(10)(4)这样是不行的。不能改变数组的维数。

    例如,如果数组只有一维,就可以修改该数组的大小,因为该维是最后的也是仅有的一维。但是,如果数组有两个或更多维,就只能改变末维的大小并保留数组内容。

    这个例子说明如何不擦掉该数组中存在的数据,而增加动态数组的终止维数。

    ReDim X(10, 10, 10)
    ...
    ReDim Preserve X(10, 10, 15) ... ok
    ReDim Preserve X(10, 15, 15) ... error
    小心 如果减小数组的大小,则将丢失被排除的元素中的数据。
    变量初始化时,数值变量初始化为 0,字符串变量初始化为零长度字符串 ("")。在使用引用对象的变量前,必须使用 Set 语句将某个现有对象赋予该变量。在进行对象赋值以前,已声明的对象变量有特定值 Nothing。

    开发“万象2003 WEB版”原因

    04年,网吧还没有强制要求使用pubwin软件,一般网吧业主都是使用“万象2003”,我家也不例外。但后来,文化局要求网吧刷卡上网,并在检查时要检查刷卡记录。要知道一般网吧,如果按国家政策行事的话,估计连卖白菜的都不如了。这中间的一些情况在这就不说了,开过网吧的,或者懂得社会一些规则的,大家都懂。比如,文化局规定早上8点到晚上12点之间营业,这时间之外是不允许营业的,实际过程中,难免严格遵守这样的规定,这是一。其实,有的人由于各种原因没带身份证的,也就无法刷卡,但你网吧做生意不能赶他们走呀,如果所有网吧都一样也无话可说,但正是由于一些潜规则,有的网吧照收不误,所以,没带身份证的这批客户上网的问题要解决,这是其二。更重要的是,父母年龄偏大,软件操作对他们来说实在是麻烦,而且易忘。为了解决这个问题,才使我用WEB开发了这款应用程序。功能如下:

    1、双击某台机器,填写金额,确定后,自动生成卡号,用户记住该卡号,即可在客户机登陆上网。上机非常方便。

    2、用户下机时,双击电脑对应的机器号,即可结帐,并提示需要支付多少钱。下机非常方便。

    3、可模拟生成刷卡信息,再也不用担心用卡号刷卡上网了。因为我是直接操作“万象2003”的数据库,绕过刷卡程序。

    4、程序精确生成早上8点到晚上12点这之间的信息,并可精确定控制金额范围。(要知道,如果你网吧生意好,人家可会有想法的~~)

    5、支持换机操作

    6、支持中途加钱操作、商品销售管理。

    7、可显示最近10人上机的机器号、卡号、金额等信息。因为有的用户上机前卡号丢失或忘记之类,网吧老板可以随时查看并告诉他们帐号。

    8、设定多少分钟前提醒用户余额不足,语音提示并红色醒目显示。

    基本上“万象2003”的实际功能,WEB版的都能操作,而且尽最大方便父母操作。

    “万象2003数据库操作”源码下载:稍后上传

    展开全文
  • 详解java静态数组

    2021-08-13 13:54:39
    数组是一种线性表 顾名思义,数组就是数据组合在一起,是一个存储数据容器。其一般定义: 数组是具有相同数据类型元素的有序集合。 从定义可知 所有元素必须是相同数据类型。 那么可以推导数组也是一种数据类型,且...

    数组是一种线性表

    顾名思义,数组就是数据组合存放在一起,是一种存储数据容器。其一般定义: 数组是具有相同数据类型元素的有序集合。

    从定义可知

    • 所有元素必须是相同数据类型
      由此可推导数组也可认为是一种数据类型,且它的类型由其元素的数据类型决定。其中,数据类型刻画操作对象的特性,是一个值的集合和该值集上的一组操作的总称,即 数据类型 = 数据值域 + 数据操作。
    • 数组中元素是有序的
      有序指数据元素之间的关系,即除首尾元素,其他元素都有且只有一个前驱元素、后继元素。

    ​元素有序说明数组是一种线性表。在逻辑内存上,数组不管有多大,各个元素都是连续的。那么很自然地认为,在物理存储层面可以使用顺序存储结构存储数组的元素和元素之间的关系,但又由于Windows操作系统的物理内存按照4KB分页,只能保证数组在4KB内绝对是连续的,超过4KB则无法确定。

    ​ 以上内容站在抽象数据类型(Abstract Data Type,ADT)的角度描述数组。实际上各类编程语言都把数组设为固有类型,有其具体实现方式。本文以下内容以java语言为例,介绍java数组的各类特性。

    Java数组

    ​ 在java中,数组用来存储固定数量的同类型元素,且用一个标识符名称把对象序列或基本数据类型序列封装到一起。数组也是一种对象,除继承Object类的相关属性外,数组对象有一个固有成员length,表示数组的最大容量,注意不是已有数组大小。

     int[] a = new int[10];
     a[1] = 1;
     System.out.println(a.length); //输出10,不是1.
    

    ​ 数组内存模型同一般java对象基本一致,在栈空间中存储数组的引用,在堆空间中存储数组对象,且栈中引用指向堆中对象。但由于数组存储多个元素,其存储模型稍有不同。

    对于一般java对象,栈空间栈帧中对象引用指向堆内存中的对象地址。如下图。

    图1 java一般对象存储模型
    当数组中存储基本数据类型时,栈空间中的数组引用指向堆空间中的第一个数组元素地址,且堆空间中直接存储的数组元素值。如下图。
    图2 基本数据类型数组的存储模型

    当数组中存储其他对象时,栈空间中的数组引用也指向堆空间中的第一个数组元素地址,但堆空间中存储的是对象的引用。如下图。

    图3 对象数组的存储模型

    特点

    • 可以存储基本数据类型
      其他集合只能存储对象,不能存储基本数据类型。但自动拆箱、装箱技术的出现,使其他集合也可以存储基本数据类型。

    • 数组一旦声明,其大小不能发生改变
      静态数组称呼的由来。
      该特点导致在实际编程中基本不使用数组,而是其他动态容器(ArrayList等)

    • 数组元素逻辑删除
      数组初始化后,数组元素只能在逻辑层面删除。在物理层面,其值已写入内存,只能被覆盖,不能被物理删除。在java体系中,若数组存储对象时,数组元素被逻辑删除之后,物理地址上的值依旧存在,此时GC无法将其回收。因此,此种情况下删除数组元素对象的同时,可将其置空,使其被GC回收。

    • 使用整型索引随机访问元素,下标从0开始
      随机访问的实现基于数组在内存中的连续存储。数组名指向数组元素的起始位置地址,索引下标是相对于起始地址的偏移量,操作系统在知道起始地址和偏移量之后,可立马获取对应地址上的内容,即所谓的随机访问。这同时也导致数组是存取效率最高的数据容器。但为保证数组各个元素内存仍旧连续,因此在插入和删除时需要移动元素(实质是重新赋值,全部赋值动作整体来看是一种移动效果),其操作效率比较耗时。

      数组内存层面的最基本操作是按照索引存取元素、获取数组长度。使用层面的基本操作基于内存上的基本操作,包括增删改查、排序等

    数组声明

    java数组定义多采用如下格式

    int[] arr;
    

    以上语句只是定义了一个数组引用,即只在栈中分配一个栈帧存储该引用,并未指向堆空间中的数组对象。之所以允许未创建数组对象就可定义一个数组引用,是因为java支持将一个数组赋值给另外一个数组。

    数组初始化

    定义数组之后,要进行数组初始化,其实质是对数组引用赋值(将数组引用指向堆空间中的具体地址)。根据数组引用赋值的时机不同,可分为静态方式和动态方式。

    • 静态初始化指在数组声明的同时,直接给数组引用赋值

      //右侧中括号不能写长度,有JVM自动计算
      int[] arr1 = int[]{1,2,3};
      
      //上一种方式的省略方式。
      int[] arr2 = {1,2,3};
      //省略方式不允许先声明后赋值,只能声明同时直接赋值。
      int[] arr3;
      arr3 = {} //编译出错
      
    • 动态初始化指数组声明和数组引用赋值分开。根据数组定义方式的不同,又分为两种。

      //这种数组定义方式,只能用数组引用赋值方式。
      int[] arr1;
      ... //其他代码 
      //其他代码段中存在同类型的数组arr2
      arr1 = arr2 
      
      //这种数组定义方式,可有数组引用赋值、数组元素下标赋值 两种赋值方式
      int[] arr1 = int[10];
      ...//其他代码段
      //数组引用赋值
      //其他代码段中存在同类型的数组arr2
      arr1 = arr2 
          
      //数组元素赋值
      arr1[1] = 1;
      arr1[2] = 2;
      

    二维数组

    二维数组实质上是一维数组的元素存储数组地址,这个地址指向其他一维数组。
    定义方式如下

    T[][] arr = new T[a][b]
    

    其中,a表示一维数组的数量,b表示每个一维数组元素的个数。

    Arrays工具类

    该类在java.util.Arrays下,提供对数组的排序、比较、复制等功能。

    延伸

    • 数组被赋值之后,在内存层面,元素已被写入相应地址的内存空间,无法被删除,只能通过覆盖修改其值。因此当数组的元素是对象时,当逻辑删除元素之后,可手动将对应位置的元素置空,使其被GC回收。

          //在不是泛型的情况下,不需要手动置空。
          //在泛型情况下,由于泛型中装载的是对象,当删除数组元素时,
          //尾部的size指针左移一位后,指向的是一个无用的对象【loitering objects】。
          //但这个无用的对象不代表内存泄露,因为数组只要再被使用,这个对象就可能被覆盖。
          //不过,若数组不再被使用,这个对象就不会被释放,无法被GC。
          //因此,手动置空可让GC回收这个对象,以提高性能。
          //置空与否不影响删除逻辑。
          data[size] = null;
      
    • 在leetcode中,数组涉及的解体方法有二分法、双指针法、滑动窗口法、模拟行为等。

    • java泛型不可以装入基本数据类型(四类八种),基本数据类型需要装箱之后使用泛型。与泛型机制相关。

      List<Integer> foo = new ArrayList<Integer>(); //正确
      List<int> bar = new ArrayList<int>();//错误
      
    • O(n)是渐进时间复杂度,是描述n趋近于无穷的情况.对于

      T1 = 2000*N + 1  O(n)
      
      T2=1* n * n + 1 O(n2)
      

      ​ 当n较小时,T1 > T2 .当n无穷大时,T2 > T1。渐进时间复杂度讨论的时n无穷大的情况,因此认为 T2的时间复杂度高于T1,即认为T1的性能更好,优先选择T1对应的算法。但是这种曲线有交叉 [最高次幂的系数有影响] 的情况,可以根据实际数量情况,灵活选择不同算法。当数据量小时选T2(此时最高次幂的系数对运算时间影响较大,2000>1),数据量大时(此时元操作的阶数对运算时间影响较大)选T1.

    • 均摊复杂度的场景 若干次基本操作后发生一次复杂操作,那么复杂操作就可以被均摊

    • 在程序设计中,基本数据类型和引用数据类型的内存存储方式是不同的,引用类型,通过new关键字创建,故引用对象会存放在堆区内存中,对于特别小的基本数据类型,往往效率不高,因此对于基本数据类型,不用new来创建,而是采取了和C/C++相同的方法,直接赋值存储,放置在内存的栈区内存,更加高效。

    参考资料

    展开全文
  • 静态数组与动态数组

    2014-06-21 10:30:34
    摘要的重要性是不言而喻的,每次发文章我都很纠结如何写出一个有特色的摘要来,能够以最为简短的文字向读者描述出我所要表达的东西。但是常常出现的问题是,摘要写得太简短了,读者看了不清楚文章究竟要讲啥;摘要写...

    摘要的重要性是不言而喻的,每次发文章我都很纠结如何写出一个有特色的摘要来,能够以最为简短的文字向读者描述出我所要表达的东西。但是常常出现的问题是,摘要写得太简短了,读者看了不清楚文章究竟要讲啥;摘要写得稍微长点的话自然能够描述清楚所要表达的东西,但是却也出现了另外一个问题,就是读者看到大段的文字描述,觉得枯燥无味,直接二话不说给文章判了个“死刑”,导致这种情况下愿意真正的花时间看完摘要的读者屈指可数,更不用说文章的正文部分了,所以时长感慨写文章最头疼的莫过于摘要了。

    很多人在编写C语言代码的时候很少使用动态数组,不管什么情况下通通使用静态数组的方法来解决,在当初学习C语言的时候我就是一个典型的例子,但是现在发现这是一个相当不好的习惯,甚至可能导致编写的程序出现一些致命的错误。尤其对于搞嵌入式的人来所,嵌入式系统的内存是宝贵的,内存是否高效率的使用往往意味着嵌入式设备是否高质量和高性能,所以高效的使用内存对我们来说是很重要的。那么我们在自己编写C语言代码的时候就应该学会使用动态数组,这也就是我这篇博客要给大家讲的,我尽我所能的用一些简单的代码来讲解动态数组,希望我所讲的对你有所帮助。

    那么我们首先来看看什么是动态数组,动态数组是相对于静态数组而言,从“动”字我们也可以看出它的灵活性,静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。动态数组的内存空间是从堆动态分配的。是通过执行代码而为其分配存储空间。当程序执行到我们编写的分配语句时,才为其分配。对于静态数组,其创建非常方便,使用完也无需释放,要引用也简单,但是创建后无法改变其大小是其致命弱点!对于动态数组,其创建麻烦,使用完必须由程序员自己释放,否则将会引起内存泄露。但其使用非常灵活,能根据程序需要动态分配大小。所以相对于静态数组的来说我们对于使用动态数组有很大的自由度。

    在创建动态数组的过程中我们要遵循一个原则,那就是在创建的时候从外层往里层,逐层创建;而释放的时候从里层往外层,逐层释放。这个话你读了可能理解并不深刻,不过不要急,接下来我们看看两段代码。

    一维动态数组的创建:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    int n1,i;
    int *array;
    printf("请输入所要创建的一维动态数组的长度:");
    scanf("%d",&n1);
    array=(int*)calloc(n1,sizeof(int));
    for(i=0;i<n1;i++)
    {
     printf("%d\t",array[i]);
    }
    printf("\n");
    for(i=0;i<n1;i++)
    {
     array[i]=i+1;
     printf("%d\t",array[i]);
    }
     free(array);//释放第一维指针 
    return 0;
    }

    运行结果为:

    特此说明:在以后的运行结果部分,我均会附上文字结果,以防图片打开失败。

    请输入所要创建的一维动态数组的长度:4
    0       0       0       0
    1       2       3       4       Press any key to continue

    在此我使用的是calloc()函数来分配的,同时也使用两个for语句来打印数组元素,我们发现第一个打印输出的数组元素值均为0,在此也是为了加深读者对于calloc()函数的印象我特地使用了它来分配,如果对于calloc()、malloc()、realloc()函数的区别还是很清楚的读者可以去看看我的另外一篇博客------C语言的那些小秘密之内存分配。

    二维数组的创建:

    #include <stdio.h> 
    #include <stdlib.h> 
    int main() 

    int n1,n2; 
    int **array,i,j; 
    printf("请输入所要创建的动态数组的第一维长度:");
    scanf("%d",&n1);
    printf("请输入所要创建的动态数组的第二维长度:");
    scanf("%d",&n2); 
    array=(int**)malloc(n1*sizeof(int*)); //第一维 
    for(i=0;i<n1; i++) 

    array[i]=(int*)malloc(n2* sizeof(int));//第二维 
    }
    for(i=0;i<n1;i++)
    {
    for(j=0;j<n2;j++) 

    array[i][j]=i*n2+j+1; 
    printf("%d\t",array[i][j]); 

    printf("\n");
    }
    for(i=0;i<n1;i++) 

    free(array[i]);//释放第二维指针 

    free(array);//释放第一维指针 
    return 0; 
    }

    运行结果为:

     请输入所要创建的动态数组的第一维长度:3
    请输入所要创建的动态数组的第二维长度:3
    1       2       3
    4       5       6
    7       8       9
    Press any key to continue

    有了上面的代码我们再来说动态数组的建立就简单了,以二维为例,先说创建,还记得我们上面说的创建的原则嘛:从外层往里层,逐层创建。

    array=(int**)malloc(n1*sizeof(int*)); //第一维

    以上是我们创建二维动态数组的最外层,创建好了最外层那么我们接下来就是要创建次外层了。

    array[i]=(int*)malloc(n2* sizeof(int));//第二维

    在创建次外层的过程中我们使用了一个for喜欢语句,千万别忘了使用for循环语句,这是绝大多数人的一个易错点。

    创建好了接下来我们该讲到释放了,而释放的时候从里层往外层,逐层释放。刚刚与我们上面的创建相反,在以上代码中我们首先使用了下面一个for循环来释放里层。

    for(i=0;i<n1;i++) 

    free(array[i]);//释放第二维指针 
    }

    在通过以下语句来释放外层。
    free(array);//释放第一维指针

    如果出现多维的情况怎么做呢,我们接下来再来看看一个三维动态数组的创建和释放,以加深下读者的印象。代码如下:

    #include <stdlib.h> 
    #include <stdio.h> 
    int main() 

    int n1,n2,n3; 
    int ***array; 
    int i,j,k; 
    printf("请输入所要创建的动态数组的第一维长度:");
    scanf("%d",&n1); 
    printf("请输入所要创建的动态数组的第二维长度:");
    scanf("%d",&n2); 
    printf("请输入所要创建的动态数组的第三维长度:");
    scanf("%d",&n3); 
    array=(int***)malloc(n1*sizeof(int**));//第一维 
    for(i=0; i<n1; i++) 

    array[i]=(int**)malloc(n2*sizeof(int*)); //第二维 
    for(j=0;j<n2;j++) 

    array[i][j]=(int*)malloc(n3*sizeof(int)); //第三维 


    for(i=0;i<n1;i++)
    {
    for(j=0;j<n2;j++)
    {
    for(k=0;k<n3;k++) 

    array[i][j][k]=i+j+k+1; 
    printf("%d\t",array[i][j][k]); 

    printf("\n");
    }
    printf("\n");
    }
    for(i=0;i<n1;i++) 

    for(j=0;j<n2;j++) 

    free(array[i][j]);//释放第三维指针 


    for(i=0;i<n1;i++) 

    free(array[i]);//释放第二维指针 

    free(array);//释放第一维指针 
    return 0; 
    }

    运行结果为:


    请输入所要创建的动态数组的第一维长度:3
    请输入所要创建的动态数组的第二维长度:3
    请输入所要创建的动态数组的第三维长度:3
    1       2       3
    2       3       4
    3       4       5

    2       3       4
    3       4       5
    4       5       6

    3       4       5
    4       5       6
    5       6       7

    Press any key to continue

    看了以上三维动态数组的创建和释放代码以后,我想读者这个时候已经可以自己编写任意维的动态数组了。但是细心的读者可能发现了一个问题,那就是我们所讲的动态数组都是一次性创建好的,如果接下来在使用的过程中我们使用的数组需要扩展或者删减一些不再使用元素该怎么办呢?!接下来我们先看一段关于动态数组扩展的代码,在此以一维动态数组的扩展为例,其它的以此类推。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    int*n,*p;
    int i,n1,n2;
    printf("请输入所要创建的动态数组的长度:");
    scanf("%d",&n1); 
    n=(int*)calloc(n1,sizeof(int));
    printf("请输入所要扩展的动态数组的长度:");
    scanf("%d",&n2); 
    p=(int*)realloc(n,(n2)*sizeof(int));//动态扩充数组
    for(i=0;i<n2;i++)
    {
    p[i]=i+1;
    if(i%5==0)
    printf("\n");
    printf("%d\t",p[i]);
    }
    free(p);
    return 0;
    }

    运行结果如下:

    请输入所要创建的动态数组的长度:6
    请输入所要扩展的动态数组的长度:25

    1       2       3       4       5
    6       7       8       9       10
    11      12      13      14      15
    16      17      18      19      20
    21      22      23      24      25      Press any key to continue

    看了上面的代码读者应该知道如何来扩展动态数组了,可能有的读者对于realloc()函数的使用有些陌生,如果有什么疑惑的话可以参考我之前写的一篇博文------C语言的那些小秘密之内存分配,在此我就不再做过多的讲解了。

    接下来如何缩小动态数组。

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    int*n,*p;
    int i,n1,n2;
    printf("请输入所要创建的动态数组的长度:");
    scanf("%d",&n1); 
    n=(int*)calloc(n1,sizeof(int));
    for(i=0;i<n1;i++)
    {
    n[i]=i+1;
    if(i%5==0)
    printf("\n");
    printf("%d\t",n[i]);
    }
    printf("\n请输入所要缩小的动态数组的长度:");
    scanf("%d",&n2); 
    p=(int*)realloc(n,(n2)*sizeof(int));
    for(i=0;i<n2;i++)
    {
    if(i%5==0)
    printf("\n");
    printf("%d\t",p[i]);
    }
    printf("\n");
    free(p);
    return 0;
    }

    运行结果为:

    请输入所要创建的动态数组的长度:25

    1       2       3       4       5
    6       7       8       9       10
    11      12      13      14      15
    16      17      18      19      20
    21      22      23      24      25
    请输入所要缩小的动态数组的长度:15

    1       2       3       4       5
    6       7       8       9       10
    11      12      13      14      15
    Press any key to continue

    在这里值得注意的一点就是在缩减动态数组的时候,它是删除了后面的元素,而前面的元素保持不变。在使用realloc()函数的时候要由其注意它的使用规则。

    讲到这儿就到了该说结束的时候了,由于本人水平有限,博客中的不妥或错误之处在所难免,殷切希望读者批评指正。同时也欢迎读者共同探讨相关的内容,如果乐意交流的话请留下你宝贵的意见。

    展开全文
  • 我们将要使用的是JAVA中的静态数组——E[] data = new E[]();去实现一个属于我们自己的动态数组 第一步:(关于泛型不作过多解释) 1 public class Array<E> { 2 private E[] data; 3 private int ...

    (希望我所描述的一切,给你带来收获!)

    我们将要使用的是JAVA中的静态数组——E[] data = new E[]();去实现一个属于我们自己的动态数组

     

    第一步:(关于泛型不作过多解释)

    1 public class Array<E> {
    2     private E[] data;
    3     private int size;
    4 }

    data是我们需要的数组,size是我们需要维护的动态数组的长度;size可以认为是数组的索引号,size总是指向数据的末端——当整个数组为空时size指向的是 data[0] 的位置,当数组只有 data[0] 一个元素时,size指向的数据末端是 data[1];也就是说,size总是指向用户数据(用户添加的数据)的后一个位置。

     

    第二步:

    1 /**
    2      *
    3      * @param capacity
    4      */
    5     public Array(int capacity) {
    6         data = (E[]) new Object[capacity];
    7     }

    我们申请一个容量为capacity的数组,注意!java中不能直接对泛型进行new的操作,但Object是所有类的父类,通过new一个Object再进行类型强转是行得通的!但是,有时我们并不确定我们所需要多少大小的capacity,为了方便,我们还需要一个无参构造函数,初始化一个默认的capacity数组(默认的capacity我给的是10)

    1 /**
    2      * the default capacity is 10
    3      */
    4     public Array() {
    5         this(10);
    6     }

     

    第三步:实现一些简单的操作,例如:获取当前数组的长度、判断数组是否为空......

     1 /**
     2      *@return the current data length of the array
     3      */
     4     public int getSize() {
     5         return size;
     6     }
     7 
     8     /**
     9      * Determines whether the array is empty
    10      * @return  false or true
    11      */
    12     public boolean isEmpty() {
    13         return size == 0;
    14     }
    15 
    16     /**
    17      * get the Array capacity
    18      * @return  Array capacity
    19      */
    20     public int capacity() {
    21         return data.length;
    22     }

     我们一定要明确的一点是:用户只能看到size长度的data(这是外界给予可能是有价值意义的数据),而data.length则只是能承载多少用户数据的边界(限度)

     

     第四步:为我们的Array<E>实现增加元素的操作add(int index,E e)方法

     1 /**
     2      * Insert an element on an index
     3      * @param index
     4      * @param e
     5      */
     6     public void add(int index,E e) {
     7         if (index < 0 || index > size)
     8             throw new IllegalArgumentException("Add failed.Array is full");
     9         for (int i = size - 1; i >= index; i--) {
    10             data[i+1] = data[i];
    11         }
    12         data[index] = e;
    13         size++;
    14     }

     需要注意的是,用户指定位置插入元素,index值一定要具有合法性——即index属于[0,size] 

     循环体中,data[i+1] = data[i]的语义是明显的——前一个元素向后一个元素移动。最初的移动在Array的尾部,即是 size - 1 的位置

     

    第五步:移除Array中的所有的元素e

    我们先理清思路:1、移除元素e我们需要先获得元素e所在的index

            2、通过目标index,我们只需要进行 data[index] = data[ index + 1 ]——拿目标元素所在位置的下一个位置上的元素去覆盖目标元素

            3、覆盖操作肯定是在循环中进行的,因为不一定只有一个元素e,很可能有相连的e,这时,当第一次覆盖操作结束之后,我们应该还需要再考察覆盖之后的index是否还是e

     1 /**
     2      * remove all "e" from the array
     3      * @param e
     4      */
     5     public void removeElements(E e) {
     6         for (int i = 0; i < size; i++) {
     7             if (data[i].equals(e)) {
     8                 remove(i);
     9                 i--;
    10             }
    11         }
    12     }

     removeElements(E e)复用了remove(int index)

     1 /**
     2      * remove elements from index positions
     3      * @param index
     4      * @return
     5      */
     6     public E remove(int index) {
     7         if (index < 0 || index >= size)
     8             throw  new IllegalArgumentException("remove failed,Index is illegal");
     9         E ret = data[index];
    10         for (int i = index + 1; i < size; i++) {
    11             data[i - 1] = data[i];
    12         }
    13         size--;
    14         return ret;
    15     }

     关于removeElements(E e)其实还有其他方法去实现,在这里小编就再列个方法:我们可以构建一个removeElement(E e)方法,这个方法每运行一次就移除Array中首个元素e,那么我们可以在removeElements(E e)中复用此方法,逻辑上加以判断就好

     

    第六步:当我们添加的数据长度超出capacity容量时就应该考虑换一个更大的数组去容纳这些数据!

    1 private void resize(int newCapacity) {
    2         E[] newData = (E[]) new Object[newCapacity];
    3         for (int i = 0; i < size; i++) {
    4             newData[i] = data[i];
    5         }
    6         data = newData;
    7     }

     主要是在添加方法中复用该resize方法

     1 /**
     2      * Insert an element on an index
     3      * @param index
     4      * @param e
     5      */
     6     public void add(int index,E e) {
     7         if (index < 0 || index > size)
     8             throw new IllegalArgumentException("Add failed.Array is full");
     9         if (size == data.length) {
    10             resize(2*data.length);
    11         }
    12         for (int i = size - 1; i >= index; i--) {
    13             data[i+1] = data[i];
    14         }
    15         data[index] = e;
    16         size++;
    17     }

     假设data数组的容量为10,那么最大索值index就为为9,当index == 9这个位置存入值时,size即为10,所以用if (size == data.length)去判断数组是否应该扩     容。当数组空间已经装满数据,我们再次添加数据时就会触发resize方法,使得数组“扩容”。

    以下是缩容的算法

     1 /**
     2      * remove elements from index positions
     3      * @param index
     4      * @return
     5      */
     6     public E remove(int index) {
     7         if (index < 0 || index >= size)
     8             throw  new IllegalArgumentException("remove failed,array is full");
     9         E ret = data[index];
    10         for (int i = index + 1; i < size; i++) {
    11             data[i - 1] = data[i];
    12         }
    13         size--;
    14         if (size == data.length/4 && data.length/2 != 0) {
    15             resize(data.length/2);
    16         }
    17         return ret;
    18     }

     我们判定数组缩容的条件是 size == data.length/4,即当前数据的总量只用capacity总容量的 1/4。

     为什么不是 size == data.length/2 

     理由:如果是 size == data.length/2 就开始缩容的话,那么当我们再次添加元素时又会进行扩容,两次基本的O(1)的操作却触发了两次O(n)的数组拷贝,试想一下,如果我的操作是反复的在 size == data.length/2 时添加和移除呢?势必每一次的操作都会是O(n)级别的复杂度!这就是复杂度的震荡!

     data.length/2 != 0    ===> 考虑data.length == 1时, 1/2 == 0,数组的容量自然是不能为0的!

    转载于:https://www.cnblogs.com/zhixiangshu/p/10106350.html

    展开全文
  • 做题做着就发现不对劲了,查找资料分析了一下静态数组名和动态数组名的区别。找着找着顺道写了C/C++中的数据访问方式
  • 摘要的重要性是不言而喻的,每次发文章我都很纠结如何写出一个有特色的摘要来,能够以最为简短的文字向读者描述出我所要表达的东西。但是常常出现的问题是,摘要写得太简短了,读者看了不清楚文章究竟要讲啥;摘要写...
  • 关于数组

    2017-07-09 17:03:22
    数组 数组是一系列的相同类型的元素放在连续的内存位置,可以通过添加一个索引中单独引用一个独一无二的标识符。 这意味着,例如,5的值类型int可以声明为数组,而无需声明5个不同变量(每个都有其自己的标识符)。 ...
  • 数组

    2021-08-18 19:56:10
    数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问他们 数组声明创建 首先必须声明数组变量,才能在程序中使用数组,...
  • Java数组

    2021-05-05 12:08:41
    目录 数组概述 数组的四个基本特点: 数组声明创建 1、声明数组 2、创建数组 3、内存分析 4、三种初始化 5、数组边界 数组使用 1、For-Each 循环 ... 数组描述的是相同类型的若干个数据,按照一..

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 110,227
精华内容 44,090
关键字:

以下关于静态数组的描述