文章目录
- 数组方法结合底层逻辑代码分析
- 1. 数组相关操作
- 1.1 指定数组找出数组中最大值下标位置
- 1.2 指定数组找出数组中最小值下标位置
- 1.3 指定数组中最大元素个数
- 1.4 指定数组中最大值元素所有对应下标位置,要求存储到另一个数组中
- 1.5 找出指定元素在指定数值中第一次出现的下标位置 【 重要 -1 在源码中的思想 】
- 1.6 找出指定元素在指定数组中最后一次出现的下标位置
- 1.7 从数组中指定下标开始,到指定下标结束,获取数据存储到新数组中 【重要思想,条件整合与边界思想】
- 1.8 复制指定数组数据内容到新数组 【保护数据】
- 1.9 指定数组内容逆序
- 1.10 在数组指定下标位置添加元素 【选择合适的返回值类型】
- 1.11 删除数组中指定下标元素内容
- 1.12 选择排序算法
- 1.13 冒泡排序
数组方法结合底层逻辑代码分析
1. 数组相关操作
1.1 指定数组找出数组中最大值下标位置
int[] arr = {1, 3, 5, 7, 9, 21, 4, 6, 8, 10};
找出数值中最大值下标位置:
maxIndex = 5;
/**
* 找出数值中最大值下标位置:
* @param arr 目标数组 int 类型数组
* @return 返回数组中最大值的下标 int 类型
*/
public static int getMaxIndex(int[] arr) {
// 假设 maxIndex = 0 为最大下标位置
int maxIndex = 0;
// 从下标为 1 开始遍历
for (int i = 1; i < arr.length; i++) {
// 遇到比 maxIndex 对应数组元素大的,将 i 赋值给 maxIndex
if (arr[maxIndex] < arr[i]){
maxIndex = i;
}
}
// 返回最大值下标
return maxIndex;
}
1.2 指定数组找出数组中最小值下标位置
int[] arr = {1, 3, 5, 7, 9, 21, 4, 6, 8, 10};
找出数组中最小值下标位置:
minIndex = 0;
/**
* 找出数组中最小值下标位置
* @param arr 目标数组 int 类型数组
* @return 返回数组中最小元素的下标 int 类型
*/
public static int getMinIndex(int[] arr) {
// 假设下标为 0 的元素是数组中最小值
int minIndex = 0;
// 从下标为 1 的元素开始遍历
for (int i = 1; i < arr.length; i++) {
// 遇到比 minIndex 对应数组元素小的,将 i 赋值给 minIndex
if (arr[minIndex] > arr[i]){
minIndex = i;
}
}
// 返回最小值对应下标
return minIndex;
}
1.3 指定数组中最大元素个数
int[] arr = {0, 21, 5, 21, 9, 21, 4, 6, 21, 10};
最大值个数
maxValueCount = 5;
/**
* 找出数组中最大值个数
* @param arr int 类型目标数组
* @return int 类型返回数组中最大值的个数
*/
public static int getMaxValueCount(int[] arr) {
// 假设数组中下标为 0 的元素是最大值
int maxValeue = arr[0];
// count 用于记录最大值的个数
int count = 1;
// 从下标 = 1 开始遍历
for (int i = 1; i < arr.length; i++) {
// 如果遇到比假设最大值大的数 将当前元素赋值给 maxValue 并且 count 重置为 1
if (maxValeue < arr[i]){
maxValeue = arr[i];
count = 1;
}else if (maxValeue == arr[i]){
// 如果遇到跟假设最大值相等的数 count 自增
count += 1;
}
}
return count;
}
1.4 指定数组中最大值元素所有对应下标位置,要求存储到另一个数组中
int[] arr = {21, 21, 5, 21, 9, 21, 4, 6, 21, 10};
其他数组:
int[] indexArray = {0, 1, 3, 5, 8};核心知识点
- 尾插法
- 空间效率和时间效率问题
int[] arr = {21, 21, 5, 21, 9, 21, 4, 6, 21, 10};
其他数组:
int[] indexArray = {0, 1, 3, 5, 8};
// 【方案一】 存储下标的数组容量与源数组容量一致 (牺牲空间换时间)
/**
* 指定数组中最大值元素所有对应下标位置,存储到另一个数组中
* @param arr 目标数组 int 类型
* @return 返回目标数组对应最大元素的下标值数组 int 类型
*/
public static int[] getMaxIndexArrOf(int[] arr) {
/*
常规思路 1. 找出最大值
2. 创建新数组
3. 将最大值元素下标存入新数组中
*/
// 假设数组最大值 maxValue = arr[0];
int maxValue = arr[0];
for (int i = 1; i < arr.length; i++) {
if (maxValue < arr[i]){
maxValue = arr[i];
}
}
// 创建新数组
int[] maxIndexArr = new int[arr.length];
/*
尾插法使用的计数器 1. 用于记录数组有效元素个数
2. 用于记录下次存入数据的下标位置
*/
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (maxValue == arr[i]){
// 找到与最大值相等的元素将元素对应下标存入新数组中然后 count++
maxIndexArr[count++] = i;
}
}
return maxIndexArr;
}
// 【方案二】 存储下标的数组容量与源数组容量一致 单循环形式
/**
* 指定数组中最大值元素所有对应下标位置,存储到另一个数组中
* @param arr 目标数组 int 类型
* @return 返回目标数组对应最大元素的下标值数组 int 类型
*/
public static int[] getMaxIndexArrOf2(int[] arr) {
// 假设 arr[0] 为数组最大值
// 尾插法 count 计数器 1. 用于记录数组中有效元素个数
// 2. 用于记录下次存入数组的下标位置
int maxValue = arr[0];
int count = 0;
// 新建一个数组长度等于源数组长度的新数组
int[] maxIndexArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
// 判断目前最大值是否小于 arr[i]
if (maxValue < arr[i]){
// 为真 则变更 最大值 maxValue = arr[i]
// 将 count 重置为0
// 将 i 插入到新数组中 count++
maxValue = arr[i];
count = 0;
maxIndexArr[count++] = i;
} else if (maxValue == arr[i]) {
// 目前最大值等与 arr[i] 将新数组对应 count 元素赋值为 i 并且 count++
maxIndexArr[count++] = i;
}
}
return maxIndexArr;
}
// 【方案三】 存储下标的新数组容量等于源数组最大元素个数 (牺牲时间换空间)
/**
* 指定数组中最大值元素所有对应下标位置,存储到另一个数组中
* @param arr 目标数组 int 类型
* @return 返回目标数组对应最大元素的下标值数组 int 类型
*/
public static int[] getMaxIndexArrOf3(int[] arr) {
/*
思路:1. 找出源数组最大值的个数
2. 根数个数创建新数组
3. 将源数组最大值元素对应下标存入新数组
*/
// 假定 arr[0] 为源数组中最大值
int maxValue = arr[0];
// count 记录最大值的个数
int count = 1;
for (int i = 1; i < arr.length; i++) {
if (maxValue < arr[i]){
maxValue = arr[i];
count = 1;
}else if (maxValue == arr[i]){
count += 1;
}
}
// 创建新数组
int[] maxIndexArr = new int[count];
count = 0;
for (int i = 0; i < arr.length; i++) {
if (maxValue == arr[i]){
maxIndexArr[count++] = i;
}
}
return maxIndexArr;
}
1.5 找出指定元素在指定数值中第一次出现的下标位置 【 重要 -1 在源码中的思想 】
找出指定元素在指定数值中第一次出现的下标位置 int[] arr = {1, 3, 5, 7, 9, 21, 3, 7, 8, 10}; 3 在数组中第一次出现的位置下标位置 1
/**
* 找出指定元素在指定数值中第一次出现的下标位置
* @param arr 目标数组 int类型
* @param value 用户提供的指定数组 int 类型
* @return 返回 value 在目标数组中第一次出现的下标如果为 -1 表示数组中没有此元素
*/
public static int getFirstIndex(int[] arr, int value) {
/* 初始化找的下标为 index = -1
【注意重点】 -1 思想,此方法要求返回的是下标,而 -1 见名知意
一定是一个非法下标,用来告知数组中没有找到此元素,如果找到对应
元素下标则正常返回下标。
*/
int index = -1;
for (int i = 0; i < arr.length; i++) {
if (value == arr[i]){
index = i;
// 方法要求返回第一个找到的下标位置 所以一旦找到直接 break 退出循环
break;
}
}
return index;
}
1.6 找出指定元素在指定数组中最后一次出现的下标位置
找出指定元素在指定数组中最后一次出现的下标位置
int[] arr = {1, 3, 5, 7, 9, 21, 3, 7, 8, 10};
3 在数组中最后一次出现的位置下标位置 6思想一样只要当着循环找到直接退出就可以
/**
* 找出指定元素在指定数值中最后一次出现的下标位置
* @param arr 目标数组 int类型
* @param value 用户提供的指定数组 int 类型
* @return 返回 value 在目标数组中最后一次出现的下标 如果为 -1 表示数组中没有此元素
*/
public static int getLastIndex(int[] arr, int value) {
int index = -1;
for (int i = arr.length - 1; i >= 0; i--) {
if (value == arr[i]){
index = i;
break;
}
}
return index;
}
1.7 从数组中指定下标开始,到指定下标结束,获取数据存储到新数组中 【重要思想,条件整合与边界思想】
/**
* 从数组中指定下标开始,到指定下标结束,获取数据存储到新数组中
* @param arr int 类型目标数组
* @param start int 类型指定的开始下标位置
* @param end int 类型指定的结束下标位置
* @return 返回从指定下标到结束下标位置所有元素对应的 int 类型数组
*/
public static int[] subArray(int[] arr, int start, int end) {
/*
判断用户输入下标是否合法,首先不合法的条件 1. start > end
2. start < 0
3. end > arr.length - 1
4. start > arr.length - 1
5. end < 0
【仔细分析】 当 start < end 这个先决条件出现时加上 end < arr.length - 1 时是不会出现 end < 0 这种情况的
相同的 start > arr.length - 1 也不会出现,这就是条件出现了【冗余】现象,所以我们要有【边界思想】
*/
if (start > end || start < 0 || end > arr.length - 1){
// 此时 返回值要求返回 int 类型数组,所以我们不能再返回 -1 来表示输入下标不合法,所以要引入【异常】
throw new IllegalArgumentException("下标范围不合法");
}
// 【Java 源码思想】在 Java 中一般出先范围下标要遵循【含头不含尾】原则
// 所以在定义新数组长度时可以是 end - start
int[] subArray = new int[end - start];
// 尾插法计数器 count 1. 用来记录数组中有效元素个数 2. 用来记录下次存入数组的下标
int count = 0;
// 遍历源数组 从下标 start 开始到 end 结束
for (int i = start; i < end; i++) {
subArray[count++] = arr[i];
}
return subArray;
}
【深入剖析】返回值是引用数据,比如此题中的数组,在内存中的情况
1.8 复制指定数组数据内容到新数组 【保护数据】
【重要思想】此操作带给我们的作用看似毫无卵用,但是在后期的数据操作中,对于数据的保护是非常重要的,因为数据一点损毁将不可逆,造成的损失将不可估量,所以可以先复制一份数据,保留源数据。
/**
* 复制指定数组数据内容到新数组
* @param arr 指定 int 类型目标数组
* @return 返回复制数据后的新数组 int 类型
*/
public static int[] copyOf(int[] arr) {
// 新建一个数组
int[] newArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
1.9 指定数组内容逆序
/**
* 指定数组内容逆序
* @param arr 指定的 int 类型目标数组
*/
public static void reverse(int[] arr) {
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
1.10 在数组指定下标位置添加元素 【选择合适的返回值类型】
目标数组:
int[] arr = {1, 3, 5, 7, 9, 11, 13, 15, 17, 0};
注意:1. 0 无效元素,仅占位使用 2. 插入数据下标的位置必须在合法范围以内 例如: 添加指定元素 20 到下标为 5 的位置 {1, 3, 5, 7, 9, 20, 11, 13, 15, 17};
/**
* 在数组指定下标位置添加元素
* @param arr int 类型目标数组
* @param index int 类型目标下标位置
* @param value int 目标元素
* @return boolean 类型 true 表示添加成功 false 表示添加失败
*/
public static boolean add(int[] arr, int index, int value) {
// 判断下标是否合法
if (index < 0 || index > arr.length - 1){
return false;
}
/*
细节:从插入元素的下标开始所有元素后移,因此我们需要从数组后边开始遍历每个元素后移
到指定下标位置为止,最后在指定下标位置 赋值 value
*/
for (int i = arr.length - 1; i > index; i--) {
arr[i] = arr[i - 1];
}
// 最后将指定数据添加到指定下标位置
arr[index] = value;
return true;
}
1.11 删除数组中指定下标元素内容
目标数组:
int[] arr = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};
注意:1. 0 是无效元素,仅占位使用 2. 删除之后,要求数组元素向前移动 3. 删除数据下标的位置必须在合法范围以内 ArrayIndexOutOfBoundsException 例如: 删除指定下标 5 的元素 {1, 3, 5, 7, 9, 13, 15, 17, 19, 0}
【注意】此题看似和上题类似,但是实则不然,考虑到对**【数据的保护】**在选择返回值的时候,我们需要考虑 int 类型
因为用户在删除指定下标的元素时,用户只知道下标数据,对元素一无所知,我们需要在删除成功时对调用者返回一个 int 类型的所删除的数据
这样调用者可以看到删除的数据是什么,如果调用者误删除也可以给调用者一个反悔的机会。
同样的我们会面对另一个问题,删除失败时返回什么呢? 这是我们就要引入异常,可以抛出异常
/**
* 删除数组中指定下标元素内容
* @param arr int 类型目标数组
* @param index int 类型指定下标
* @return int 类型删除成功返回删除的元素值
*/
public static int remove(int[] arr, int index){
// 判断下标是否合法
if (index < 0 || index > arr.length - 1){
throw new IllegalArgumentException("下标不合法");
}
/*
细节:从删除元素开始所有元素向前移,因此我们需要从前开始遍历数组到指定下标位置
最后在有效元素后一位的位置插入 0 充当占位符
*/
// 为了保护数据提前将要删除的元素赋值给 temp
int temp = arr[index];
for (int i = index; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
// 在有效元素后一位的位置插入 0 充当占位符
arr[arr.length - 1] = 0;
// 返回删除的数据
return temp;
}
1.12 选择排序算法
选择排序算法核心
- 找极值( 极大值, 极小值)
- 换位置
/**
* 选择排序算法
* @param arr int 类型需要排序的数组
*/
public static void selectSortDesc(int[] arr) {
// 排序到倒数第二个时就已经全部有序所以 i < arr.length - 1
for (int i = 0; i < arr.length - 1; i++) {
//假设 i 为极值对应下标
int index = i;
// i + 1 之前的元素已经排序好 不在需要遍历
for (int j = i + 1; j < arr.length; j++) {
if (arr[index] < arr[j]){
index = j;
}
}
// 如果 index != i 说明发现了新的极值那么交换位置
if (index != i){
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
}
1.13 冒泡排序
/**
* 冒泡排序
* @param arr 需要排序的 int 类型数组
*/
public static void bubbleSortDesc(int[] arr) {
// 排序到倒数第二个时就已经全部有序所以 i < arr.length - 1
for (int i = 0; i < arr.length - 1; i++) {
// 每轮排序完毕数组的末尾位置元素时有序的所以要 - i 而需要遍历到 arr[j + 1] 防止指针越界 所以需要 -1
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] < arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}