百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 优雅编程 > 正文

【每日一学】开发者福音:JavaSE数组实战技巧一览!

sinye56 2024-10-17 15:18 7 浏览 0 评论

学习目标

数组

4.1 数组的概念

4.1.1 容器概述

需求分析:

现在需要统计某公司员工的工资情况,例如计算平均工资、找到最高工资等。假设该公司有50名员工,用前面所学的知识,程序首先需要声明50个变量来分别记住每位员工的工资,然后在进行操作,这样做会显得很麻烦,而且错误率也会很高。因此我们可以使用容器进行操作。将所有的数据全部存储到一个容器中,统一操作。

容器概念:

?生活中的容器:水杯(装水等液体),衣柜(装衣服等物品),教室(装学生等人员)。

?程序中的容器:是将多个数据存储到一起,每个数据称为该容器的元素。

4.1.2 数组的概念

?数组概念: 数组就是用于存储数据的长度固定的容器,保证多个数据的数据类型要一致。

百度百科中对数组的定义:

所谓数组(array),就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,以便统一管理他们,然后用编号区分他们,这个名字称为数组名,编号称为下标或索引(index)。组成数组的各个变量称为数组的元素(element)。数组中元素的个数称为数组的长度(length)。

数组的特点:

1、数组的长度一旦确定就不能修改

2、创建数组时会在内存中开辟一整块连续的空间。

3、存取元素的速度快,因为可以通过[下标],直接定位到任意一个元素。

4.1.3 数组的分类

1、按照维度分:

?一维数组:存储一组数据

?二维数组:存储多组数据,相当于二维表,一行代表一组数据,这是这里的二维表每一行长度不要求一样。

2、按照元素类型分:

?基本数据类型的元素:存储数据值

?引用数据类型的元素:存储对象(本质上存储对象的首地址)(这个在面向对象部分讲解)

注意:无论数组的元素是基本数据类型还是引用数据类型,数组本身都是引用数据类型。

4.2 一维数组的声明与使用

4.2.1 一维数组的声明

?一维数组的声明/定义格式

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

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

?数组的声明,就是要确定:

(1)数组的维度:在Java中数组的标点符号是[],[]表示一维,[][]表示二维

(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int, String, Student等

(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。

?示例

public class Test01ArrayDeclare {
public static void main(String[] args) {
//比如,要存储一个小组的成绩
int[] scores;
int grades[];
// System.out.println(scores);//未初始化不能使用

//比如,要存储一组字母
char[] letters;

//比如,要存储一组姓名
String[] names;

//比如,要存储一组价格
double[] prices;

}
}

4.2.2 一维数组的静态初始化

?什么是初始化?

–初始化就是确定数组元素的总个数(即数组的长度)和元素的值

?什么是静态初始化?

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

?一维数组静态初始化格式1:

数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分开两个语句写

例如,定义存储1,2,3,4,5整数的数组容器

int[] arr = {1,2,3,4,5};//正确

int[] arr;
arr = {1,2,3,4,5};
//错误

?一维数组静态初始化格式2:

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

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

例如,定义存储1,2,3,4,5整数的数组容器。

int[] arr = new int[]{1,2,3,4,5};//正确

int[] arr;
arr = new int[]{1,2,3,4,5};
//正确

?一维数组静态初始化演示

public class Test02ArrayInitialize {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
//右边不需要写new int[]

int[] nums;
nums = new int[]{10,20,30,40};
//声明和初始化在两个语句完成,就不能使用new int[]

char[] word = {'h','e','l','l','o'};

String[] names = {"张三","李四","王五"};

System.out.println("arr数组:" + arr);
//arr数组:[I@1b6d3586
System.out.println("nums数组:" + nums);
//nums数组:[I@4554617c
System.out.println("word数组:" + word);
//word数组:[C@74a14482
System.out.println("names数组:" + names);
//names数组:[Ljava.lang.String;@1540e19d
}
}

4.2.3 一维数组的使用

?如何获取数组的元素总个数,即数组的长度

数组的长度属性: 每个数组都具有长度,而且是固定的,Java中赋予了数组的一个属性,可以获取到数组的长度,语句为:数组名.length ,属性length的执行结果是数组的长度,int类型结果。

数组名.length

?如何表示数组中的一个元素?

每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标,可以通过数组的索引/下标访问到数组中的元素。

数组名[索引/下标]

?数组的下标范围?

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

?一维数组的使用演示

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

System.out.println("arr数组的长度:" + arr.length);
System.out.println("arr数组的第1个元素:" + arr[0]);
//下标从0开始
System.out.println("arr数组的第2个元素:" + arr[1]);
System.out.println("arr数组的第3个元素:" + arr[2]);
System.out.println("arr数组的第4个元素:" + arr[3]);
System.out.println("arr数组的第5个元素:" + arr[4]);

//修改第1个元素的值
//此处arr[0]相当于一个int类型的变量
arr[0] = 100;
System.out.println("arr数组的第1个元素:" + arr[0]);
}
}

4.2.4 数组下标越界异常

当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会报数组下标越界异常:ArrayIndexOutOfBoundsException。

public class Test04ArrayIndexOutOfBoundsException {
public static void main(String[] args) {
int[] arr = {1,2,3};
// System.out.println("最后一个元素:" + arr[3]);//错误,下标越界ArrayIndexOutOfBoundsException
// System.out.println("最后一个元素:" + arr[arr.length]);//错误,下标越界ArrayIndexOutOfBoundsException
System.out.println("最后一个元素:" + arr[arr.length-1]);
//对
}
}

创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。


4.2.4 一维数组的遍历

数组遍历: 就是将数组中的每个元素分别获取出来,就是遍历。遍历也是数组操作中的基石。for循环与数组的遍历是绝配。

public class Test05ArrayIterate {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印数组的属性,输出结果是5
System.out.println("数组的长度:" + arr.length);

//遍历输出数组中的元素
System.out.println("数组的元素有:");
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
}
}

4.2.5 一维数组动态初始化

?什么是动态初始化?

动态初始化就是先确定元素的个数(即数组的长度),而元素此时只是默认值,并不是真正的数据。元素真正的数据需要后续单独一个一个赋值。

?格式:

数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];



数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];

?new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组对象。

?[长度]:数组的长度,表示数组容器中可以存储多少个元素。

?注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。

例如,定义可以存储5个整数的数组容器,代码如下:

int[] arr = new int[5];

int[] arr;
arr = new int[5];

int[] arr = new int[5]{1,2,3,4,5};
//错误的,后面有{}指定元素列表,就不需要在[]中指定元素个数了。

?一维数组的动态初始化演示

public class Test06ArrayInitialize {
public static void main(String[] args) {
int[] arr = new int[5];

System.out.println("arr数组的长度:" + arr.length);
System.out.print("存储数据到arr数组之前:[");
for (int i = 0; i < arr.length; i++) {
if(i==0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");

//初始化
/* arr[0] = 2;
arr[1] = 4;
arr[2] = 6;
arr[3] = 8;
arr[4] = 10;*/

for (int i = 0; i < arr.length; i++) {
arr[i] = (i+1) * 2;
}

System.out.print("存储数据到arr数组之后:[");
for (int i = 0; i < arr.length; i++) {
if(i==0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
}
}

4.2.6 数组元素的默认值

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


public class Test07ArrayElementDefaultValue {
public static void main(String[] args) {
//存储26个字母
char[] letters = new char[26];
System.out.println("letters数组的长度:" + letters.length);
System.out.print("存储字母到letters数组之前:[");
for (int i = 0; i < letters.length; i++) {
if(i==0){
System.out.print(letters[i]);
}else{
System.out.print("," + letters[i]);
}
}
System.out.println("]");

//存储5个姓名
String[] names = new String[5];
System.out.println("names数组的长度:" + names.length);
System.out.print("存储姓名到names数组之前:[");
for (int i = 0; i < names.length; i++) {
if(i==0){
System.out.print(names[i]);
}else{
System.out.print("," + names[i]);
}
}
System.out.println("]");
}
}

4.3 一维数组内存分析

4.3.1 内存概述

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁。其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来。我们编写的程序是存放在硬盘中的,在硬盘中的程序是不会运行的,必须放进内存中才能运行,运行完毕后会清空内存。

Java虚拟机要运行程序,必须要对内存进行空间的分配和管理。

4.3.2 Java虚拟机的内存划分

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


区域名称

作用

程序计数器

程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

本地方法栈

当程序中调用了native的本地方法时,本地方法执行期间的内存区域

方法区

存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

堆内存

存储对象(包括数组对象),new来创建的,都存储在堆内存。

虚拟机栈

用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。

4.4.3 一维数组在内存中的存储

1、一个一维数组内存图

public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr);
//[I@5f150435
}

思考:打印arr为什么是[I@5f150435,它是数组的地址吗?

答:它不是数组的地址。

问?不是说arr中存储的是数组对象的首地址吗?

答:arr中存储的是数组的首地址,但是因为数组是引用数据类型,打印arr时,会自动调用arr数组对象的toString()方法,该方法默认实现的是对象类型名@该对象的hashCode()值的十六进制值。

问?对象的hashCode值是否就是对象内存地址?

答:不一定,因为这个和不同品牌的JVM产品的具体实现有关。例如:Oracle的OpenJDK中给出了5种实现,其中有一种是直接返回对象的内存地址,但是OpenJDK默认没有选择这种方式。

2、数组下标为什么是0开始

因为第一个元素距离数组首地址间隔0个单元格。

3、两个一维数组内存图

两个数组独立

public static void main(String[] args) {
int[] arr = new int[3];
int[] arr2 = new int[2];
System.out.println(arr);
System.out.println(arr2);
}

4、两个变量指向一个一维数组

两个数组变量本质上代表同一个数组。

public static void main(String[] args) {
// 定义数组,存储3个元素
int[] arr = new int[3];
//数组索引进行赋值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//输出3个索引上的元素值
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
//定义数组变量arr2,将arr的地址赋值给arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]);
}

4.4 数组的常见算法

4.4.1 数组统计:求总和、均值、统计偶数个数等

示例代码1:

public class Test08ArrayElementSum {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//求总和、均值
int sum = 0;
//因为0加上任何数都不影响结果
for(int i=0; i<arr.length; i++){
sum += arr[i];
}
double avg = (double)sum/arr.length;

System.out.println("sum = " + sum);
System.out.println("avg = " + avg);
}
}

示例代码2:

public class Test09ArrayElementMul {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};

//求总乘积
long result = 1;
//因为1乘以任何数都不影响结果
for(int i=0; i<arr.length; i++){
result *= arr[i];
}

System.out.println("result = " + result);
}
}

示例代码3:

public class Test10ArrayElementEvenCount {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//统计偶数个数
int evenCount = 0;
for(int i=0; i<arr.length; i++){
if(arr[i]%2==0){
evenCount++;
}
}

System.out.println("evenCount = " + evenCount);
}
}

4.4.2 数组找最值

1、找最大值/最小值


思路:

(1)先假设第一个元素最大/最小

(2)然后用max/min与后面的元素一一比较

示例代码:

public class Test11ArrayMax {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//找最大值
int max = arr[0];
for(int i=1; i<arr.length; i++){
//此处i从1开始,是max不需要与arr[0]再比较一次了
if(arr[i] > max){
max = arr[i];
}
}

System.out.println("max = " + max);
}
}

2、找最值及其第一次出现的下标

思路:

(1)先假设第一个元素最大/最小

(2)用max/min变量表示最大/小值,用max/min与后面的元素一一比较

(3)用index时刻记录目前比对的最大/小的下标

示例代码:

public class Test12MaxIndex {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//找最大值以及第一个最大值下标
int max = arr[0];
int index = 0;
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
index = i;
}
}

System.out.println("max = " + max);
System.out.println("index = " + index);
}
}

思路:

(1)先假设第一个元素最大/最小

(2)用maxIndex时刻记录目前比对的最大/小的下标,那么arr[maxIndex]就是目前的最大值

public class Test12MaxIndex2 {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//找最大值
int maxIndex = 0;
for(int i=1; i<arr.length; i++){
if(arr[i] > arr[maxIndex]){
maxIndex = i;
}
}
System.out.println("最大值:" + arr[maxIndex]);
}
}

3、找最值及其所有最值的下标(选讲)

有一种情况是元素是重复的,那么最大值就有多个。

思路:

(1)先找最大值

①假设第一个元素最大

②用max与后面的元素一一比较

(2)遍历数组,看哪些元素和最大值是一样的

示例代码:

public class Test13AllMaxIndex {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9,9,3};
//找最大值
int max = arr[0];
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
System.out.println("最大值是:" + max);
System.out.print("最大值的下标有:");

//遍历数组,看哪些元素和最大值是一样的
for(int i=0; i<arr.length; i++){
if(max == arr[i]){
System.out.print(i+"\t");
}
}
System.out.println();
}
}

优化

public class Test13AllMaxIndex2 {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9,9,3};
//找最大值
int max = arr[0];
String index = "0";
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
index = i + "";
}else if(arr[i] == max){
index += "," + i;
}
}

System.out.println("最大值是" + max);
System.out.println("最大值的下标是[" + index+"]");
}
}

4.4.3 数组的元素查找

1、顺序查找

顺序查找:挨个查看

要求:对数组元素的顺序没要求

顺序查找示例代码:

public class Test14ArrayOrderSearch {
//查找value第一次在数组中出现的index
public static void main(String[] args){
int[] arr = {4,5,6,1,9};
int value = 1;
int index = -1;

for(int i=0; i<arr.length; i++){
if(arr[i] == value){
index = i;
break;
}
}

if(index==-1){
System.out.println(value + "不存在");
}else{
System.out.println(value + "的下标是" + index);
}
}
}

2、二分查找

import java.util.Scanner;

public class Test15ArrayBinarySearch {
public static void main(String[] args){
//数组一定是有序的
int[] arr = {8,15,23,35,45,56,75,85};

Scanner input = new Scanner(System.in);
System.out.print("请输入你要查找的值:");
int target = input.nextInt();

int index = -1;
for(int left = 0,right = arr.length-1; left<=right; ){
//int mid = (left+right)/2;
int mid = left + (right-left)/2;

if(arr[mid] == target){
index = mid;
break;
}else if(target > arr[mid]){
//说明target在[mid]右边
left = mid+1;
}else{
//说明target <arr[mid],target在[mid]左边< span> </arr[mid],target在[mid]左边<>
right= mid-1;
}
}
if(index!=-1){
System.out.println("找到了,下标是"+index);
}else{
System.out.println("不存在");
}

}
}

2、二分查找(2)没找到

3、二分查找考虑重复元素(选讲)

/*
思考:二分查找时,如果有重复元素怎么办?
*/
import java.util.Scanner;

public class Test16BinarySearchSameElement{
public static void main(String[] args){
//数组一定是有序的
int[] arr = {1,1,1,1,3,3,3,3,5,5,5,7,7,7,7,9,9,9,9,11,11,11,11,13,13,13};

Scanner input = new Scanner(System.in);
System.out.print("请输入你要查找的值:");
int target = input.nextInt();
//5

int index = -1;
for(int left = 0,right = arr.length-1; left<=right;){
//int mid = (left+right)/2;
int mid = left + (right-left)/2;
System.out.println("mid=" + mid);

if(arr[mid] == target){
index = mid;
System.out.print(target +"的下标有:" + index);

//使用二分查找的数是“有序”,如果有重复元素,那么它们是在一起的
//继续比较mid的左边和右边的元素,直到元素与target不相等为止
for(int j=mid-1; j>=left; j--){
if(arr[j] == target){
System.out.print("," + j);
}else{
break;
}
}
for(int j=mid+1; j<=right; j++){
if(arr[j] == target){
System.out.print("," + j);
}else{
break;
}
}
System.out.println();
break;
}else if(target > arr[mid]){
//说明target在[mid]右边
left = mid+1;

//其实这里还可以优化,如果arr[mid]右边的元素和arr[mid]一样,可以把左边界继续往右移动
while(arr[mid] == arr[left]){
System.out.println("left=" + left);
left++;
}
}else{
//说明target <arr[mid],target在[mid]左边< span> </arr[mid],target在[mid]左边<>
right= mid-1;

//其实这里还可以优化,如果arr[mid]左边的元素和arr[mid]一样,可以把右边界继续往左移动
while(arr[mid] == arr[right]){
System.out.println("right=" + right);
right--;
}
}
}
if(index==-1){
System.out.println("不存在");
}
}
}

4.4.5 数组元素的反转

实现思想:数组对称位置的元素互换。

1561469467316

public class Test17ArrayReverse {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println("反转之前:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

//反转
/*
思路:首尾对应位置的元素交换
(1)确定交换几次
次数 = 数组.length / 2
(2)谁和谁交换
for(int i=0; i<次数; i++){
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
*/
for(int i=0; i<arr.length/2; i++){
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}

System.out.println("反转之后:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}

}

1561469087319

public class Test17ArrayReverse2 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println("反转之前:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

//反转
//左右对称位置交换
for(int left=0,right=arr.length-1; left<right; left++,right--){
//首 与 尾交换
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}

System.out.println("反转之后:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}

4.4.6 数组元素排序

1、排序算法概述

数组的排序算法很多,实现方式各不相同,时间复杂度、空间复杂度、稳定性也各不相同:


?时间复杂度:

常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)

一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

?空间复杂度:

类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。

?稳定性:

排序一定会设计到数组元素位置的交换。如果两个元素相等,无论它们原来是否相邻,在排序过程中,最后它们变的相邻,但是它们前后顺序并没有改变,就称为稳定的,否则就是不稳定的。

2、直接选择排序

/*
1、直接选择排序

思想:每一轮找出本轮的最大值/最小值,然后看它是否在它应该在的位置。
如果不在正确的位置,就与这个位置的元素交换。

过程:arr{6,9,2,9,1} 目标:从小到大
第1轮:最大值是9,它现在在arr[1],它应该在arr[4],不对,交换arr[1]和arr[4],{6,1,2,9,9}
第2轮:最大值是9,它现在在arr[3],它应该在arr[3],对,不动
第3轮:最大值是6,它现在在arr[0],它应该在arr[2],不对,交换arr[0]和arr[2],{2,1,6,9,9}
第4轮:最大值是2,它现在在arr[0],它应该在arr[1],不对,交换arr[0]和arr[1],{1,2,6,9,9}

过程:arr{6,9,2,9,1} 目标:从小到大
第1轮:最小值是1,它现在在arr[4],它应该在arr[0],不对,交换arr[4]和arr[0],{1,9,2,9,6}
第2轮:最小值是2,它现在在arr[2],它应该在arr[1],不对,交换arr[2]和arr[1],{1,2,9,9,6}
第3轮:最小值是6,它现在在arr[4],它应该在arr[2],不对,交换arr[4]和arr[2],{1,2,6,9,9}
第4轮:最小值是7,它现在在arr[3],它应该在arr[3],对,不动

*/
public class Test18SelectSort{
public static void main(String[] args){
int[] arr = {6,9,2,9,1};

//直接选择排序,轮数 = 数组的元素总个数-1
/*
arr.length=5
i=0
i=1
i=2
i=3
*/
for(int i=0; i<arr.length-1; i++){
//找出本轮的最小值,及其下标
/*
i=0,第1轮,查找的范围是[0,4],一开始假设arr[0]最小
i=1,第2轮,查找的范围是[1,4],一开始假设arr[1]最小
i=2,第3轮,查找的范围是[2,4],一开始假设arr[2]最小
i=3,第4轮,查找的范围是[3,4],一开始假设arr[3]最小
int min = arr[i];
*/
int min = arr[i];
int index = i;
//用[i+1, arr.length-1]范围的元素与min比较
for(int j=i+1; j<arr.length; j++){
if(arr[j] < min){
min = arr[j];
index = j;
}
}

//判断min是否在它应该在的位置
/*
i=0,第1轮,最小值应该在arr[0]位置,它现在在arr[index]位置
i=1,第2轮,最小值应该在arr[1]位置,它现在在arr[index]位置
i=2,第3轮,最小值应该在arr[2]位置,它现在在arr[index]位置
i=3,第4轮,最小值应该在arr[3]位置,它现在在arr[index]位置

最小值应该在arr[i]位置, 如果index!=i,说明它不在应该在的位置,
就交换arr[i]和arr[index]位置
*/
if(index!=i){
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}

}

//完成排序,遍历结果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}

}
}

3、冒泡排序

Java中的经典算法之冒泡排序(Bubble Sort)

原理:比较两个相邻的元素,将值大的元素交换至右端。

思路:依次比较相邻的两个数,将小数放到前面,大数放到后面。

即第一趟,首先比较第1个和第2个元素,将小数放到前面,大数放到后面。

然后比较第2个和第3个元素,将小数放到前面,大数放到后面。

如此继续,直到比较最后两个数,将小数放到前面,大数放到后面。

重复第一趟步骤,直至全部排序完成。

/*
1、冒泡排序(最经典)
思想:每一次比较“相邻(位置相邻)”元素,如果它们不符合目标顺序(例如:从小到大),
就交换它们,经过多轮比较,最终实现排序。
(例如:从小到大) 每一轮可以把最大的沉底,或最小的冒顶。

过程:arr{6,9,2,9,1} 目标:从小到大

第一轮:
第1次,arr[0]与arr[1],6>9不成立,满足目标要求,不交换
第2次,arr[1]与arr[2],9>2成立,不满足目标要求,交换arr[1]与arr[2] {6,2,9,9,1}
第3次,arr[2]与arr[3],9>9不成立,满足目标要求,不交换
第4次,arr[3]与arr[4],9>1成立,不满足目标要求,交换arr[3]与arr[4] {6,2,9,1,9}
第一轮所有元素{6,9,2,9,1}已经都参与了比较,结束。
第一轮的结果:第“一”最大值9沉底(本次是后面的9沉底),即到{6,2,9,1,9}元素的最右边

第二轮:
第1次,arr[0]与arr[1],6>2成立,不满足目标要求,交换arr[0]与arr[1] {2,6,9,1,9}
第2次,arr[1]与arr[2],6>9不成立,满足目标要求,不交换
第3次:arr[2]与arr[3],9>1成立,不满足目标要求,交换arr[2]与arr[3] {2,6,1,9,9}
第二轮未排序的所有元素 {6,2,9,1}已经都参与了比较,结束。
第二轮的结果:第“二”最大值9沉底(本次是前面的9沉底),即到{2,6,1,9}元素的最右边
第三轮:
第1次,arr[0]与arr[1],2>6不成立,满足目标要求,不交换
第2次,arr[1]与arr[2],6>1成立,不满足目标要求,交换arr[1]与arr[2] {2,1,6,9,9}
第三轮未排序的所有元素{2,6,1}已经都参与了比较,结束。
第三轮的结果:第三最大值6沉底,即到 {2,1,6}元素的最右边
第四轮:
第1次,arr[0]与arr[1],2>1成立,不满足目标要求,交换arr[0]与arr[1] {1,2,6,9,9}
第四轮未排序的所有元素{2,1}已经都参与了比较,结束。
第四轮的结果:第四最大值2沉底,即到{1,2}元素的最右边

*/
public class Test19BubbleSort{
public static void main(String[] args){
int[] arr = {6,9,2,9,1};

//目标:从小到大
//冒泡排序的轮数 = 元素的总个数 - 1
//轮数是多轮,每一轮比较的次数是多次,需要用到双重循环,即循环嵌套
//外循环控制 轮数,内循环控制每一轮的比较次数和过程
for(int i=1; i<arr.length; i++){
//循环次数是arr.length-1次/轮
/*
假设arr.length=5
i=1,第1轮,比较4次
arr[0]与arr[1]
arr[1]与arr[2]
arr[2]与arr[3]
arr[3]与arr[4]

arr[j]与arr[j+1],int j=0;j<4; j++

i=2,第2轮,比较3次
arr[0]与arr[1]
arr[1]与arr[2]
arr[2]与arr[3]

arr[j]与arr[j+1],int j=0;j<3; j++

i=3,第3轮,比较2次
arr[0]与arr[1]
arr[1]与arr[2]

arr[j]与arr[j+1],int j=0;j<2; j++
i=4,第4轮,比较1次
arr[0]与arr[1]

arr[j]与arr[j+1],int j=0;j<1; j++

int j=0; j
*/
for(int j=0; j<arr.length-i; j++){
//希望的是arr[j] < arr[j+1]
if(arr[j] > arr[j+1]){
//交换arr[j]与arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}

//完成排序,遍历结果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}

4、冒泡排序优化(选讲)

/*
思考:冒泡排序是否可以优化
*/
class Test19BubbleSort2{
public static void main(String[] args){
int[] arr = {1,3,5,7,9};

//从小到大排序
//int lun = 0;//声明lun变量,统计比较几轮
//int count = 0;//声明count变量,统计比较的次数
for(int i=1; i<arr.length; i++){
//lun++;
boolean flag = true;
//假设数组已经是有序的
for(int j=0; j<arr.length-i; j++){
//count++;
//希望的是arr[j] < arr[j+1]
if(arr[j] > arr[j+1]){
//交换arr[j]与arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;

flag = false;
//如果元素发生了交换,那么说明数组还没有排好序
}
}
if(flag){
break;
}
}

//System.out.println("一共比较了" + lun +"轮");
//System.out.println("一共比较了" + count +"次");

//完成排序,遍历结果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}

相关推荐

程序员:JDK的安装与配置(完整版)_jdk的安装方法

对于Java程序员来说,jdk是必不陌生的一个词。但怎么安装配置jdk,对新手来说确实头疼的一件事情。我这里以jdk10为例,详细的说明讲解了jdk的安装和配置,如果有不明白的小伙伴可以评论区留言哦下...

Linux中安装jdk并配置环境变量_linux jdk安装教程及环境变量配置

一、通过连接工具登录到Linux(我这里使用的Centos7.6版本)服务器连接工具有很多我就不一一介绍了今天使用比较常用的XShell工具登录成功如下:二、上传jdk安装包到Linux服务器jdk...

麒麟系统安装JAVA JDK教程_麒麟系统配置jdk

检查检查系统是否自带java在麒麟系统桌面空白处,右键“在终端打开”,打开shell对话框输入:java–version查看是否自带java及版本如图所示,系统自带OpenJDK,要先卸载自带JDK...

学习笔记-Linux JDK - 安装&amp;配置

前提条件#检查是否存在JDKrpm-qa|grepjava#删除现存JDKyum-yremovejava*安装OracleJDK不分系统#进入安装文件目...

Linux新手入门系列:Linux下jdk安装配置

本系列文章是把作者刚接触和学习Linux时候的实操记录分享出来,内容主要包括Linux入门的一些理论概念知识、Web程序、mysql数据库的简单安装部署,希望能够帮到一些初学者,少走一些弯路。注意:L...

测试员必备:Linux下安装JDK 1.8你必须知道的那些事

1.简介在Oracle收购Sun后,Java的一系列产品就被整合到Oracle官网中,打开官网乍眼一看也不知道去哪里下载,还得一个一个的摸索尝试,而且网上大多数都是一些Oracle收购Sun前,或者就...

Linux 下安装JDK17_linux 安装jdk1.8 yum

一、安装环境操作系统:JDK版本:17二、安装步骤第一步:下载安装包下载Linux环境下的jdk1.8,请去官网(https://www.oracle.com/java/technologies/do...

在Ubuntu系统中安装JDK 17并配置环境变量教程

在Ubuntu系统上安装JDK17并配置环境变量是Java开发环境搭建的重要步骤。JDK17是Oracle提供的长期支持版本,广泛用于开发Java应用程序。以下是详细的步骤,帮助你在Ubuntu系...

如何在 Linux 上安装 Java_linux安装java的步骤

在桌面上拥抱Java应用程序,然后在所有桌面上运行它们。--SethKenlon(作者)无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序...

Windows和Linux环境下的JDK安装教程

JavaDevelopmentKit(简称JDK),是Java开发的核心工具包,提供了Java应用程序的编译、运行和开发所需的各类工具和类库。它包括了JRE(JavaRuntimeEnviro...

linux安装jdk_linux安装jdk软连接

JDK是啥就不用多介绍了哈,外行的人也不会进来看我的博文。依然记得读大学那会,第一次实验课就是在机房安装jdk,编写HelloWorld程序。时光飞逝啊,一下过了十多年了,挣了不少钱,买了跑车,娶了富...

linux安装jdk,全局配置,不同用户不同jdk

jdk1.8安装包链接:https://pan.baidu.com/s/14qBrh6ZpLK04QS8ogCepwg提取码:09zs上传文件解压tar-zxvfjdk-8u152-linux-...

运维大神教你在linux下安装jdk8_linux安装jdk1.7

1.到官网下载适合自己机器的版本。楼主下载的是jdk-8u66-linux-i586.tar.gzhttp://www.oracle.com/technetwork/java/javase/downl...

window和linux安装JDK1.8_linux 安装jdk1.8.tar

Windows安装JDK1.8的步骤:步骤1:下载JDK打开浏览器,找到JDK下载页面https://d.injdk.cn/download/oraclejdk/8在页面中找到并点击“下载...

最全的linux下安装JavaJDK的教程(图文详解)不会安装你来打我?

默认已经有了linux服务器,且有root账号首先检查一下是否已经安装过java的jdk任意位置输入命令:whichjava像我这个已经安装过了,就会提示在哪个位置,你的肯定是找不到。一般我们在...

取消回复欢迎 发表评论: