003-数组

三 数组

3.1 概述

  • 什么是数组?

    是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过索引的方式对这些数据进行统一管理。数组包含数组名下标(或索引)元素数组的长度

3.1.1 数组的分类

  • 按照元素类型分:
    1. 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
    2. 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)。

3.1.2 数组的特点

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。

  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。

  • 数组中的元素在内存中是依次紧密排列的,有序的。

  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改

  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。

  • 数组名中引用的是这块连续空间的首地址

3.2 一维数组

3.2.1 声明

// 推荐
元素的数据类型[] 一维数组名;
// 不推荐
元素的数据类型 一维数组名[];

// 举例
int[] arr1;
int arr2[];
double[] arr3;
String[] arr4; // 引用类型变量数组

数组的声明,需要明确:

  1. 数组的维度:在 Java 中数组的符号是[ ],[ ]表示一维,[ ][ ]表示二维。
  2. 数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的 Java 的数据类型。例如:int、String、Student(学生类) 等。
  3. 数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。

注意:Java 语言中声明数组时不能指定其长度(或数组中元素的个数)。

// 例如:
int a[5]; // 非法

3.2.2 初始化

3.2.2.1 静态初始化
  • 数组变量的初始化数组元素的赋值操作同时进行,即为静态初始化

  • 静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。

  • 一维数组声明和静态初始化

    • 格式 1:

      数据类型[] 数组名 = new 数据类型[]{元素 1,元素 2,元素 3,...}; 
      // 或  
      数据类型[] 数组名; 
      数组名 = new 数据类型[]{元素 1,元素 2,元素 3,...}; 
      // new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用 new 创建数组实体。
      
      // 例如,定义存储 1,2,3,4,5 整数的数组容器。
      int[] arr1 = new int[]{1,2,3,4,5}; // 正确
      // 或
      int[] arr2;
      arr2 = new int[]{1,2,3,4,5}; // 正确
      
    • 格式 2:

      数据类型[] 数组名 = {元素 1,元素 2,元素 3...};
      // 必须在一个语句中完成,不能分成两个语句写
      
      // 例如,定义存储 1,2,3,4,5 整数的数组容器
      int[] arr3 = {1,2,3,4,5}; // 正确
      // 或
      int[] arr4;
      arr4 = {1,2,3,4,5}; // 错误
      
3.2.2.2 动态初始化
  • 数组变量的初始化数组元素的赋值操作分开进行,即为动态初始化

  • 动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独逐个赋值。

  • 一维数组声明和动态初始化

    • 格式:

      数据类型[] 数组名 = new 数据类型[长度]; 
      // 或 
      数据类型[] 数组名; 
      数组名 = new 数据类型[长度]; 
      // [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
      // 注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水 杯,总容量就是2升是固定的。
      
      // 为元素赋新值
      数组名[索引/下标] =;
      
      // 正确写法
      int[] arr1 = new int[5];
      int[] arr2;
      arr2 = new int[5];
      
      // 错误写法,动态初始化和静态初始化是相互
      int[] arr3 = new int[5]{1,2,3,4,5}; // 错误,后面有{}指定元素列表,就不需要在[]中指定元素个数了。
      

3.2.3 使用

3.2.3.1 数组的长度
// 格式
 数组名.length
// 举例
int[] arr = {1, 2, 3, 4, 5};
System.out.println("数组arr的长度:" + arr.length); // 数组arr的长度:5
3.2.3.2 数组元素的引用
  • 每一个存储到数组的元素,都会自动的拥有一个编号,从 0 开始,这个自动编号,称为数组索引(index)或下标,可以通过数组的索引/下标访问到数组中的元素。

  • Java 中数组的下标从0开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1] 。

  • 数组元素下标可以是整型常量整型表达式。如 a[3] , b[i] , c[6*i]。

 // 格式
 数组名[索引/下标]
// 举例
String[] arr = {"kerwin", "tiecui", "gangdan"};
System.out.println("数组arr的第一个元素:" + arr[0]); // 数组arr的第一个元素:kerwin
System.out.println("数组arr的第二个元素:" + arr[1]); // 数组arr的第二个元素:tiecui
System.out.println("数组arr的第三个元素:" + arr[2]); // 数组arr的第三个元素:gangdan
3.2.3.3 数组的遍历
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.length; i++) {
    System.out.println("数组arr第" + (i + 1) + "个元素为:" + arr[i]);
}

运行结果在这里插入图片描述

3.2.3.4 数组元素的默认值

数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。

int[] arr1 = new int[5];
System.out.println("索引为1的元素对应的默认初始化值为:" + arr1[1]); // 索引为1的元素对应的默认初始化值为:0
  • 对于基本数据类型而言,默认初始化值各有不同。
  • 对于引用数据类型而言,默认初始化值为 null(注意与 0 不同)。

3.2.4 内存分析

3.2.4.1 Java 虚拟机的内存划分

为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特 定的处理数据方式和内存管理方式。

区域名称作用
栈内存用于存储正在执行的每个 Java 方法的局部变量表等。局部变量表存放了编译期可知长度
的各种基本数据类型、对象引用,方法执行完,自动释放。
堆内存存储对象(包括数组对象),new 来创建的,都存储在堆内存。
方法区存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。
本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址
3.2.4.2 两个一维数组内存图

3.2.4.3 两个变量指向一个一维数组内存图

4.3 多维数组

3.3.1 二维数组声明

// 推荐

元素的数据类型[][] 二维数组的名;

// 不推荐

元素的数据类型 二维数组名[][];

// 不推荐

元素的数据类型[] 二维数组名[];

// 举例
int[][] arr1;
String[][] arr2;
// 面试题
int[] x, y[];
// 一维数组:int[] x 或者 int x[]
// 二维数组:int[][] y 或者 int[] y[] 或者 int y[][]

3.3.2 二维数组初始化

3.3.2.1 静态初始化
  • 格式1:
 数据类型[][] 数组名 = new 数据类型[][]{{元素 11,元素 12,...},{{元素 21,元素 22,...},{{元素 31,元素 32,...},...}; 
 // 或  
 数据类型[][] 数组名; 
 数组名 = new 数据类型[][]{{元素 11,元素 12,...},{{元素 21,元素 22,...},{{元素 31,元素 32,...},...}; 
int[][] arr1 = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}}; // 正确

int[][] arr2;
arr2 = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}}; // 正确

int[][] arr3;
arr3 = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}}; // 错误,静态初始化右边 new 数据类型[][]中不能写数字
  • 格式2
数据类型[][] 数组名 = {{元素 11,元素 12,...},{{元素 21,元素 22,...},{{元素 31,元素 32,...},...}; 
// 举例
int[][] arr4 = {{1,2,3},{4,5,6},{7,8,9,10}};  // 正确
3.3.2.2 动态初始化
  • 如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。
  • 格式1:规则二维表,每一行的列数是相同的
 //(1)确定行数和列数
 数据类型[][] 二维数组名 = new 数据类型[m][n];
 /*
 	其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
 	其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格
 	此时创建完数组,行数、列数确定,而且元素也都有默认值
 */
 //(2)再为元素赋新值
 二维数组名[行下标][列下标] =;
// 举例
int[][] arr = new int[3][2];
// 1. 二维数组中有 3 个一维数组
// 2. 每个一维数组都是默认初始化值 0 (注意:区别于格式 2)
// 3. 每一个一维数组中有 2 个元素
// 4. 一维数组的名称分别为 arr[0], arr[1], arr[2]
  • 格式2:不规则二维表,每一行的列数不一样
 //(1)先确定总行数
 数据类型[][] 二维数组名 = new 数据类型[总行数][];
 // 此时只是确定了总行数,每一行里面现在是 null
 
 //(2)再确定每一行的列数,创建每一行的一维数组
 二维数组名[行下标] = new 数据类型[该行的总列数];
 // 此时已经 new 完的行的元素就有默认值了,没有 new 的行还是 null
 
 //(3)再为元素赋值
 二维数组名[行下标][列下标] =;
// 举例
int[][] arr = new int[3][];
// 1. 二维数组中有 3 个一维数组。
// 2. 每个一维数组都是默认初始化值 null (注意:区别于格式 1)
// 3. 可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
// 4. 注:int[][]arr = new int[][3]; //非法

3.3.3 二维数组使用

3.3.3.1 二维数组的长度和角标
  • 二维数组的长度/行数:

    二维数组名.length
    
  • 二维数组的某一行:

    二维数组名[行下标]
    // 它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。 
    
  • 某一行的列数:

    二维数组名[行下标].length
    // 因为二维数组的每一行是一个一维数组。 
    
  • 某一个元素:

    二维数组名[行下标][列下标]
    // 即先确定行,再确定列。
    
int[][] scores = {
    {85, 85, 75},
    {99, 96, 74, 72, 75},
    {52, 42, 56, 75}
};
System.out.println("一共有" + scores.length + "组成绩"); // 一共有3组成绩
System.out.println("第 1 组有" + scores[0].length + "个学员"); // 第 1 组有3个学员
System.out.println("第 2 组有" + scores[1].length + "个学员"); // 第 2 组有5个学员
System.out.println("第 3 组有" + scores[2].length + "个学员"); // 第 3 组有4个学员
3.3.3.2 二维数组遍历
int[][] scores = {
    {85, 85, 75},
    {99, 96, 74, 72, 75},
    {52, 42, 56, 75}
};
for (int i = 0; i < scores.length; i++) {
    for (int j = 0; j < scores[i].length; j++) {
        System.out.println(scores[i][j]);
    }
}

运行结果

3.3.4 二维数组内存分析

举例一

举例二

3.4 Arrays 工具类

3.4.1 打印数组 toString()

方法描述
static String toString(int[] a)字符串表示形式由数组的元素列表组成,括在方括号(“[ ]”)中。
相邻元素用字符 ", "(逗号加空格)分隔。形式为: [元素 1,元素 2,元素 3, …]。
static String toString(Object[] a)元素将自动调用自己从 Object 继承的 toString 方法将对象转为字符串进行拼接,
如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
public class ArrayElementCite {
    public static void main(String[] args) {
        String[] arr = {"kerwin", "tiecui", "gangdan"};
        System.out.println(arr); // [Ljava.lang.String;@6d311334
        System.out.println(Arrays.toString(arr)); // [kerwin, tiecui, gangdan]
    }
}

3.4.2 数组排序 sort()

方法描述
static void sort(int[] a)将 a 数组按照从小到大进行排序。
static void sort(int[] a, int fromIndex, int toIndex)将 a 数组的 [fromIndex, toIndex) 部分按照升序排列。
static void sort(Object[] a)根据元素的自然顺序对指定对象数组按升序进行排序。
int[] arr = {6, 10, 3, 8, 5};
Arrays.sort(arr);
System.out.println("升序后的数组内容如下:");
for (int j = 0; j < arr.length; j++) {
    System.out.print(arr[j] + "\t");
}

System.out.println("\r\n");

int[] arr1 = {6, 10, 3, 8, 5};
Arrays.sort(arr1, Collections.reverseOrder()); // Collections.reverseOrder()
System.out.println("降序后的数组内容如下:");
for (int j = 0; j < arr1.length; j++) {
    System.out.print(arr1[j] + "\t");
}

运行结果

在这里插入图片描述

3.4.2.1 数组中存储对象排序方式一
  • 使用下面这个 sort 方法,创建 Comparator 比较器接口的匿名内部类对象,然后自己制定比较规则。

    static <T> void sort(T[] arr, Comparator<? super T> c) // 对数组进行排序
    
  • 自定义升序排序规则时,需要遵循的官方约定如下:

    左边对象 > 右边对象则返回正整数

    左边对象 < 右边对象则返回负整数

    左边对象 = 右边对象则返回0整数

public class Demo01 {
    public static void main(String[] args) {
        Student[] stuArr = new Student[4];
        stuArr[0] = new Student("蜘蛛精", 169.5, 30);
        stuArr[1] = new Student("牛魔王", 190.8, 88);
        stuArr[2] = new Student("紫霞仙子", 163.8, 18);
        stuArr[3] = new Student("至尊宝", 167.5, 24);

        // 方式一
        Arrays.sort(stuArr, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                // return Double.compare(o1.getHeight(), o2.getHeight()); // 按身高升序排序
                return Double.compare(o2.getHeight(), o1.getHeight()); // 按身高降序排序
            }
        });
        for (int i = 0; i < stuArr.length; i++) {
            System.out.println(stuArr[i]);
        }
        /*
        	输出结果:
            Student{name='牛魔王', height=190.8, age=88}
            Student{name='蜘蛛精', height=169.5, age=30}
            Student{name='至尊宝', height=167.5, age=24}
            Student{name='紫霞仙子', height=163.8, age=18}
        */
    }

}

class Student {
    private String name;
    private double height;
    private int age;

    // 有参和无参构造

    // setXxx()和getXxx()

    // toString()
}
3.4.2.2 数组中存储对象排序方式二

让该对象的类实现 Comparable 接口,重写 compareTo 方法,然后自己制定比较规则。

public class Demo01 {
    public static void main(String[] args) {
        Student[] stuArr = new Student[4];
        stuArr[0] = new Student("蜘蛛精", 169.5, 30);
        stuArr[1] = new Student("牛魔王", 190.8, 88);
        stuArr[2] = new Student("紫霞仙子", 163.8, 18);
        stuArr[3] = new Student("至尊宝", 167.5, 24);

        // 方式二
        Arrays.sort(stuArr);
        for (int i = 0; i < stuArr.length; i++) {
            System.out.println(stuArr[i]);
        }
        /*
        	输出结果:
			Student{name='紫霞仙子', height=163.8, age=18}
			Student{name='至尊宝', height=167.5, age=24}
			Student{name='蜘蛛精', height=169.5, age=30}
			Student{name='牛魔王', height=190.8, age=88}
        */
    }

}

class Student implements Comparable<Student> {
    private String name;
    private double height;
    private int age;

    // 有参和无参构造

    // setXxx()和getXxx()

    // toString()

    // 重写 compareTo 方法
    @Override
    public int compareTo(Student o) {
        // 按照升高升序排序
        return Double.compare(this.height, o.getHeight());
    }
}

3.4.3 数组元素的二分查找 binarySearch()

方法描述
static int binarySearch(int[] a, int key)
static int binarySearch(Object[] a, Object key)
先对数组进行排序,再去数组中查找 key 是否存在,
如果存在返回第一次找到的下标不存在返回负数
public static void main(String[] args) {
    int[] arr = {1, 2, 323, 23, 543, 12, 59};
    // 使用二分法查找,必须先对数组进行排序
    Arrays.sort(arr);
    System.out.println("排序后的数组:" + Arrays.toString(arr)); // [1, 2, 12, 23, 59, 323, 543]
    // 返回排序后新的索引位置,若未找到返回负数
    System.out.println("该元素的索引:" + Arrays.binarySearch(arr, 12)); // 2
}

4.4.4 数组的复制 copyOf() / copyOfRange()

方法描述
static int[ ] copyOf(int[ ] original, int newLength)根据 original 原数组复制一个长度为 newLength 的新数组并返回。
static int[ ] copyOfRange(int[ ] original, int from, int to)复制 original 原数组,from 为开始下标(包含),to 为结束下标(不包含),构成新数组并返回。
public static void main(String[] args) {
    int[] arr1 = {23, 34, 345, 234};
    int[] arr2 = new int[5];//默认值0
    System.out.println("拷贝前:" + arr2);
    arr2 = Arrays.copyOf(arr1, 5);  // 开辟新内存空间
    System.out.println("拷贝后:" + arr2);
    System.out.println(Arrays.toString(arr2));

    int[] arr3 = {1, 2, 3, 4, 5, 6};
    System.out.println(arr3); // [I@214c265e
    // 复制整个数组
    int[] allArr = Arrays.copyOfRange(arr3, 0, 6);
    System.out.println(allArr); // [I@448139f0 -> 开辟新内存空间
    System.out.println(Arrays.toString(allArr)); // [1, 2, 3, 4, 5, 6]
    // 复制部分数组
    int[] partArr = Arrays.copyOfRange(arr3, 1, 4);
    System.out.println(Arrays.toString(partArr)); // [2, 3, 4]

}

4.4.5 比较两个数组是否相等 equals()

方法描述
static boolean equals(int[ ] a, int[ ] a2)比较两个数组的长度、元素是否完全相同。
static boolean equals(Object[ ] a, Object[ ] a2)比较两个数组的长度、元素是否完全相同。
public class Demo01 {
    public static void main(String[] args) {
        int[] arr1 = {23, 34, 345, 234};
        int[] arr2 = {23, 34, 345, 234};
        // Arrays.equals():两个数组以相同的顺序包含相同的元素
        System.out.println(Arrays.equals(arr1, arr2)); // true
        // Object中的equals方法比较两个数组的地址
        System.out.println(arr1.equals(arr2)); // false
        // ==:比较两个数组的地址
        System.out.println(arr1 == arr2); // false
    }
}

4.4.6 填充数组 fill()

方法描述
static void fill(int[] a, int val)用 val 值填充整个 a 数组。
static void fill(int[] a, int fromIndex, int toIndex, int val)将 a 数组 [fromIndex,toIndex) 部分填充为 val 值。
public static void main(String[] args) {
    int[] arr = {1, 2, 323, 23, 543, 12, 59};
    // 将2到4索引的元素替换为100,包含2但不包含4
    Arrays.fill(arr, 2, 4, 100);
    System.out.println(Arrays.toString(arr)); // [1, 2, 100, 100, 543, 12, 59]
    // 将所有的元素都换为55
    Arrays.fill(arr, 55);
    System.out.println(Arrays.toString(arr)); // [55, 55, 55, 55, 55, 55, 55]
}

4.4.7 数组原数据改为新数据 setAll()

方法说明
static void setAll(double[] array, IntToDoubleFunction generator)把数组中的原数据改为新数据
public class Test {
    public static void main(String[] args) {

        double[] arr = {90.0, 85.8, 97.4};
        
        Arrays.setAll(arr, new IntToDoubleFunction() {
            /**
             *
             * @param index 对应数组 arr 的索引
             * @return
             */
            @Override
            public double applyAsDouble(int index) {
                return arr[index] + 1;
            }
        });
        System.out.println(Arrays.toString(arr)); // [91.0, 86.8, 98.4]
    }
}

3.5 数组常用算法

3.5.1 数组元素查找

3.5.1.1 二分查找

前提条件:数组中的数据必须是有序的。

public class BinarySearch {
    public static void main(String[] args) {
        int[] arr = {7, 23, 79, 81, 103, 127, 131, 147};

        System.out.println(getIndex(arr, 79)); // 2
        System.out.println(getIndex(arr, 101)); // -1
    }

    // 使用while循环控制二分查询(条件是左边位置<=右边位置)
    public static int getIndex(int[] arr, int target) {
        // 循环内部获取中间元素索引
        // 定义变量记录左边和右边位置对应的索引
        int left = 0, right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] == target) {
                // 判断当前要找的元素如果等于中间元素,返回当前中间元素索引
                return mid;
            } else if (arr[mid] > target) {
                // 判断当前要找的元素如果小于中间元素,右边位置=中间索引-1
                right = mid - 1;
            } else if (arr[mid] < target) {
                // 判断当前要找的元素如果大于中间元素,左边位置=中间索引+1                
                left = mid + 1;
            }
        }
        return -1;
    }
}

3.5.2 数组元素排序

3.5.2.1 排序算法概述

稳定性:若两个记录 A 和 B 的关键字值相等,但排序后 A、B 的先后次序保持不变,则称这种排序算法是稳定的

十大内部排序算法常见时间复杂度所消耗的时间从小到大排序:

O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

3.5.2.2 冒泡排序

排序思想:

  1. 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后, 最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。

动态演示:https://visualgo.net/zh/sorting

public static void main(String[] args) {
    int[] arr = {23, 8, 36, 12, 2, 8};
    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]) {
                int temp = arr[j + 1];
                arr[j + 1] = arr[j];
                arr[j] = temp;
            }
        }
        System.out.println("第" + (i + 1) + "轮排序结果为:" + Arrays.toString(arr));
    }
}
/*
    运行结果:
        第1轮排序结果为:[23, 12, 2, 8, 36, 55]
        第2轮排序结果为:[12, 2, 8, 23, 36, 55]
        第3轮排序结果为:[2, 8, 12, 23, 36, 55]
        第4轮排序结果为:[2, 8, 12, 23, 36, 55]
        第5轮排序结果为:[2, 8, 12, 23, 36, 55]
*/

3.5.2.3 快速排序

排序思想:

  1. 从数列中选出第一个元素,称为"基准"(pivot)。
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列重复步骤2进行排序。
  4. 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

动态演示:https://visualgo.net/zh/sorting

第一轮初始状态

第一轮操作

第二轮初始状态

第二轮操作

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        quickSort(arr, 0, arr.length - 1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " "); // 1 2 3 4 5 6 7 8 9 10
        }
    }

    public static void quickSort(int[] arr, int low, int high) {
        int i, j, temp, t;
        if (low > high) {
            return;
        }
        i = low;
        j = high;
        //temp就是基准位
        temp = arr[low];

        while (i < j) {
            // 先看右边,依次往左递减
            while (temp <= arr[j] && i < j) {
                j--;
            }
            // 再看左边,依次往右递增
            while (temp >= arr[i] && i < j) {
                i++;
            }
            // 如果满足条件则交换
            if (i < j) {
                t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
        }

        // 最后将基准为与i和j相等位置的数字交换
        arr[low] = arr[i];
        arr[i] = temp;
        
        // 递归调用左半数组
        quickSort(arr, low, j - 1);
        // 递归调用右半数组
        quickSort(arr, j + 1, high);
    }
}

第一轮运行过程:

3.5.2.4 选择排序
  • 什么是选择排序?

    答:每轮选择当前位置,开始找出后面最小值与该位置交换。

  • 选择排序的关键:

    • 确定总共需要选择几轮: 数组的长度-1
    • 控制每轮从当前位置为基准,与后面元素进行比较。
public static void main(String[] args) {
    int[] arr = {5, 1, 3, 2, 4};
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[i] > arr[j]) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        System.out.println("第" + (i + 1) + "轮排序结果为:" + Arrays.toString(arr));
    }
}
/*
    运行结果:
        第1轮排序结果为:[1, 5, 3, 2, 4]
        第2轮排序结果为:[1, 2, 5, 3, 4]
        第3轮排序结果为:[1, 2, 3, 5, 4]
        第4轮排序结果为:[1, 2, 3, 4, 5]
*/

在这里插入图片描述

3.6 数组中的常见异常

  1. 空指针异常 NullPointerException
  2. 角标越界异常 ArrayIndexOutOfBoundsException
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值