首页 > 代码库 > 当数据结构遇到编程语言——数组

当数据结构遇到编程语言——数组

赵振江 数据结构 数组 一维数组

“数组”你真的很了解吗?

数组大家都不是很陌生,它已经“植入”了许多编程语言,但初学者一提到数组,可能不会联想到“数据结构”,而是想到的会是一种“数据类型”,数组本质上就是一种极其简单的数据结构。所谓数组,就是相同数据类型的元素按一定顺序排列的集合。也就是在内存中划分一段连续的且大小固定(注意是连续)的内存空间(或者其他存储器)保存相同数据类型的数据,如下图。一般说的简单数组,都是静态的数组,所以很容易导致数据溢出和访问越界。不需担心的是,很多高级语言中已经设计了动态数组了,这个以后还会讨论。

数组的维数

数组的维数始终是个重要话题,这里先看看最简单的一维数组。对了,搞数学的更愿意把二维数组称之为“矩阵”,关于矩阵的话题也很多,以后会提到。依次类推还有三维、四维、……

数组和表有什么关系

先说明一下:数组属于线性表。关于表的话题以后再说,不急。

数组和数据类型

int型的数组、float型的数组好理解,也就是规定数组里面存储了统一数据类型的变量(绝大部分编程语言都是这样,有些不一定如:Visual Foxpro)。在C/C++中,数组是基本数据类型,C中作为一种构造数据类型出现,而在java中作为引用数据类型出现(有的书也称之为 对象数据类型)。

数组与Java,C++,C,JS,汇编……(之后再补充)

知道了数组的数据组织方式(在内存中的结构),接下来就要知道对于数组的一些操作(处理)了。主要就是插入insert,查找find,删除remove,显示。这里分别用几种编程语言来实现,显然java和C++等具有面向对象编程思想的编程语言,对于实现算法要直观些,但要讲到对于在数组内存中的体现,显然C和汇编直观些。

Java语言版本

class Array{

    private long[] a;

    int nElems;

    //构造函数constructor
    public Array(int size){
        this.a=new long[size];
        this.nElems=0;
    }

    //插入insert
    public void insert(long e){
        a[nElems]=e;
        nElems++;
    }

    //查找find
    public int find(long searchKey){
        int j;
        for(j=0;j<nElems;j++){
            if(searchKey==a[j])
                return j;
        }
        return -1;
    }

    //删除delete
    public boolean remove(long searchKey){
        int k =find(searchKey);
        if(k!=-1){
            for(;k<nElems;k++){
                a[k]=a[k+1];
            }
            nElems--;
            return true;
        }
        return false;
    }

    //显示display
    public void display(){
        for(int i =0;i<nElems;i++){
            System.out.print(a[i]+" ");
        }
    }
}


public class ArrayTest {
    public static void main(String[] args){
        Array array = new Array(10);
        //插入
        array.insert(12);
        array.insert(13);
        array.insert(11);
        array.insert(15);
        array.insert(16);
        //查找
        if(array.find(13)!=-1)
            System.out.println("查找成功,下标为:"+array.find(13));
        else System.out.println("查找失败!");
        //删除
        if(array.remove(11))
            System.out.println("删除成功!");
        else System.out.println(" 删除失败!");
        //显示 
        System.out.print("显示数组:");
        array.display();
    }

}

C++语言版本

#include <iostream>
using namespace std;

template <class T>
class Array{
private :
    int size;               //数组大小
    T *a;               //一维数组
    int nElems;             //实际元素个数
public:
    Array(int sz);              //构造函数
    ~Array() {delete [] a;}            //析构函数
    int Size(){             //返回数组大小
        return size;
    }
    void insert(T e);               //插入

    int find(T e);              //查找

    bool remove(T e);               //删除

    void display();

};
template <class T>
Array<T>::Array(int sz){
    if(sz<0){
        cerr<< "数组元素不能为0"<<endl;
        return;
    }
    else{
        size=sz;
        a= new T[sz];
        nElems=0;
    }

}


/////////插入/////////////
template <class T>
void Array<T>:: insert(T e){
    a[nElems]=e;
    nElems++;
}

//////////查找///////////////
template <class T>
int Array<T>::find(T searchKey){
    int j;
    for(j=0;j<nElems;j++){
        if(searchKey==a[j])
            return j;
    }
    return -1;
}

///////////删除///////////
template <class T>
bool Array<T>::remove(T e){
    int k =find(e);
    if(k!=-1){
        for(;k<nElems;k++){
            a[k]=a[k+1];
        }
        nElems--;
        return true;
    }
    return false;
}

//////////显示//////////////
template <class T>
void Array<T>::display(){
    for(int i =0;i<nElems;i++){
        cout<<a[i]<<" ";
    }
}


int main() {
    Array<long> arr(10);
    //插入
    arr.insert(12);
    arr.insert(13);
    arr.insert(14);
    arr.insert(15);
    arr.insert(16);
    //显示
    arr.display();
    cout<<endl;
    //查找
    cout<<arr.find(13)<<endl;               //注意返回的是下标
    //删除
    arr.remove(14);
    //显示
    arr.display();
    ~Array();
    return 0;
}

JS语言版本

//稍等两天提供

C语言版本
具体操作这里不再提供,只提供struct结构体,构造数组和销毁数组函数

#include <stdio.h>
#include <stdlib.h>

#define OK 1
#define ERROR 0
#define ElemType int
#define Status int
#define OVERFLOW 3
//定义结构体Array
typedef struct{
    ElemType *base;
    int nElems;
    int size;
}Array;

//初始化数组,类似于java和C++的构造函数
Status InitArray(Array A,int sz){
    A.size=sz;
    if(sz<0)
        return ERROR;
    A.base=(ElemType *)malloc(A.size*sizeof(ElemType));
    if(!A.base) exit(OVERFLOW);
    return OK;
}

//销毁数组,类似C++的析构函数和java的垃圾回收
Status DestroyArray(Array A){
    if(!A.base)
        return ERROR;
    free(A.base);
    A.base=NULL;
    return OK;
}

Win32汇编版本
汇编版本的更加直观,所谓直观,是直接到了内存的层面。程序先是以Array为基地址,连续创建了4块每块大小为32bit(4字节,双字)连续的空间,并进行了初始赋值。下面用提示框(MessageBox)进行数组的展示,和数组大小的展示(用lengthof可以更快实现,程序中都有用到)。接着用一个loop循环,依次遍历读取数组各个元素,算出数组元素的和。其中部分注释展示了,数组的读取和删除,注意删除的时候,数组后面元素会向前移动,这在上面的Java 和C++中有所展示,汇编里就不再展示了。

.386
.model flat,stdcall
option casemap:none

include user32.inc
include windows.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib

.data
Array DWORD 12,23,11,22     ;Array为基地址,数据类型为双字(32bit,4个字节)
ArraySize=($-Array)/4
szCap db ‘一维数组‘,0
szText1 db 100 dup(0)           ;100byte字符缓冲区
szText2 db 100 dup(0)
szOutPut1 db ‘数组:%d,%d,%d,%d 长度:%d‘,0
szOutPut2 db ‘数组:%d‘,0
.code
start:
invoke wsprintf,addr szText1,addr szOutPut1,Array,Array+4,Array+8,Array+12, ArraySize
invoke MessageBox,NULL,offset szText1,offset szCap,MB_OK
mov esi,offset Array
mov ecx,lengthof Array
L:                          ;循环读取数组元素,并求所有元素之和
add eax,[esi]
add esi,type Array          ;指向下一个元素
loop L

;mov eax,[esi+4]        ;数组第2个元素
;mov [esi+8],[esi+12]       ;删除数组第三个元素
invoke wsprintf,addr szText2,addr szOutPut2,eax
invoke MessageBox,NULL,addr szText2,offset szCap,MB_OK
invoke ExitProcess,NULL
end start

数组存放对象元素

以上无论是什么语言的数组,里面存储的元素都是基本数据类型的,这显然不符合平时的需求,我们希望它可以存储对象元素(当然,得是遵循OOP的编程语言)。这里以java为例,用数组来存放Person类类型的数据。

package array;
/**
 * 
 * @author River 2015-1-27
 *
 */

/**
 * Person类
 */
class Person{
    private long ID;
    private String name;
    private int age;

    public Person(long id,String n,int a){
        ID=id;
        name=n;
        age=a;
    }
    public String display(){
        return "id:"+ID+" 姓名:"+name+"  年龄:"+age;
    }

    public String getName(){
        return name;
    }

    public long getID(){
        return ID;
    }
}

/**
 * ClassDataArray类实现,数组保存对象
 * @author River
 *
 */
class ClassDataArray{
    private Person[] a;             //定义一个Person类型的数组
    private int nElems;             //实际元素个数

    public ClassDataArray(int size){
        a=new Person[size];
        nElems=0;
    }

    ////////////插入/////////////////
    //public void insert(Person per){
    public void insert(long id,String name,int age){
        a[nElems]= new Person(id, name, age);
        nElems++;
    }

    ////////////查找//////////////////

    public int find(long id){
        for(int j=0;j<nElems;j++){
            if(a[j].getID()==id)
                return j;               //找到,返回下标
            else if(j==nElems)
                break;                  //没有查找到
        }
        return nElems;
    }
    ///////////查找另一种写法////////////
    /*public Person find2(String name){
        for(int j=0;j<nElems;j++){
            if(a[j].getName().equals(name))
                return a[j];                //找到,返回下标
            else if(j==nElems)
                break;                  //没有查找到
        }
        return null;
    }*/

    //////////////删除//////////////
    public boolean delete(long id){
        if(find(id)!=nElems){
            for(int j=find(id);j<nElems;j++)
                a[j]=a[j+1];
            nElems--;
            return true;
        }
        return false;
    }

    ///////////////显示/////////////////
    public void display(){
        for(int j=0;j<nElems;j++){
            System.out.println(a[j].display());
        }
    }
}
public class ClassDataArrayTest {
    public static void main(String[] args){
        ClassDataArray arr = new ClassDataArray(10);
        arr.insert(1, "River", 22);
        arr.insert(2, "ShiTianShaoNv", 22);
        arr.insert(3, "QiXiaoXing", 21);

        arr.display();

        arr.find(1);//可以写成查找名字的形式
        arr.delete(2);
        arr.display();
    }

}

数组真的是存储的对象?

数组确实存储的是对象元素,但可能与你心中想象的有些区别,这时,数组实际存储的是一个地址,这个地址指向目标对象在里面的首地址,而这个变量名(对象名),存在于栈当中。

数组的优点与缺点

优点:数据结构简单,可以完成简单的数据存储。当对于数据元素没有要求时(比如顺序),可以很容易的进行数据添加(在数组尾部插入即可)。
缺点:数组的大小尺寸是不变的,太小容易越界,太大浪费空间。查找,添加,删除都比较费时(除去最好情况),移动数组元素比较频繁。具体的复杂度这里先不给出,下篇专门讨论复杂度问题。

数组的升级版

有序数组,放到算法分析里以其例子进行说明。

当数据结构遇到编程语言——数组