数组中集中了多个数据类型相同的元素,为了更好的对数组元素操作,有时候对数组排序是比不可少的,因此,下面我们讨论如何对数组排序。排序算法在数据结构中有多个,这里算法不是我们讲解的重点,我们选择其中一种:冒泡排序(排序后元素值递增)进行讲解。
冒泡排序的关键点是从后向前对相邻的两个数组元素进行比较,若后面元素的值小于前面元素的值,则让两元素交换值;否则,不进行交换。依次遍历所有元素,这样,第一趟排序后数组中的最小值就是下标为0的元素了,依次类推,我们进行第二趟排序(这时候我们无需遍历所有元素,因为数组下标为0的元素,其值已经是最小,我们只需遍历从除下标为0的所有元素),经过第二趟后,下标为1的数组元素存储的是数组中次小的值,这样对于有n个元素的数组,循环执行n-1趟后便可完成排序。当然,也可以从前向后对两个数组元素进行排序,但此时是较大者的值向后移。
[例5-6]
import java.util.Scanner;
class SortClass{
//对数组排序,arr:数组名
public void sort(int[]arr){
int temp;//交换值时作为临时变量
for(int i=0;i if(arr[k]>arr[k-1]){//后者小于前者,需要交换两者的值
temp = arr[k];
arr[k]= arr[k-1];
arr[k-1]= temp;
}
}
paint(i+1,arr);//调用数组打印方法
}
}
//打印数组元素,用于排序时检测每趟的排序结果
//time:第几趟,arr:数组名
public void paint(int time,int[] arr){
System.out.print("\n第"+time+"趟排序:");
for(int i=0;i }
}
}
public class Test5_6 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n=5;//数组元素个数
int[] arr= new int[n];
System.out.println("请从键盘上输入"+n+"个数:");
//利用Scanner的 nextInt方法从键盘输入10个数
for(int i=0;i arr[i]=scan.nextInt();
}
System.out.println("排序前数组元素值:");
for(int i=0;i }
SortClass sc = new SortClass();//生成SortClass的类对象,用以调用sort方法
sc.sort(arr);//调用SortClass的sort方法
System.out.println("\n排序后数组元素值:");//字符串中的"\n"是换行操作
for(int i=0;i }
} }
运行结果如下:
请从键盘上输入5个数:
第1个整数:52
第2个整数:45
第3个整数:68
第4个整数:96
第5个整数:32
排序前数组元素值:
52 45 68 96 32
第1趟排序:96 52 45 68 32
第2趟排序:96 68 52 45 32
第3趟排序:96 68 52 45 32
第4趟排序:96 68 52 45 32
排序后数组元素值:
96 68 52 45 32
从结果中可以看出,经过sort方法处理后,数组元素按升序进行排列了,那么,是否可以让数组元素按从大到小顺序排序的,其实,只要改变判断条件“if(arr[k]>arr[k-1])”中的“>”为“<”即可实现了,读者可自行验证。
3.数组特定数据的查找从数组中查找
从数组中查找特定数据的最简单的办法是遍历数组中的所有元素,这种插好方法也称为线性查找。以下方法indexOf()用于查找arr数组中屈指为value的元素的索引位置,若找不到,返回-1。该方法采用的就是线性查找方式。
public int indexOf(int[] arr,int value){
for(int i=0;i return i;//找到,返回对应下标
}//for循环执行完后,表示未找到,则返回-1
return -1;
}
线性查找的时间复杂度为O(n),它适用于小型数组或未排序的数组。对于大型数组,线性查找的效率比较低。如果数组已经排好序,那么我们可以采用高效的二叉查找算法。
二叉查找算法的中心思想史:查找数组中位于中间位置的元素,并将其与查找值进行比较,如果两者相等,就返回该数组元素的下标值;否则,将问题简化为查找已排序数组的一半元素——如果查找值小于数组的中间元素,就查找数组的前半部分,否则就查找数组的后半部分(假设数组按升序排序)。二叉查找的 时间复杂度是 O(log2n)。
例5-7给出了使用二叉查找算法的数组元素查找方法。
[例5-7]
class ArraySearch{
//使用二叉查找算法查找数组arr中值为value的数组元素下标,找不到返回-1
public int indexOf(int[] arr,int value){
int low=0;
int high=arr.length-1;
int middle;
while(low<=high){
middle=(low+high)/2;
if(arr[middle]==value){
return middle;
}
else
if(arr[middle] }
else{
high=middle-1;
}
}
return -1;
}
}
public class Test5_7 {
public static void main(String[] args) {
int[] arr={1,3,5,7,9,11,13};
ArraySearch as = new ArraySearch();
int index=as.indexOf(arr, 3);
System.out.println("3在数组arr中的下标位置为:"+index);
}
}
运行结果:(分找到、找不到两次实验)
找到:
3在数组arr中的下标位置为:1
找不到:(将查找的值改为:4)
4在数组arr中的下标位置为:-1
5.1.7 数组元素的复制
从前面的讲解中我们了解到,如果我们用一个已经初始化后的数组为另一个刚刚声明的数组做初始化,那么这两个数组实际上会指向同一个数组,对其中一个数组的操作会影响到另一个数组的值,在实际应该用中,经常会遇到只是想将数组的值赋值到另一个数组,被赋值的数组的修改对原数组不产生影响的情况。这时候我们就不能采用“int[]a=b”(b是一个已初始化的数组)了,可以通过如下的for循环完成:
for(int i=0;i }
在java中提供了一个现有的方法实现数组元素的复制,它就是“System.arraycopy”方法,该方法的原型为:
public static void arraycopy(Object src,int srcPos,
Object dest,int destPos, int length)
各参数的意义:
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目标数据中的起始位置。
length - 要复制的数组元素的个数。
方法的具体含义:
从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。从 src 引用的源数组到 dest 引用的目标数组,数组的一个子序列被复制下来。源数组中位置在 srcPos 到 srcPos+length-1 之间的数组 元素被分别复制到目标数组中的 destPos 到 destPos+length-1 位置。
如果参数 src 和 dest 引用相同的数组对象,则复制的执行过程就好像首先将 srcPos 到 srcPos+length-1 位置的元素复制到一个带有 length 个元素的临时数组,然后再将此临时数组的内容复制到目标数组的 destPos 到 destPos+length-1 位置一样。 不会造成读和写的冲突。
在使用该方法时,需注意,如果参数的数据错误有可能会引起以下几种异常(有关异常的内容请参见第10章)。
NullPointerException异常:这个异常的产生可能是因为源数组src或目标数组dest并没有引用到一个数组的实例,即数组没有初始化,这样数组的引用值为null,就会产生这个异常。如果这个是因为为null,则目标数组并不会去做任何的修。
ArrayStoreException异常:这个异常的产生原因可能是来源或目标数组根本就不是数组,或者是来源目标数组不是基本类型的数组。Arraycopy只适用基本数据类型的数组。
IndexOutOfBoundsException异常:这个异常的产生原因是索引值指定错误。包括srcPos、destPos和length值为负数,或者srcPos+length 大于 src.length,destPos+length 大于 dest.length。这些情况中的任何一种都会引发IndexOutOfBoundsException异常。
例[5-8]
public class Test5_8 {
public static void main(String[] args) {
int[] a={12,43,54,56,78};
int[] b=new int[8];
System.arraycopy(a, 1, b, 2, 4);
for(int i=0;i } 考试站
}
}
由上面关于arraycopy的解释我们知道System.arraycopy(a, 1, b, 2, 4),表示将数组a中下标为1开始的元素复制给数组b中下标为2开始的4个元素。
运行结果为:
0 0 43 54 56 78 0 0
可能读者会有这样的疑问,既然我们自己通过for语句也可以控制两个数组的复制,那么为什么还需要使用arraycopy方法呢?
原因有两个:系统给定的方法时经过严格测试的方法,一般不存在错误,且考虑了各种异常情况,比自己写的考虑全面;再有,arraycopy在内部实现上使用的是JNI的方法(即调用本地的其他语言所写的程序的方法),运行速度上会比我们用java写的程序要快。