第四篇 数组

143 阅读4分钟

一、数组的定义

1、基本概念和内存图

数组可以存放多个同一类型的数据。 数组也是一种数据类型, 是引用类型,他在内存里是这个样子的。

image.png 他是这样定义的。

定义:
int[] nums;
初始化:
nums = new int[3];
赋值:
nums[0] = 1;   
nums[1] = 4;
nums[2] = 3;

// 直接定义初始化并赋值
int[] nums = {1,2,3};

// 这样写也行
int nums[] = new int[5];
int nums[] = new int[]{1,2,4,5};

// 数组有一个属性,可以获得数组的长度
nums.length 

类型[] 名字 = new 类型[长度];

说说这三个步骤,如图:

image-20210802145617399

三个问题:

1、数组不初始化能赋值吗?

2、初始化之后,如果不赋值,会不会有默认值?只要给你开辟了空间就一定会有默认值。

3、数组能不能越界赋值,比如长度为三,非要给第五个赋值。

2、数组的性质

结束了这个谈论,我们简单的概括一个数组的特性

  1. 数组一旦建立,长度不能改变。
  2. 每个位置只能存一个值,多了会覆盖。
  3. 数组创建后会有默认值:int 0, short 0, byte 0, long 0, float 0.0,double 0.0, char \u0000, boolean false, String null
  4. 编号从0开始,下标必须在指定范围内使用, 否则报: 下标越界异常。
  5. 他有个长度的属性,最后一个位置的编号是 长度-1。 0 - length -1
  6. 数组里边可以是基本类型,也可以是引用类型。

3、数据结构

大学里有一门课很重要,叫数据结构与算法。

数组是一种最基本的数据结构,是的一种,是一种二维的线性表,我们以后还会接触链表,hash表等。

百度百科是这样解释线性表的:

  • 线性表是最基本、最简单、也是最常用的一种数据结构。线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
  • 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的)。

二、玩转数组

1、通循环遍历打印数组的每一个值

我们已经学习过了for循环,for循环正好能从0循环到某一个值,而这刚好可以对应我们数据的下标,写出一个优雅的循环。

我们要明确的是我的的范围是 0 ~ length -1

for (int i = 0; i < salary.length; i++) {
    System.out.print(salary[i] +  " ");
}

我们不妨思考一下使用while循环可以吗?

int i = 0;
while (i < nums.length){
    System.out.println(nums[i]);
    i++;
}

1
2
3
4
5\

大家慢慢一定要学会举一反三。

2、查找一个数组里存在的值

思路很简单,遍历每一个值,然后比较,知道遇到这个值为止。

// 定义一个变量,存放找到的目标值
int target ;
for (int i = 0; i < salary.length; i++) {
    if(salary[i] == 5){
        res = salary[i];
    }
}
System.out.println(target);

你会发现这么写是错的,编译可能出问题:Error:(21, 28) java: 可能尚未初始化变量res;

那我们target初始化一个几呢?0吗? 万一人家最后的就是要找0,怎么办,其实巧用数组下标是个好办法。

这其实很简单,我们的目标其实是查找一下这个数字在第几个格子而已。这样的好处就是,格子不可能有负数。

我们不妨这样设计,如果找到了就获得他的下标,如果找不到就得到-1,代码可以优化如下:

int targetIndex = -1;
for (int i = 0; i < salary.length; i++) {
    if(salary[i] == 9){
        targetIndex = i;
    }
    break;
}

3、打擂台的形式找最大值

今天要比武决胜负,找出一个武功最高的当武林盟主。

1、黄蓉在台上大喊,今天我们要比武决胜负,选出一位丐帮帮主,哪位英雄好汉愿意第一个上。

2、丁春秋第一个上来,大吼,谁敢上来和我比试比试?

3、郭芙蓉上台一个排山倒海干掉了丁春秋,于是丁春秋下台,台上站的是郭芙蓉。

4、大家你来我往,一个一个上,最后站在台上的是张无忌,顺利当选。

我们的代码如下:

// 定义一个数组,存放各路英雄豪杰
int[] salary = {4,5,0,6,7,8};
// 搞一个擂台,让第0个人上来
int maxIndex = 0 ;
// 然后从第一个开始打
for (int i = 1; i < salary.length; i++) {
    // 谁赢了,谁继续在台上站着
    if(salary[i] > salary[maxIndex]){
        maxIndex = i;
    }
}
System.out.println("经过了n轮比赛得到的最大值得下标是:"+maxIndex+"。值是:"+salary[maxIndex]);

4、元素的位移

一个新的需求:

我门计划将数组中的两个元素交换位置。

int[] salary = {4,5,0,6,7,8};

salary[0] = salary[1];
salary[1] = salary[0];

我们第一次很有可能写出这样的代码。

salary[0] = salary[1]; 这句话执行完了之后 数组中 就出现了两个salary[1]了。

合适的方法是,在找一个空间,临时存一下salary[0]。

int[] salary = {4,5,0,6,7,8};

int temp = salary[0];
salary[0] = salary[1];
salary[1] = temp;

5、数组的扩容

(1)思路一:

我们一定要知道,数组本质上不能改变大小,实质上的扩容是不行的,需要使用一些手段。

画一个图吧:

image-20210802163532080

// 定义原始数组
int[] nums = new int[]{3,4,6};
// 定义一个新的临时数字
int[] temp = new int[6];
// 讲原始数组的数据全部拷贝到临时数组
for (int i = 0; i < nums.length; i++) {
	temp[i] = nums[i];
}
// 让原始数组的引用指向临时数组,感觉上就像原始数组被扩容了
nums = temp;
for (int i = 0; i < nums.length; i++) {
System.out.println(temp[i]);
}

创建一个等长的数组,反向放入,最后改变引用即可

int[] nums = new int[]{3,4,6};
int[] temp = new int[nums.length];
for (int i = nums.length - 1; i >= 0; i--) {
    temp[i] = nums[nums.length - 1 - i];
}
nums = temp;
for (int i = 0; i < nums.length; i++) {
    System.out.println(temp[i]);
}

(2)思路二:

利用交换的方式:

image-20210802164737918

// 定义原始数组
int[] nums = new int[]{3,4,6};
// 交换反转
for (int i = 0; i < nums.length/2; i++) {
    int temp = nums[nums.length - 1 - i];//保存
    nums[nums.length - 1 - i] = nums[i];
    nums[i] = temp;
}
for (int i = 0; i < nums.length; i++) {
    System.out.println(nums[i]);
}

三、二维数组

二维数组使用的频率很少,但是也有一些类似的需求,比如我们开发一个围棋游戏, 棋盘就可以用二维数组来表示。 如图:

image-20210802171101104

怎么定义一个数组呢?

比如: int nums[][]=new int[2][3]

在内存中二维数组张这个样子:

image-20210802171351473

请用二维数组输出如下图形 :TwoDimensionalArray

int[][] nums = {{1,2,3,4,5,6},
                {6,5,4,3,2,1},
                {1,2,3,4,5,6}};
                

for(int i = 0; i < arr.length; i++) {
    for(int j = 0; j < arr[i].length; j++) {
    	System.out.print(arr[i][j] + " ");
    } 
	System.out.println();//换行
}

思考,二维数组里的每一个数组的长度一定要一样吗,如下图可以吗?

image-20210802172753104

答案是可以的:

int[][] nums = {{1,4},{1,2,6},{5,6,3,2,1}};

我们还可以这样赋值:

// 这个初始化做了什么工作,这样就可以开辟空间初识化了,只不过每一个都是null
int[][] nums = new int[3][];
// 赋值
nums[0] = new int[5];
nums[1] = new int[3];
nums[2] = new int[7];

明白一个道理,初识化只要知道我需要多少空间,然后在堆内存给他分配就好了。

附加题:

写一个杨辉三角,他是这么一个三角:

  1. 每个数等于它上方两数之和。
  2. 每行数字左右对称,由1开始逐渐变大。
  3. 第n行的数字有n项。
  4. 前n行共[(1+n)n]/2 个数。

image-20210802173406744

我们的输入目标:

使用二维数组打印一个 10 行杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1

代码实现

public class YangHui {
    public static void main(String[] args) {

        int max = 10;

        // 分配三角形的数组
        int[][] nums = new int[max + 1][];
        for (int n = 0; n <= max; n++)
            nums[n] = new int[n + 1];

        // 填充杨辉三角
        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums[i].length; j++) {
                // 边边上全是1,每一行的0和length-1都是1
                if (j == 0 || j == nums[i].length - 1) {
                    nums[i][j] = 1;
                } else {
                    // 剩余的部分按照规律来
                    nums[i][j] = nums[i-1][j-1] +nums[i-1][j];
                }
            }
        }

        for (int i = 0; i < nums.length; i++) {
            for (int j = 0; j < nums[i].length; j++) {
                System.out.print(nums[i][j]+" ");
            }
            System.out.println();
        }
    }
}