Java数据结构和算法-数组

1.Java数组的基础知识

1.1 创建数组

Java当中的数据类型有两类,一类是基础类型(如int和double),还有一类是对象类型,在许多编程语言中(如C和C++),数组属于基础类型,但在Java当中,数组属于对象,所以数组的创建方式利用new操作符:
int[] array; //定义对数组的引用
array = new int[100]; //创建一个数组,并且利用array来引用它

或者使用等价的单语句声明方法:

int[] array = new int[100];

[]操作符是一种标志,它说明了正在声明的数组对象。当然也可以将[]放在变量名的后面来使用这个操作符:

int array[] = new int[100];

不过将[]放在int的后面能够更清楚的说明[]是数据类型的一部分,而不是变量名的一部分。
因为数组是对象类型,所以创建的变量名是对数组引用,而并不是数组本身,数组存储在内存的其他地址中,在变量(如上面代码中的array变量)当中存储的仅仅是数组所在的地址。
数组中有一个length字段,通过它可以知道当前数组的大小(数据项的个数)。

int arrayLength = array.length;

和大多数的编程语言一样,数组一旦被创建,其大小是不可改变的。

1.2访问数据项

和其他编程语言类似,数组的访问通过方括号加下标进行,且下标的大小为0到数据项个数减1。

int[] array = {1, 23, 54, 98};
int temp = array[0]; //对temp赋予array数组的第一个数据项1
array[3] = 88;       //对array的最后一个数据项赋值88

如果访问之外的下标,程序会出现Array index out of Bounds的错误。

1.3初始化

当创建数组之后,如果没有进行另外指定,整个数组会被初始化为空,和C++不同的地方在于,即使通过方法(函数)来定义一个数组:

int[] array = new int[100];

除非对数组的数据项赋予特定的值,否则他们将被赋予特殊的null对象。如果尝试去访问这些null对象,那么程序将报Null Point Assignment(空指针赋值的错误),这主要是为了保证在读取数组的数据项之前先对其赋值。
在数组中利用如下的语法对一个基本的数组赋予非空值:

int[] array = {10, 100, 24, 342, 242};

这种赋值的方式同时取代了引用声明和使用new来创建一个数组,花空号内的内容被称为初始化列表,数据项的初始化个数,即为数组的大小。

1.4面向对象的数组列子

将数组封装在一个类HighArray的中,通过另为一个类ArrayApp来访问这个类。

class HighArray {
    private int[] array;
    private int nItem;

    public HighArray(int max) {
        array = new int[max];
        nItem= 0;
    }

    public boolean find(int key) {
        for(int i = 0; i < nItem; i ++) {
            if(array[i] == key)
                return true;
        }

        return false;
    }

    public void insert(int val) {
        array[nItem] = val;
        nItem ++;
    }

    public boolean delete(int val) {
        int i, n;
        for(i = 0; i < nItem; i ++) {
            if(array[i] == val) {
                n = i;
                break;
            }
        }

        if(n < nItem) {
            for(i = n; i < nItem-1; i ++) {
                array[i] = array[i+1];
            }
            nItem --;

            return true;
        }

        return false;
    }

    public void display() {
        for(int i = 0; i < nItem; i ++)
            System.out.print(array[i] + " ");
        System.out.println("");
    }
}//end HighArray

class ArrayApp {

    public static void main(String[] args) {
        int max = 100;
        HighArray array = new HighArray(100);

        //插入数据项
        array.insert(32); 
        array.insert(453);
        array.insert(98);
        array.insert(43);
        array.insert(24);
        array.insert(234);

        //展示当前数组数据项
        array.display();

        //查询24是否存在于数组中
        if(array.find(24))
            System.out.println("Found 24");
        else
            System.out.println("Not Found 24");

        //删除数据项35和43
        array.delete(35);
        array.delete(43);

        //展示当前数组数据项
        array.display();
    }//end main
}//end ArrayApp

控制台输出:
32 453 98 43 24 234
Found 24
32 453 98 24 234

1.5有序数组

当数组中的数据项按升序进行排列时,该数组被称为有序数组。引入有序数组在插入会比较麻烦,不能像一般的数组那样直接在最后一个数据项之后插入,而需要找到合适位置是其插入后,数组仍有序,但是有序数组也有一定的优点,它相对与一般数组而言,除了可以使用线性查找外,也可以使用二分查找,从而显著的提高了查找速度。

线性查找

即从0开始依次向后,寻找匹配直到找到匹配项,或者到达最后的数据项。

二分查找

与线性查找不同,二分查找在给定的范围内按照线性查找进行查找,当发现匹配项,或者比查找值大的数据项时退出,如果找到匹配项,或者缩小后的范围上限值小于范围下限值,则停止,否则在缩小后的范围内继续查找,重复步骤。或许我说的并不清楚,有点抽象,那么就从平常玩的猜数游戏来体会一下什么是二分查找,二分查找的优点在哪里。

猜数游戏

在游戏中,一个朋友一般会给定初始范围为1~100,在心中给定一个具体的数字,然后让你猜,根据你给的数字,他会给你三个答案中的一个:太大,太小,猜对。为了尽快猜到数字,我们会从最中间的数字开始猜起,即50,如果他说你猜的太大了,那么我们会缩小范围到1~49,此时我们会选择25,如果他说你猜的太小,那么我们会从51~100中选择,此时我们会选择75,依次猜下去,每次都会将可能的值划分为两部分,最后范围就会缩小到一个数字那么大了,那么这个数字就是答案。
这种方法大大减少了比较次数,如果按照线性查找的步骤来进行,那么100个数据项平均会进行50次比较,而二分查找例如猜测答案为32的数字,猜测的步数如下表:

步数所猜的数结果可能值范围
01~100
150太大1~49
225太小26~49
337太大26~36
431太小32~36
534太大32~33
633太大32~32
732猜对

可以发现如果在第六次就猜32则6次即可得到答案,否则最多也只要7次就能得出答案。

有序数组的Java代码

class OrdArray {
    private int[] array;
    private int nItem;       //数据项个数

    public OrdArray(int max) {
        array = new int[max];
        nItem = 0;
    }

    public int size() {
        return nItem;
    }

    public int find(int val) {
        int lower = 0;   //初始下限下标
        int upper = nItem-1; //初始上限下标
        int in           //初始猜测值下标

        while(true) {
            if(lower > uppper)
                return nItem;            //没有找到
            in = (lower + upper) / 2;
            if(array[in] == val)
                return in;               //返回下标
            else if(array[in] > val)    //猜测的值太大
                lower = in + 1;          
            else                        //猜测的值太小
                upper = in - 1;
        }
    }

    public void insert(int val) {
        int j;
        for(j = 0; j < nItem; j ++)
            if(array[j] > val)
                break;

        for(int n = nItem; n > j; n --)
            array[n] = array[n - 1];
        array[j] = val;
        nItem ++;
    }

    public boolean delete(int val) {
        int j = find(val);        //利用find方法找到val所在下标
        if(j < nItem) {
            for(int i = j; i < nItem-1; i ++)
                array[i] = array[i + 1];
            nItem --;
            return true;
        }

        return false;
    }

    public void display() {
        for(int i = 0; i < nItem; i ++)
            System.out.print(array[i] + " ");
        System.out.println("");
    }
}//end OrdArray

class OrderedArray() {

    public static void main(String[] args) {
        OrdArray array = new OrdArray(10);

        array.insert(15);
        array.insert(32);
        array.insert(8);
        array.insert(64);
        array.insert(53);

        array.display();
        System.out.println(array.find(32));
        System.out.println(array.find(50));

        array.delete(53);
        array.display();
    }
}

控制台输出:
8 15 32 53 64
2
5
8 15 32 64
OrdArray封装的类中的find方法是该类的核心,该方法在开始时设定下限的下标lower为0,上限的下标upper为数组大小减1,来确定查找的初始范围,在while循环中,将当前猜测的值下标in设为上限和下限下标的中间值,若该下标in所对应的值正好与所需值相同,则返回in下标,否则缩小范围,重复上述步骤。如果幸运的话,在第一次就可以找到想要的答案。OrdArray类程序和HighArray类类似,主要差异在于find方法使用了二分查找。
其实insert方法中查找新插入的位置也可以使用二分查找,那样的话就需要稍微改变一下find方法,虽然查找的步骤有所减少,但由于插入时,平均一半的数据项都要移动,所以二分查找并不会明显提高insert的速度,所有简单起见还是在insert中使用了线性查找。

有序数组的优点

有序数组相对于一般数组的最大优点在于查找速度大大提高,尤其是在数组大小很大的情况下。缺点在于插入时因为要有序,所以增加了查找插入位置以及数据项移动的时间,速度较慢。有序数组和无序数组在进行删除时因为要向前移动来填补已删除的数据项的洞,所以都很慢。
有序数组在查找时很方便,插入和删除都较为复杂。因此适用于查找工作多,而插入和删除工作不那么频繁的情况。

1.6存储对象

数组作为一种数据结构,既可以存储简单数据类型,同样也可以存储对象类型数据。在现实世界,我们存储的数据记录通常是由多个字段组成的,例如一条学生记录需要存储名字、性别、班级、专业等等。具体的例子如下:

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "PersonName: " + name + "; " + 
                "PersonAge: " + age + ";";
    }
}//end Person

class ObjectArray {
    private Person[] personArray;
    private int nItem;

    public ObjectArray(int max) {
        personArray = new Person[10];
        nItem = 0;
    }

    public Person find(String name) {
        for(int i = 0; i < nItem; i ++)
            if(name.equal(personArray[i].getName))
                return Person;

        return null;
    }

    public void insert(Person p) {
        personArray[nItem] = p;
        nItem ++;
    }

    public boolean delete(String name) {
        int n;
        for(n = 0; n < nItem; n ++)
            if(name.equal(personArray[n].getName))
                break;

        if(n < nItem) {
            for(int i = n; i < nItem-1; i ++)
                personArray[n] = personArray[n + 1];
            nItem --;
            return true;
        }

        return false;
    }

    public void display() {
        for(int i = 0; i < nItem; i ++)
            System.out.println(personArray[i]);
    }
}//end ObjectArray

class ObjectApp {

    public static void main(String[] args) {
        ObjectArray[] array = new ObjectArray[10];

        array.insert(new Person("a", 24));
        array.insert(new Person("b", 43));
        array.insert(new Person("c", 54));
        array.insert(new Person("d", 18));
        array.insert(new Person("e", 20));

        array.display();
        System.out.println("- - - - - - - - - - - - - - -");
        System.out.println(array.find("c"));
        System.out.println("- - - - - - - - - - - - - - -");
        array.delete("d");
        System.out.println("- - - - - - - - - - - - - - -");
        array.display();
    }
}//end ObjectApp

控制台输出:
PersonName: a; PersonAge: 24;
PersonName: b; PersonAge: 43;
PersonName: c; PersonAge: 54;
PersonName: d; PersonAge: 18;
PersonName: e; PersonAge: 20;


PersonName: c; PersonAge: 54;


PersonName: a; PersonAge: 24;
PersonName: b; PersonAge: 43;
PersonName: c; PersonAge: 54;
PersonName: e; PersonAge: 20;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值