Java基础学习【第四章 数组(二)】

6.综合案例

案例1:求数组平均值

实现一个方法,参数是int[],该方法可以计算出数组中所有数据的平均值并返回



public class Test06_Case {
    // 求数组的平均值
    public static double getAvg(int[] arr) {
        int length = arr.length;
        double sum = 0;
        for (int i = 0; i < length; i++) {
            sum = sum + arr[i];
        }
        return sum / length;
    }

    public static void main(String[] args) {
        int[] array = new int[]{12, 3, 5, 7, 6};
        double avg = getAvg(array);
        System.out.println("平均值: " + avg);
    }
}

案例2:求数组最大值

实现一个方法,参数是int[],该方法可以计算出数组中所有数据的最大值并返回


 

public class Test06_Case {
    // 省略...

    // 求最大值
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }

    public static void main(String[] args) {
        int[] array = new int[]{12, 3, 5, 7, 6};
        int max = getMax(array);
        System.out.println("最大值: " + max);
    }
}

案例3:数组反转

实现一个方法,参数是int[],该方法可以将数组中所有元素进行反转,第一个和最后一个元素互换、第二个和倒数第二个互换...

import java.util.Arrays;

public class Test06_Case {
    // 省略...

    // 反转方法
    public static void reverseArray(int[] arr) {
        int len = arr.length;
        for (int i = 0; i < len / 2; i++) {
            arr[i] = arr[i] ^ arr[len - 1 - i];
            arr[len - 1 - i] = arr[i] ^ arr[len - 1 - i];
            arr[i] = arr[i] ^ arr[len - 1 - i];
        }
    }

    public static void main(String[] args) {
        int[] array = {8, 6, 9, 1, 4, 12, 7};
        System.out.println(Arrays.toString(array));
        reverseArray(array);
        System.out.println(Arrays.toString(array));
    }
}

案例4:冒泡排序

冒泡(Bubble Sort)排序是一种简单排序算法,它通过依次比较交换两个相邻元素实现功能。每一次冒泡会让至少一个元素移动到它应该在的位置上,这样 n 次冒泡就完成了 n 个数据的排序工作。


import java.util.Arrays;

public class Test06_Sort {
    public static void main(String[] args) {
        int[] array = {4, 5, 3, 2, 1};
        bubbleSort(array);
        System.out.println(Arrays.toString(array));
    }

    // 冒泡排序
    public static void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    arr[j] = arr[j] ^ arr[j + 1];
                    arr[j + 1] = arr[j] ^ arr[j + 1];
                    arr[j] = arr[j] ^ arr[j + 1];
                }
            }
            System.out.println("第" + (i + 1) + "次排序后: " + Arrays.toString(arr));
        }
    }
}


```

案例5:二分查找

在一个有序序列中查找其中某个元素,我们可以采用二分查找(折半查找),它的基本思想是:将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止;如果x<a[n/2],则我们只要在数组a的左半部继续搜索x;如果x>a[n/2],则我们只要在数组a的右半部继续搜索x。

public class Test06_BinarySearch {
    // 二分查找:针对有序序列进行查找
    public static void main(String[] args) {
        int[] arr = {1, 3, 4, 5, 7, 9, 10};
        int index = binarySearch(arr, 1);
        System.out.println("1: " + index);
        index = binarySearch(arr, 2);
        System.out.println("2: " + index);
        index = binarySearch(arr, 10);
        System.out.println("10: " + index);
    }

    // 二分查找算法,如果value存在arr中,则返回元素位置,如果找不到返回-1
    public static int binarySearch(int[] arr, int value) {
        int start = 0;
        int end = arr.length - 1;
        int mid;
        while (true) {
            mid = (start + end) / 2;
            if (value == arr[mid]) {
                return mid;
            } else if (value > arr[mid]) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
            if (start > end) {
                break;
            }
        }
        return -1;
    }
}


```

7. 数组拷贝

数组的长度确定后便不能修改。如果需要数组存放更多元素,可以通过创建长度更长的新数组,然后先复制老数组内容到新数组中,再往新数组中放入额外的元素。在`java.lang.System`类中提供一个名为`arraycopy`的方法可以实现复制数组中元素的功能。

该方法的声明:
```java
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
```
参数1: 需要被复制的目标数组
参数2: 从目标数组的哪一个位置开始复制
参数3: 需要把数据复制到另外一个新的数组中
参数4: 把数据复制到新数组的时候,需要把数据从什么位置开始复制进去
参数5: 复制的目标数组的长度

案例展示:

定义一个方法,传递一个数组对象给它,其将数组长度扩大到原来的2倍并返回。

```java
import java.util.Arrays;

public class Test08_ArrayCopy {
    // 拷贝数组扩容2倍
    public static int[] extend(int[] src) {
        // 1. 先准备有一个空白数组,容量为src的两倍
        int[] dest = new int[src.length * 2];

        // 2. 拷贝
        System.arraycopy(src, 0, dest, 0, src.length);

        // 3. 返回
        return dest;
    }

    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5};
        int[] newArr = extend(array);
        System.out.println(Arrays.toString(newArr));

        // [1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
        // 将array拷贝到newArr的最后5个元素位置
        // 注意事项:最后一个参数不要越界【其值 > 待拷贝数组.length】
        System.arraycopy(array, 0, newArr, 5, array.length);
        System.out.println(Arrays.toString(newArr));
    }
}


```

8. 可变参数列表

JDK 1.5或者以上版本中,可以使用可变参数列表。格式如下:
```java
修饰符 返回值类型 方法名(数据类型... 参数名) {
    方法体语句;
}

```

使用示例:
```java
// 普通方法定义
public static void fun(int[] a) {
    // ...
}

// 可变参数列表方法定义
public static void test(int... a) {
    // ...
}

public static void main(String[] args) {
    int[] arr = {1, 2, 3};

    // 普通方法的调用,只有下面一种形式
    fun(arr);

    // 可变参数列表方法的调用,下面形式都可以
    test();                 // 不传参
    test(1);                // 传递1个元素
    test(1, 2, 3, 4);       // 传递多个元素
    test(arr);              // 传递数组
}


```
结论:可变参数列表本质上是一个数组,方法中使用可变参数列表,比用数组作参数功能更强大。

案例展示:
```java
 

import java.util.Arrays;

public class Test091_Basic {
    public static void main(String[] args) {
        //一维长度2,代表这个二维数组里面包含2个元素,每个元素都是一个一维数组
        //二维长度3,代表这个二维数组中元素,类型都是int[3]的一维数组,存放3个int数据
        int[][] arr = new int[2][3];
        System.out.println(arr);

        //二维数组的每个元素值(第一维), 对应的是一维数组的内存地址值
        System.out.println(arr[0]); //[I@15db9742
        System.out.println(arr[1]); //[I@6d06d69c

        System.out.println("--------------");

        //第二种定义格式
        int[][] arr2 = new int[2][];

        //输出arr2中2个元素值,默认为null、null
        System.out.println(Arrays.toString(arr2));

        //给二维数组的每个元素赋值
        //arr[0] = new int[2];
        //arr[1] = new int[3];
    }
}

```
注意事项:可变参数和普通参数共存时,可变参数必须放到最后一个参数的位置,且只能出现1次。

总结

  1. 可变参数列表 可以接受 0-n个参数
  2. 可变参数列表还可以接受数组
  3. 可变参数列表必须放在函数参数列表的最右端,且只能出现1

9. foreach循环

foreach关键字可以简化数组遍历的写法,具体见下面案例:

//加强for循环:针对数组、集合遍历,JDK提供的简单写法
public static void outArray(int[] arr) {
    //遍历数组常规写法
    for(int i = 0; i < arr.length; i++) { System.out.print(arr[i] + "\t"); }
    System.out.println();

    //加强for循环
    for (int v : arr) { System.out.print(v + "\t"); }
    System.out.println();
}

System.out.println("---------------");

10. 二维数组

如果把普通的数组(一维数组),看作一个小盒子的话,盒子里面可以存放很多数据,那么二维数组就是像一个大点的盒子,里面可以存放很多小盒子(一维数组)。

10.1 定义格式

二维数组固定定义格式有2种,具体如下:

格式1:
数据类型[][] 数组名 = new 数据类型[一维长度m][二维长度n];
m:表示二维数组的元素数量,即可以存放多少个一维数组
n:表示每一个一维数组,可以存放多少个元素

格式2:
数据类型[][] 数组名 = new 数据类型[一维长度][];

案例展示:

import java.util.Arrays;

public class Test091_Basic {
    public static void main(String[] args) {
        //一维长度2,代表这个二维数组里面包含2个元素,每个元素都是一个一维数组
        //二维长度3,代表这个二维数组中元素,类型都是int[3]的一维数组,存放3个int数据
        int[][] arr = new int[2][3];
        System.out.println(arr);

        //二维数组的每个元素值(第一维), 对应的是一维数组的内存地址值
        System.out.println(arr[0]); //[I@15db9742
        System.out.println(arr[1]); //[I@6d06d69c

        System.out.println("--------------");

        //第二种定义格式
        int[][] arr2 = new int[2][];

        //输出arr2中2个元素值,默认为null、null
        System.out.println(Arrays.toString(arr2));

        //给二维数组的每个元素赋值
        //arr[0] = new int[2];
        //arr[1] = new int[3];
    }
}

10.2 内存结构

一维数组内存结构:

二维数组内存结构:

可以把二维数组看成一个一维数组,数组的每个元素对应的内存区域中,存放的是一维数组引用值。

10.3 元素访问

二维数组中元素的访问和赋值,也是通过数组下标实现的。

书写格式:

二维数组名[一维下标m][二维下标n];

例如:int arr[2][3];

注意:m、n的取值都是 [0,length-1] ,注意不要越界,否则会出现异常 ArrayIndexOutOfBoundsException 。

案例展示:

public class Test093_Access {
    public static void main(String[] args) {
        // 数据类型[][] 变量名 = new 数据类型[m][n];
        int[][] arr = new int[2][3];

        //获取二维数组元素值并输出
        System.out.println(arr[0][0]);
        System.out.println(arr[1][1]);

        System.out.println("-----------");

        // 向二维数组中存储元素
        arr[0][0] = 11; arr[0][1] = 22; arr[0][2] = 33;
        arr[1][0] = 11; arr[1][1] = 22; arr[1][2] = 33;

        //arr[2][0] = 3; //数组越界异常

        // 3. 遍历二维数组,获取所有元素,累加求和
        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();
        }
    }
}

10.4初始化

二维数组的静态初始化,有点类似一维数组的初始化,具体格式如下:完整格式 :

数据类型[][] 数组名 = new 数据类型[][]{ {元素1, 元素2...} , {元素1, 元素2...}, ...};

简化格式 :

数据类型[][] 数组名 = { {元素1, 元素2...} , {元素1, 元素2...}
...};

案例:

```java
 

public class Test094_Init {
    //封装二维数组遍历方法
    public static void outArray(int[][] arr) {
        // 遍历二维数组,获取所有元素,累加求和
        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();
        }
    }

    public static void main(String[] args) {
        //第一种:完整格式
        int[][] arr1 = new int[][]{{1,2,3},{4,5}};
        outArray(arr1);

        System.out.println("----------");

        //第二种:简化格式
        int[][] arr2 = {{11, 22, 33}, {44, 55}};
        outArray(arr2);

        System.out.println("----------");

        //第三种:建议从内存角度理解
        int[] arr3 = {11, 33};
        int[] arr4 = {44, 55, 66};
        int[][] array = {arr3, arr4};
        outArray(array);
    }
}


```

10.5综合案例

案例1:二维数组元素遍历

```java
//上面案例已经使用
public static void outArray(int[][] arr) {
    // 遍历二维数组,获取所有元素,累加求和
    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();
    }
}

```

案例2:二维数组元素求和

```java

public class Test095_Sum {
    public static void main(String[] args) {
        // 1. 定义求和变量,准备记录最终累加结果
        int sum = 0;

        // 2. 使用二维数组来存储数据
        int[][] arr = new int[3][];
        arr[0] = new int[]{10};
        arr[1] = new int[]{20,20};
        arr[2] = new int[]{30,30,30};

        // 3. 遍历二维数组,获取所有元素,累加求和
        for (int i = 0; i < arr.length; i++) {
            for(int j = 0; j < arr[i].length; j++){
                sum += arr[i][j];
            }
        }

        // 4. 输出最终结果
        System.out.println(sum);
    }

}
```

注意事项:

可以把上述案例中二维数组理解为一栋大厦,共有3层楼,每层楼有多个房间,每个房间可以存放一个int数据,现在每个房间的默认值都是0。

11.扩展案例

11.1选择排序

选择排序(Selection Sort)的原理有点类似插入排序,也分已排序区间和未排序区间。但是选择排序每次会从未排序区间中找到最小的元素,将其放到已排序区间的末尾,最终完成排序。

选择排序算法描述:

1.初始状态:无序区间为Arr[0...n],有序区间为空;
2.第i==1趟排序开始,从无序区中选出最小的元素Arr[k],将它与无序区的第1个元素交换,从而得到有序区间Arr[0...i-1],无序区间Arr[i...n];
3.继续后面第i趟排序(i=2,3...n-1),重复上面第二步过程;
4.第n-1趟排序结束,数组排序完成。选择排序过程如下图:

源码实现:```java

import java.util.Arrays;

public class Test07_SelectSort {
    public static void main(String[] args) {
        //准备一个int数组
        int[] array = {5, 2, 6, 5, 9, 0, 3};
        System.out.println("排序前: "+ Arrays.toString(array));
        //插入排序
        selectionSort(array);
        //输出排序结果
        System.out.println("排序后: "+ Arrays.toString(array)); 
    }
    public static void selectionSort(int[] arr) {
        int len = arr.length; 
        if(len <= 1)
            return;
        //外层循环控制总体排序次数
        for(int i = 0; i < len-1; i++) { 
            int minIndex = i;
            //内层循环找到当前无序列表中最小下标
            for(int j = i + 1; j < len; j++) { 
                if(arr[minIndex] > arr[j]) {
                    minIndex = j;
                }
            }
            //将无需列表中最小值添加到有序列表最后位置
            if(minIndex != i) {
                arr[minIndex] = arr[minIndex] ^ arr[i]; 
                arr[i] = arr[minIndex] ^ arr[i]; 
                arr[minIndex] = arr[minIndex] ^ arr[i];
            }
        }
    }
}


```

11.2插入排序

插入排序(Insertion Sort),一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。

插入排序算法描述:

1.将数组分成两部分,已排序、未排序区间,初始情况下,已排序区间只有一个元素,即数组第一个元素;
2.取未排序区间中第一个元素,插入到已排序区间中合适的位置,这样子就得到了一个更大的已排序区间;
3.重复这个过程,直到未排序区间中元素为空,算法结束。插入排序过程见下图:

源码实现:

```java

import java.util.Arrays;

public class Test07_InsertSort {
    public static void main(String[] args) {
        //准备一个int数组
        int[] array = {5, 2, 6, 5, 9, 0, 3};
        System.out.println("排序前: "+ Arrays.toString(array));
        //插入排序
        insertionSort(array);
        //输出排序结果
        System.out.println("排序后: "+ Arrays.toString(array));
    }
    public static void insertionSort(int[] arr) { 
        int len = arr.length;
        if(len <= 1) { 
            return;
        }
        //外层循环控制总体循环次数
        for(int i = 1; i < len; i++) {
            //内层循环做的事情:将无序列表中第一个元素插入到有序列表中合适位置
            int value = arr[i];
            //获取有序列表中最后一个元素下标
            int j = i - 1;
            for(; j >= 0; j--) {
                if(value < arr[j]) {
                    arr[j+1] = arr[j];
                }else {
                    break;
                }
            }
            //将需要插入的元素放置到合适位置
            arr[j+1] = value;
        }
    }
}


```

11.3希尔排序

希尔(shell)排序是Donald Shell于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。

希尔排序对直接插入排序改进的着眼点:若待排序序列中元素基本有序时,直接插入排序的效率可以大大提高;如果待排序序列中元素数量较小时,直接插入排序效率很高。

希尔排序算法思路:将整个待排序序列分割成若干个子序列,在子序列内部分别进行直接插入排序,等到整个序列基本有序时,再对全体成员进行直接插入排序!

待解决问题:如何分割子序列,才能保证最终能得到基本有序?子序列内部如何进行直接插入排序?

分割方案:1.将有n个元素的数组分成n/2个数字序列,第i个元素和第i+n/2,i+n/2*m...个元素为一组;2.对一组数列进行简单插入排序;3.然后,调整增量为n/4,从而得到新的几组数列,再次排序;4.不断重复上述过程,直到增量为1,shell排序完全转化成简单插入排序,完成该趟排序,则数组排序成功。

希尔排序流程:

具体源码实现:

```java

import java.util.Arrays;

public class Test07_ShellSort {
    public static void main(String[] args) {
        //准备一个int数组
        int[] array = {5, 2, 6, 5, 9, 0, 3};
        System.out.println("排序前: "+ Arrays.toString(array));
        //shell排序
        shellSort(array);
        //输出排序结果
        System.out.println("排序后: "+ Arrays.toString(array));
    }
    //由简单插入排序改造得到shell排序
    public static void shellSort(int[] array) {
        int len = array.length; 
        if(len <= 1)
            return;
        //设置初始增量
        int gap = len / 2;
        //由增量控制整体排序次数
        while(gap > 0) {
            //插入排序改造
            for(int i = gap; i < len; i++) {
                //记录要插入的值
                int value = array[i];
                //有序序列的最后一个元素下标
                int j = i - gap;
                for(; j >= 0; j -= gap) {
                    if(value < array[j]) {
                        array[j + gap] = array[j];
                    }else {
                        break;
                    }
                }
                array[j+gap] = value; 
            }
            gap = gap / 2; 
        }
    }
}


```

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时肆 知还

你的鼓励将是我创作的最大动力,

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值