Java数组和排序
Java数组和排序,
学习视频-西部开源Java之数组与排序:视频链接
Java数组和排序
数组
概述
为什么要有数组
- 数组的出现,是为了我们方便的去操做多个数据
数组的概念
-
数组,相当于一个容器,可以存储一组,相同数据类型的元素
-
既可以存储基本数据类型,也可以存储引用数据类型
数组声明创建
数组的定义格式
-
数据类型[] 数组名:int[] a = new int[10];
-
数据类型 数组名[]:double b[] = new double[10];
什么是数组的初始化
-
Java中的数组必须先初始化,然后才能使用
-
所谓初始化:就是为数组中的数组元素分配内存空间,并为每个数组元素赋值
初始化的分类
-
动态初始化:只指定长度,由系统给出初始化值
-
静态初始化:给出初始化值,由系统决定长度
-
注意事项:两种方式只能选择其中之一
动态初始化的格式
-
数据类型[] 数组名 = new 数据类型[数组长度]
-
int[] a = new int[5];
静态初始化的格式
-
数据类型[] 数组名 = new 数据类型[]
-
数据类型[] 数组名 =
-
int[] b = new int[]{2,3,4,5};
-
int[] b = {2,3,4,5};
数组的常见操作
1、数组元素遍历
package com.tang.array;
//数组遍历
public class ArrayDemo01 {
public static void main(String[] args) {
//定义数组
int[] a = {2,4,5,6};
//遍历数组
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
2、数组常见的下标越界异常
ArrayIndexOutOfBoundsException
3、数组元素的反向遍历
package com.tang.array;
//数组反向遍历
public class ArrayDemo02 {
public static void main(String[] args) {
//定义数组
int[] a = {2,4,5,6};
//反向遍历数组
for (int i = a.length - 1; i >= 0; i--) {
System.out.println(a[i]);
}
}
}
4、获取数组中元素的最大值或最小值
package com.tang.array;
//获取数组中的最大值或最小值
public class ArrayDemo03 {
public static void main(String[] args) {
//定义数组
int[] a = {45,34,5,23,97};
int max = a[0];
int min = a[0];
//遍历数组
for (int i = 0; i < a.length; i++) {
//最大值 max = max>a[i]?max:a[i];
max = Math.max(max, a[i]);
//最小值 min = min<a[i]?min:a[i];
min = Math.min(min, a[i]);
}
System.out.println("max: "+max);
System.out.println("min: "+min);
}
}
5、数组元素的反转
package com.tang.array;
import java.util.Arrays;
//数组元素的反转
public class ArrayDemo04 {
public static void main(String[] args) {
//定义数组
int[] a = {25,34,5,23,97};
//方法一:
for (int i = 0; i < a.length-1-i; i++) {
//元素反转
int t = a[i];
a[i] = a[a.length-1-i];
a[a.length-1-i]=t;
}
//方法二:
int[] b = new int[a.length];
for (int i = 0, j = b.length-1; i < a.length; i++, j--) {
b[j]=a[i];//元素反转
}
a=b;
//输出数组元素
System.out.println(Arrays.toString(a));
}
}
二维数组的介绍
1、二维数组概述
- 二维数组中的每个元素为一维数组
2、二维数组定义格式
二维数组动态初始化
// 数据类型[][] 数组名 = new 数据类型[m][n];
int[][] m = new int[4][3]; //动态初始化
// 数据类型 数组名[][] = new 数据类型[m][n];
// 数据类型[] 数组名[] = new 数据类型[m][n];
二维数组静态初始化
// 数据类型[][] 数组名 = {{元素00,元素02,…},{元素10,元素12,…},{元素20,元素22,…}};
int[][] a = new int[][] {{1,3},{5,6},{3,4}}; //静态初始化
3、二维数组的遍历
package com.tang.array.array2;
import java.util.Arrays;
//二维数组
public class ArrayDemo01 {
public static void main(String[] args) {
int[][] a = new int[][]{{1,3},{5,6},{3,4}};
for (int i = 0; i < a.length; i++) {
System.out.println(Arrays.toString(a[i]));
}
System.out.println("====================");
System.out.println(Arrays.deepToString(a));
}
}
4、二维数组的练习
案例演示:
1、需求:公司年销售额求和
-
某公司按照季度和月份统计的数据如下:单位(万元)
-
第一季度:22,66,44
-
第二季度:77,33,88
-
第三季度:25,45,65
-
第四季度:11,66,99
package com.tang.array.array2; public class ArrayDemo02 { public static void main(String[] args) { int sum = 0;//定义一个年销售额变量:sum int[][] arr = {{22,66,44},{77,33,88},{25,45,65,},{11,66,99}}; //遍历数组,累加求和 for (int[] a : arr) { for (int i : a) { sum+=i; } } System.out.println("年销售额: "+sum); } }
需求:打印杨辉三角形(行数可以键盘录入)
package com.tang.array.array2;
import java.util.Scanner;
/*
需求:打印杨辉三角形(行数可以键盘录入)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
我们观察发现:
1. 每一行的第一列和最后一列都是1
2. 从第三行开始,从第二列开始,这个数字等于他上一行的前一列和上一行的本列之和
*/
public class ArrayDemo03 {
public static void main(String[] args) {
System.out.println("请输入行数: ");
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
//定义一个二维数组,存储数据
int[][] a = new int[n][n];
for (int i = 0; i < a.length; i++) {
a[i]=new int[i+1]; //设置每一行的元素个数
a[i][0] = 1; //第一列设置为1
a[i][i] = 1; //最后一列为设置为1
System.out.print(a[i][0]+" ");//打印每行第一列
//设置每行中间的元素值
for (int j = 1; j < a[i].length-1; j++) {
a[i][j]=a[i-1][j-1]+a[i-1][j]; //设置每行中间的元素值
System.out.print(a[i][j]+" ");
}
System.out.print(a[i][i]);//打印每行最后一列
System.out.println();
}
}
}
需求:查找数组元素的索引
-
方式1:遍历数组挨个查找
package com.tang.array.array2; /* 需求:根据数组元素查找出该元素第一次在数组中出现的索引 - 方式1:遍历数组挨个查找--->第一次索引 - 方式2:二分查找--->不一定是第一次索引 */ public class ArrayDemo04 { public static void main(String[] args) { //定义一个数组 int[] a = {56,34,63,4,34,3,32}; //根据数组元素查找出该元素第一次在数组中出现的索引 int index = getIndexByEle(a,34); System.out.println("该元素第一次在数组中出现的索引是: "+index); } //方式1:遍历数组挨个查找 private static int getIndexByEle(int[] a, int ele) { for (int i = 0; i < a.length; i++) { if(ele==a[i]){ return i; } } return -1;//没有找到就返回-1 } }
-
方式2:二分查找
-
二分查找的前提:数组必须是有序的
-
二分查找的思想:每次查找中间的元素,比较大小就能减少一半的元素
package com.tang.array.array2; /* 需求:根据数组元素查找出该元素第一次在数组中出现的索引 - 方式2:二分查找--->不一定是第一次索引 */ public class ArrayDemo05 { public static void main(String[] args) { //定义一个数组 int[] a = {1,2,3,4,5,6,7,8,9}; //根据数组元素查找出该的索引 int index = getIndexByEle(a,3); System.out.println("该元素在数组中出现的索引是: "+index); } //方式2:二分查找 private static int getIndexByEle(int[] a, int ele) { //定义最小索引 int min = 0; //定义最大索引 int max = a.length-1; for (int i = (min+max)/2; min <= max; i = (min+max)/2) { //找到元素,返回索引 if(ele==a[i]){ return i; // ele>中间索引元素,min索右移,等于中间索引+1 }else if(ele>a[i]){ min=i+1; // ele<中间索引元素,max索引左移,等于中间索引-1 }else{ max=i-1; } } return -1;//没有找到就返回-1 } }
-
数组之常见排序算法
排序:把一个无需序列通过某种方式变成一个有序序列
1.冒泡排序
-
排序原理:数组元素两两比较,交互位置,大元素往后放,那么经过 一轮比较后,最大的元素,就会出现在最大索引处
-
int[] arr = {24, 69, 80, 57, 13}; //待排序数组
package com.tang.sort; import java.util.Arrays; public class ArraysDemo01 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序数组 boolean flag = true;//标记位,可以减少比较次数 for (int i = 0; i < arr.length-1; i++) { for (int j = 1; j < arr.length-i; j++) { if (arr[j-1]>arr[j]){ //交换位置 int t = arr[j-1]; arr[j-1] = arr[j]; arr[j] = t; flag = false;//标记位,可以减少比较次数 } } if (flag){ break; } flag = true; } System.out.println(Arrays.toString(arr)); } }
2.选择排序
-
排序原理:从0索引处开始,依次和后面的元素进行比较,小的元素往前放,经过一轮比较后,最小的元素就出现在了最小索引处
-
int[] arr = {24, 69, 80, 57, 13}; //待排序数组
package com.tang.sort; import java.util.Arrays; public class ArraysDemo02 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序数组 for (int i = 0; i < arr.length-1; i++) { for (int j = i+1; j < arr.length; j++) { if (arr[i]>arr[j]){ //交换位置 int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } } } System.out.println(Arrays.toString(arr)); } }
3.插入排序
-
排序原理:算法思路:直接插入排序,是一种最简单的排序方法,它的基本操作是将一个记录插入到一个长度为m的有序表中,使之仍保持有序
package com.tang.sort; import java.util.Arrays; //直接插入排序 public class ArraysDemo03 { public static void main(String[] args) { int[] arr = {24, 69, 80, 57, 13};//待排序数组 //外层循环定义轮次 for (int i = 1; i < arr.length; i++) { //里层循环进行比较插入 for (int j = i; j > 0; j--) { if (arr[j]<arr[j-1]){ int t = arr[j]; arr[j] = arr[j-1]; arr[j-1] = t; }else { break; } } } System.out.println(Arrays.toString(arr)); } }
4.希尔排序
-
希尔排序又称缩小增量排序,是对插入排序的一个优化
-
基本思想:先将原表按增量ht分组,每个子文件按照直接插入法排序。同样,用下一个增量ht/2将文件再分为子文件,再直接插入法排序。直到ht=1时整个文件排好序
-
关键:选择合适的增量
-
增量选择:
-
数组长度的一半,效率不是太高
-
Knuth序列
-
h=1
-
h=3*h+1
-
-
-
-
希尔排序算法:可以通过三重循环来实现
package com.tang.sort; import java.util.Arrays; //希尔排序 public class ArraysDemo04 { public static void main(String[] args) { int[] arr = {46, 55, 13, 42, 17, 94, 5, 70}; shellSort(arr); System.out.println(Arrays.toString(arr)); } private static void shellSort(int[] arr) { //定义一个增量 int h = 1; //根据克努特序列选取我们的第一次增量 while (h <= arr.length/3){ h = h*3+1; } for (; h > 0; h/=2) { for (int i = h; i < arr.length; i++) { //里层循环进行比较插入 for (int j = i; j >= h; j-=h) { if (arr[j]<arr[j-h]){ int t = arr[j]; arr[j] = arr[j-h]; arr[j-h] = t; }else { break; } } } } } }
插入排序与希尔排序比较:
- 使用希尔排序,元素位置交换的次数变少了,效率变高了
5.快速排序
-
分治法:比大小,再分区
-
从数组中取出一个数,作为基准数
-
分区:将比这个数大或等于的数全放到他的右边,小于它的数全放到它的左边
-
再对左右区间重复第二步,直到个区间只有一个数
-
-
实现思路
-
将基准数挖出形成第一个坑
-
由后向前找比他小的数,找到后挖出次数填到前一个坑中
-
由前向后找比他大或等于的数,找到后也挖出此数填坑到前一个坑中
-
再重复下执行2,3两步骤
package com.tang.sort; import java.util.Arrays; //快速排序 public class ArraysDemo05 { public static void main(String[] args) { int[] arr = {46, 55, 13, 42, 17, 94, 5, 70}; int start = 0; int end = arr.length-1; quickSort(arr, start, end); System.out.println(Arrays.toString(arr)); } //快速排序 private static void quickSort(int[] arr, int start, int end) { //找出左右两区的索引位置,然后对左右两区进行递归调用 if (start<end){ //找出左右两区的索引位置 int index = getIndex(arr,start,end); //左边快速排序 quickSort(arr, start, index-1); //右边快速排序 quickSort(arr, index+1, end); } } /* 1. 将基准数挖出形成第一个坑 2. 由后向前找比他小的数,找到后挖出次数填到前一个坑中 3. 由前向后找比他大或等于的数,找到后也挖出此数填坑到前一个坑中 4. 再重复下执行2,3两步骤 */ //获取一轮排序后的基数最终索引 private static int getIndex(int[] arr, int start, int end) { int i = start; int j = end; int x = arr[i]; //基准数 while (i<j){ while (i<j&&arr[j]>=x){ j--; } if (i<j){ arr[i]=arr[j]; } while (i<j&&arr[i]<x){ i++; } if (i<j){ arr[j]=arr[i]; } } arr[i]=x; return i; } }
-
6.归并排序
算法思想:
-
归并排序(Merge Sort)就是利用归并的思想实现排序的方法。
-
它的原理是假设初始序列有N个记录,可以看成是N个有序的子序列,每个子序列的长度为1,然后两两归并你…如此重复,直至得到一个长度为N的有序序列为止,这种排序方法称为2路归并排序。
package com.tang.sort;
import java.util.Arrays;
//归并排序
public class ArraysDemo06 {
static int[] b;
public static void main(String[] args) {
int[] arr = {46, 55, 13, 42, 17, 94, 5, 70};
//拆分
split(arr, 0, arr.length-1);
System.out.println(Arrays.toString(arr));
}
//拆分
private static void split(int[] arr, int start, int end) {
//计算中间索引
int center = (start + end) / 2;
if (start<end){
//拆分左边
split(arr, start, center);
//拆分右边
split(arr, center+1, end);
//归并
merge(arr, start, center, end);
}
}
//归并
private static void merge(int[] arr, int start, int center, int end) {
//定义一个临时数组
int[] b = new int[end-start+1];
//定义左边数组的起始索引
int i =start;
//定义右边数组的起始索引
int j =center+1;
//定义临时数组的起始索引
int index = 0;
//比较左右两边数组的元素大小,往临时数组中放
while (i<=center&&j<=end){
if (arr[i]<=arr[j]){
b[index]=arr[i];
i++;
}else {
b[index]=arr[j];
j++;
}
index++;
}
//处理剩余元素
while (i<=center){
b[index]=arr[i];
i++;
index++;
}
while (j<=end){
b[index]=arr[j];
j++;
index++;
}
//将临时数组中的元素取到原数组中
for (int k = 0; k < b.length; k++) {
arr[k+start]=b[k];
}
}
}
7.基数排序
-
基数排序不同于之前所介绍的各类排序
-
前边介绍到的排序方法或多或少的是通过使用比较和移动记录来实现排序,而基数排序的实现不需要进行对关键字的比较,只需要对关键字进行”分配“与”收集“两种操作即可完成。
package com.tang.sort; import java.util.Arrays; //基数排序 public class ArraysDemo07 { public static void main(String[] args) { //基数排序: 通过分配再收集的方式进行排序 int[] arr = {33,24,8,876,34,12,67,89,56,43,354,45,34}; //基数排序 sort(arr); System.out.println(Arrays.toString(arr)); } //基数排序 private static void sort(int[] arr) { //定义二维数组管理,放10个桶 int[][] tempArr = new int[10][arr.length]; //定义一维数组为上面10个桶统计数字 int[] counts = new int[10]; //获取数组中的最大值 int max = getMax(arr); //确定排序轮次 int len = String.valueOf(max).length();//获取整数的位数 for (int i = 0, n = 1; i < len; i++, n*=10) { //元素放入桶中 for (int j = 0; j < arr.length; j++) { //获取每个数各个位上的数字 int ys = arr[j] / n % 10; tempArr[ys][counts[ys]++] = arr[j]; } //取出桶中的元素 //定义arr数组的下标 int index = 0; for (int k = 0; k < 10; k++) { if (counts[k]!=0) { for (int h = 0; h < counts[k]; h++) { //从桶中取出元素放回原数组 arr[index] = tempArr[k][h]; index++; } counts[k]=0;//清楚上一次统计的个数 } } } } //获取数组中的最大值 private 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; } }
8.堆排序
-
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序
-
堆排序的基本思想:
-
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点
-
将其与末尾元素进行交换,此时末尾就为最大值
-
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值
-
如此反复执行,便能得到一个有序序列了
-
-
我们用简单的公式来描述一下堆排序的定义就是:
-
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
-
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
package com.tang.sort; import java.util.Arrays; //堆排序 public class ArraysDemo08 { public static void main(String[] args) { //定义一个数组 int[] arr = {33,24,8,876,34,12,67,89,56,43,354,45,34}; //调成大顶堆的方法 //定义开始调整的位置 int startIndex = arr.length/2-1; //循环开始调 for (int i = startIndex; i >= 0; i--) { getMaxHeap(arr,arr.length,i); } //经过上面的操作后,已经把数组变成一个大顶堆,把根元素和最后一个元素进行调换 for (int i = arr.length-1; i > 0; i--) { //进行调换 int t = arr[0]; arr[0] = arr[i]; arr[i] = t; //换完之后我们再把剩余元素调成大顶堆 getMaxHeap(arr,i,0); } System.out.println(Arrays.toString(arr)); } /** * * @param arr 要排序的数组 * @param size 调整的元素个数 * @param index 从哪里开始调整 */ private static void getMaxHeap(int[] arr, int size, int index) { //获取左右子节点的索引 int leftNodeIndex = index*2+1; int rightNodeIndex = index*2+2; //找到最大节点所对应的索引 int maxIndex = index; if (leftNodeIndex<size&&arr[leftNodeIndex]>arr[maxIndex]){ maxIndex = leftNodeIndex; } if (rightNodeIndex<size&&arr[rightNodeIndex]>arr[maxIndex]){ maxIndex = rightNodeIndex; } //我们来调换位置 if (maxIndex!=index){ int t = arr[maxIndex]; arr[maxIndex] = arr[index]; arr[index] = t; //调换完之后,可能会影响到,下面的子树,不是大顶堆,我们还需要再次调换 getMaxHeap(arr,size,maxIndex); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步