一、冒泡排序
将数组中的元素按照从小到大的顺序进行排序。
21 | 8 | 5 | -8 | 0 | 12 | 9 |
---|---|---|---|---|---|---|
冒泡排序算法:
从左到右,逐个元素两两比较,如果前者比后者大,则交换这两个元素值;如果前者小于或等于后者,则不用交换;直至最后两个元素比较并交换;
8,21,5,-8,0,12,9
8,5,21,-8,0,12,9
8,5,-8,21,0,12,9
8,5,-8,0,21,12,9
8,5,-8,0,12,21,9
8,5,-8,0,12,9,21
这就完成了一趟排序。一趟过后,当前范围中的最大元素就被移动到当前范围最后的位置;该元素的最终位置就确定了,以后不会再变化。
第二趟,继续从左到右,逐个元素两两比较,前者如果大于后者,则交换;否则,不用交换。
直至倒数第二个和第三个比较完成。
8,5,-8,0,12,9,21
5,8,-8,0,12,9
5,-8,8,0,12,9
5,-8,0,8,12,9
5,-8,0,8,12,9
5,-8,0,8,9,12
第二趟完成后,倒数第二个元素就确定了最终位置。
。。。。。。
第n-1趟完成后,倒数第n-1个元素就确定了最终位置。
总结:
对于长度为n的数组,总共需要n-1趟;
每一趟要完成的任务:从下标0开始,两两比较,前者大就交换;直到下标为n-2;
for(int i=0;i<n-1;i++){
//i0时第一趟,i1时第二趟,i==2时第三趟…
for(int index=0;index<n-1-i;index++){
if(array[index]>array[index+1]){
//交换
}
}
}
//冒泡排序思路: 从前向后两两比较, 如果前面的数组元素大于后面的数组元素就进行交换
public class BubbleSort {
public static void main(String[] args) {
int[] array = {21, 8, 5, -8, 0, 12, 9};
int n = array.length;
//临时变量
int t;
//冒泡排序:外层循环控制趟数
//数组有n个元素, 需要进行 n-1轮, 可以通过循环控制比较的轮次
for (int i = 0; i < n - 1; i++) {
//内层循环:从左到右,将大的值向右交换
for (int index = 0; index < n - 1 - i; index++) { //判断第一个元素是否大于第二个元素,大于,交换两个元素的值,判断第二个元素是否大于第三个元素,大于就交换,以此类推...
if (array[index] > array[index + 1]) {
//交换两个元素的值
t=array[index];
array[index]=array[index+1];
array[index+1]=t;
}
}
}
//输出结果
showArray(array);
}
private static void showArray(int[] array) {
if (array == null) {
System.out.println("数组为null!");
return;
}
System.out.print("数组中的所有元素值:");
for (int i = 0; i < array.length; i++) {
if (i > 0) System.out.print(",");
System.out.print(array[i]);
}
System.out.println();
}
}
冒泡排序算法:另一种实现
i表示趟数,从0开始,到达n-1;
每一趟,从右向左,下标从n-1开始直至1+i,两两比较,后者如果小于前者就交换,将小的元素向前移动。
public static void main(String[] args) {
int[] array = {21, 8, 5, -8, 0, 12, 9};
int t = 0;
for (int i = 0; i < array.length - 1; i++) {
//j>0或j>i都可以
for (int j = array.length - 1; j > 0; j--) {
if (array[j] < array[j - 1]) {
t = array[j];
array[j] = array[j - 1];
array[j - 1] = t;
}
}
}
showArray(array);
}
private static void showArray(int[] array) {
if (array == null) {
System.out.println("数组为null!");
return;
}
System.out.print("数组中的所有元素值:");
for (int i = 0; i < array.length; i++) {
if (i > 0) System.out.print(",");
System.out.print(array[i]);
}
System.out.println();
}
时间复杂度:2n*2,空间复杂度1
二、选择排序
SelectionSort 选择排序:第一趟,从第一个元素开始一直遍历到末尾,找出最小元素,然后将该元素和第一个元素进行交换,也就确定了第一个元素的最终位置;第二趟,从第二个元素开始遍历到末尾,找出最小元素,然后和第二个元素交换,从而确定了第二个元素的最终位置;。。。。。。。。当第n-1趟后,也就确定了第n-1个元素的最终位置。排序结束。
public class SelectionSort {
public static void main(String[] args) {
int[] array = {21, 17, 1, -88, 0, 123, -9, 34, 28, 6};
//
selectionSort(array);
showArray(array);
}
private static void showArray(int[] array) {
if (array == null) {
System.out.println("数组为null!");
return;
}
System.out.print("数组中的所有元素值:");
for (int i = 0; i < array.length; i++) {
if (i > 0) System.out.print(",");
System.out.print(array[i]);
}
System.out.println();
}
/**
* 选择排序
*
* @param array
*/
private static void selectionSort(int[] array) {
if (array == null || array.length < 2) return;
int temp = 0;
//外层循环控制趟数
for (int i = 0; i < array.length - 1; i++) {
int index = i;//定义变量保存这一轮最小元素索引值
//内层循环从i开始到尾遍历找出最小元素的下标
for (int j = i + 1; j < array.length; j++) {
//如果有元素比min标记的元素还小,则这个元素就是当前最小的
if (array[j] < array[index]) {
index = j;//把当前最小元素索引值保存到index变量中
}
}
//1,3,2
if (index != i) {
//和下标为i的元素(本趟开始的元素)进行交换
temp = array[i];
array[i] = array[index];
array[index] = temp;
}
}
}
}
三、二分查找(折半查找)
3.1二分查找
从数组中寻找指定的元素值,判断数组中是否存在该元素值。
二分查找算法思想:
1、将数组中的所有元素从小到大排序;
2、计算待查找序列的中间位置,将查找的值和中间位置元素进行比较;
如果中间位置值比查找的值大,查找范围变为之前的范围;
如果中间位置值比查找的值小,查找范围变为之后的范围;
如果中间位置值和查找的值相等,直接返回该下标;
循环第2步,直至范围中没有元素,返回-1。
{1,25,36,47,49,51,66,77,99}
要查找25:
1、start=0,end=8; middle=(0+8)/2=4;
2、array[4]对应的值49,大于25,查找范围start=0,end=3;
3、middle=(0+3)/2=1,array[1]=25,等于要查找的值,直接返回1.
要查找69:
1、start=0,end=8; middle=(0+8)/2=4;
2、array[4]对应的值49,小于要找的值69,修改范围start=5,end=8;
3、middle=(5+8)/2=6,array[6]对应的值66,修改范围start=7,end=8;
4、middle=(7+8)/2=7,array[7]对应的值77,修改范围start=7,end=6;
5、start已经超过end,返回-1。
public class BinarySearch {
public static void main(String[] args) {
int[] array = {1, 25, 36, 47, 51, 66, 77, 99};
// 调用二分法查找,找到查找元素的索引
int index = binarySearch(array, 51);
System.out.println(index == -1 ? "没找到" : "所在的下标为:" + index);
index = binarySearch(array, 61);
System.out.println(index == -1 ? "没找到" : "所在的下标为:" + index);
index = binarySearch(array, 1);
System.out.println(index == -1 ? "没找到" : "所在的下标为:" + index);
index = binarySearch(array, 66);
System.out.println(index == -1 ? "没找到" : "所在的下标为:" + index);
}
/**
* 使用二分法查找,从数组中查找特定的值
*
* @param array 需要查找的数组
* @param key 需要查找的元素值
* @return 如果返回值为正数,则证明找到;如果返回-1,则证明没找到。
*/
private static int binarySearch(int[] array, int key) {
if (array == null || array.length == 0) return -1;
int start = 0, end = array.length - 1;
int index = -1, middle;
while (start <= end) {
middle = (start + end) / 2;
if (array[middle] == key) {
index = middle;
break;
} else if (array[middle] < key) {
start = middle + 1;
} else {
end = middle - 1;
}
}
return index;
}
}
3.2递归方法实现二分查找
public static void main(String[] args) {
int[] array = {1, 25, 36, 47, 49, 51, 66, 77, 99};
int index = binarySearch(array, 51, 0, array.length - 1);
System.out.println(index == -1 ? "没找到!" : "所在下标为" + index);
index = binarySearch(array, 61, 0, array.length - 1);
System.out.println(index == -1 ? "没找到!" : "所在下标为" + index);
index = binarySearch(array, 66, 0, array.length - 1);
System.out.println(index == -1 ? "没找到!" : "所在下标为" + index);
index = binarySearch(array, 1, 0, array.length - 1);
System.out.println(index == -1 ? "没找到!" : "所在下标为" + index);
index = binarySearch(array, 99, 0, array.length - 1);
System.out.println(index == -1 ? "没找到!" : "所在下标为" + index);
}
public static int binarySearch(int[] array, int key, int start, int end) {
if (array == null || array.length < 1) return -1;
if (start > end || start >= array.length || end >= array.length || start < 0 || end < 0) return -1;
//计算中间下标
int middle = (start + end) / 2;
if (array[middle] == key) {
return middle;
} else if (array[middle] > key) {
return binarySearch(array, key, start, middle - 1);
} else {
return binarySearch(array, key, middle + 1, end);
}
}
三、插入排序
/**
* 插入排序
*/
public class Test04Insert {
public static void main(String[] args) {
int [] ints = { 54, 34, 78, 12, 9 , 20 , 65, 11 };
//插入排序时, 把数组分为已排序与未排序两部分,
//初始时已排序部分是0元素, 从1开始的元素是未排序的
for( int i = 1; i < ints.length; i++){ //从1位置开始遍历未排序的所有数组元素
//1) 确定i位置的元素应该插入到前面已排序的位置x
int tmp = ints[i]; //先把i位置元素保存到临时变量中
int x = i; //定义变量保存tmp数据在前面已排序数组部分的索引值
//从后向前遍历已排序的数组元素, 如果数组元素比 tmp 更大,就向后移
for( ; x > 0 && ints[x-1] > tmp; x--){
ints[x] = ints[x-1];
}
//2) 把原来i位置元素保存到x位置上
ints[x] = tmp;
}
for (int anInt : ints) {
System.out.print(anInt + " ");
}
System.out.println();
}
}
四、补充知识
4.1 使用foreach循环结构遍历数组
语法:
for(变量类型 局部变量名 : 数组名){
}
每次迭代,都会将数组中的一个元素值赋值给局部变量,然后执行一次循环体。
需要确保数组不能是null值,否则会抛出NullPointerException异常。
【示例】文件名ForeachDemo.java
public class ForeachDemo {
public static void main(String[] args) {
int[] array = {23, 1, 24, 54, 4, 3, 10, -9, 55};
//l变量可以是int,long等能够容纳数组元素值的类型
//要确保array不能是null值
//foreach循环无法在循环体中获得当前元素的下标
for (long l : array) {
System.out.println("l=" + l);
}
}
}
4.2 main方法的参数String[]的使用
开发者启动程序时,可以给程序传入一个参数值。这些传入的参数值就传递给了main方法。
如果是在命令行启动程序: java 类名 参数值1 参数值2 参数值3…
如果要传递的参数值为字符串且包含了空格,需要使用引号引起来。
【示例】启动程序时,传入学生的姓名,程序中使用这些姓名提示录入各个学生的身高。
文件名:MainParamDemo.java
import java.util.Scanner;
public class MainParamDemo {
public static void main(String[] args) {
if (args == null || args.length == 0) {
System.out.println("没有传入学生姓名,程序直接结束!");
return;
}
//用来存储身高值的数组
float[] h = new float[args.length];
//
Scanner scanner = new Scanner(System.in);
int index = 0;
for (String name : args) {
System.out.print("请输入" + name + "学生的身高:");
h[index++] = scanner.nextFloat();
}
//计算平均身高
float sum = 0;
for (float h1 : h) {
sum += h1;
}
System.out.println("平均身高为:" + sum / h.length + ".");
}
}
4.3 变长参数
在定义方法时,使用变长参数的语法,使得在调用该方法时,可以传递任意数量的实参值。
【示例】定义方法,允许传入任意数量的学生姓名。
文件名:ParamVariable.java
public class ParamVariable {
public static void main(String[] args) {
showNames();
showNames("张三");
showNames("张三", "李四", "王五");
}
/**
* 定义方法,允许传入任意数量的学生姓名。
*
* @param names
*/
public static void showNames(String... names) {
System.out.println("姓名的个数:" + names.length);
//在方法体内部,变长参数可以被看作数组
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
System.out.println("-----------------------");
}
}
提示:
1、变长参数只能传递任意数量的同一种类型的实参值;
2、一个方法最多只能有一个变长参数,且变长参数必须位于最后。
3、重载的方法,如果有变长参数,还有非变长参数,则优先匹配非变长参数的方法。
public static void main(String[] args) {
showNames();
showNames("张三");
showNames("张三", "李四", "王五");
showScores("赵六",23.5f);
}
public static void showScores(String name,float... scores){
System.out.println("float... scores");
}
public static void showScores(String name,float scores){
System.out.println("float scores");
}
4.4 数组元素的访问
数组分配内存空间时,肯定是连续的内存空间。
int[] array=new int[100];
数组名array是个引用变量,它保存了堆内存中数组对象的首地址。
每个存储单元占4个字节(int);
当程序中访问 array[6] 元素时,直接计算 array+6*4 的结果就是对应存储单元的内存地址。
访问数组中的任意元素,都是根据地址直接访问对应的内存,因此速度极快。