第4章 数组
数组是一种重要的数据结构。一个数组是具有同一数据类型的对象的集合。数据类型可以是简单类型,也可以是类。数组可以是一维的,也可以是多维的,通过其下标可以访问数组元素。数组提供了一种将有联系的信息进行分组的有效方法。本章将详细讲解数组的应用。
实例13 一维数组复制、插入和合并
本质上讲,数组是一组相关的存储单元,这些存储单元在逻辑上被看作是相互独立的若干元素,它们具有相同的名字和数据类型。数组中的元素位置由它的下标决定。对数组的操作包括数组的复制、数组的合并、插入以及搜索等。本实例将讲解如何实现这些功能。
技术要点
本实例根据Java提供的不同方法操作数组。技术要点如下:
• 数组复制:通过System.arraycopy()方法实现数组的复制。
• 数组插入与数组删除相似,当插入元素后,插入点后面的每一个元素向后移一个位置,而删除是向前移一个位置。需要注意的是在插入元素前,必须对数组进行排序。
实现步骤
(1)新建一个类名为TextOperatorArray.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 import java.util.Arrays; //引入类 import java.util.Scanner; public class TextOperatorArray { //对数组元素进行各种操作的类 public static void copy(){ //数组复制 int array[]=new int[]{1,2,3,4}; //声明数组并初始化 int temp1[]=new int[array.length]; //声明数组长度为array数组的长度 int temp2[]=new int[array.length]; System.arraycopy(array, 0,temp1,0,array.length); //将数组array复制给数组temp1 System.out.println("复制后的数组结果:"); for(int i=0;i<temp1.length;i++) //循环显示数组元素 System.out.print(temp1[i]+" "); System.out.println(); temp2=array; //使用赋值将数组array复制给数组temp2 } public static void insert(){ //数组插入 int i,j; int n=5; int num[]=new int[n+1]; for(i=0;i<num.length-1;i++){ num[i]=(i+1)*6; } int length=num.length; //获得数组长度 System.out.println("插入数字之前的数组为:"); for(i=0;i<length;i++) //循环显示数组元素 if(num[i]==0) System.out.print("存数空间"); else System.out.print(num[i]+" "); System.out.println(); System.out.println("输出一个要插入的数:"); Scanner scan=new Scanner(System.in); //键盘输入一个数 int in=scan.nextInt(); for(i=0;i<length-1;i++){ //循环查找一个大于要插入数的位置 if(num[i]>in) //找到大于要插入的数就跳出 break; } for(j=length-1;j>i;j--){ //为要插入的数留出位置 num[j]=num[j-1]; } num[i]=in; //将要插入的数保存到该位置 for(i=0;i<length;i++) //循环显示数组元素 System.out.print(num[i]+" "); System.out.println(); } public static int[] combine(int[] a, int[] b) { //数组合并并排好序 int alen = a.length; //获得传入数组的长度 int blen = b.length; int length = alen + blen; int i, j; System.out.println("合并之间的两个数组:"); for(i=0;i<alen;i++) //循环显示a数组元素 System.out.print(a[i]+" "); System.out.println(); for(i=0;i<blen;i++) //循环显示b数组元素 System.out.print(b[i]+" "); System.out.println(); int[] c = new int[length]; //声明数组长度为传入两个数组长度之和 for (i = 0, j = 0; i < alen && j < blen;){ //循环查看元素进行比较 if (a[i] < b[j]) { //判断两个数组元素值的大小 c[i + j] = a[i];// i++; } else { c[i + j] = b[j]; j++; } } if (i == alen) //将b数组从下标为j开始将值赋给c数组,放在c数组的alen+j,blen-j之间 System.arraycopy(b, j, c, alen + j, blen - j); if (j == blen) //将a数组从下标为i开始将值赋给c数组,放在c数组的blen+i,alen-i之间 System.arraycopy(a, i, c, blen + i, alen - i); System.out.println("合并并排序之后的新数组:"); for (int k = 0; k < a.length + b.length; k++) //循环输出合并后的数组的元素 System.out.print(c[k]+" "); System.out.println(); return c; } public static void main(String []args){ //Java程序的主入口处 System.out.println("1.数组复制:"); copy(); //调用数组复制方法 int a[] = { 1, 2, 3, 12 }; //声明数组并初始化 int b[] = { 5, 6, 7, 8 }; System.out.println("2.数组合并:"); int c[] = combine(a, b); //调用数组合并方法 System.out.println("3.数组插入:"); insert(); //调用数组插入方法 } }
(3)运行结果如下所示:
1.数组复制: 复制后的数组结果: 1 2 3 4 2.数组合并: 合并之间的两个数组: 1 2 3 12 5 6 7 8 合并并排序之后的新数组: 1 2 3 5 6 7 8 12 3.数组插入: 插入数字之前的数组为: 6 12 18 24 30 存数空间 输出一个要插入的数: 20 6 12 18 20 24 30
源程序解读
(1)在copy()方法中,运用System.arraycopy()方法对数组进行复制。方法中声明两个数组,其中array数组在声明时初始化。temp1数组的长度为array数组的长度以便进行复制。进行复制时arraycopy(array,0,temp1,1,array.length)方法表示将array数组从第一个元素开始复制到temp1数组下标从0到array.length的位置。
(2)在insert()方法中,运用循环对数组初始化并排好序。接受通过键盘输入的待插入的数值。循环先查找第一个大于要插入数的位置,然后再运用循环为要插入的数留出位置,将待插入的数放到该位置。
(3)在combine()方法中,声明一个新数组,长度为传入数组的长度之和。循环查看传入的两个数组元素,将两个数组的元素依次进行大小比较,比较之后的元素放入新数组。通过arraycopy()方法将未放入新数组的元素复制进去,完成数组的合并与新数组的排序。
实例14 数组排序
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。常用的排序方法有冒泡排序、快速排序、选择排序、插入排序、希尔(Shell)排序、堆排序。本实例对常见排序方法进行讲解。
技术要点
本实例讲述几种排序的运算方法。技术要点如下:
• 冒泡排序是依次比较相邻的两个数,将大数放在前面,小数放在后面。即首先比较第一个和第二个数,将大数放前,小数放后。如此继续,直至比较最后两个数,将大数放前,小数放后。然后下一行再进行重复操作。
• 选择排序是每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。选择排序是不稳定的排序方法。
• 插入排序(Insertion Sort)把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,但将最后一个元素除外,而第二部分就只包含这一个元素。在第一部分排序后,再把这个最后元素插入到此刻已是有序的第一部分里的正确位置中。
• 希尔排序(Shell Sort)是插入排序的一种。基本思想:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<⋯<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
实现步骤
(1)创建一个类名为TextSort.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 public class TextSort { //操作排序的类 public static void bubbleSort(int[] x) { //冒泡排序 for (int i = 0; i < x.length; i++) { for (int j = i + 1; j < x.length; j++) { if (x[i] > x[j]) { //将下标为i的数与下标为j的数进行比较 int temp = x[i]; //元素交换 x[i] = x[j]; x[j] = temp; } } } for (int i = 0; i < x.length; i++) { //循环将重新排序的结果输出 System.out.print(x[i] + " "); } } public static void selectSort(int[] x) { //选择排序 for (int i = 0; i < x.length; i++) { int lowerIndex = i; for (int j = i + 1; j < x.length; j++) { //循环找出最小的一个索引 if (x[j] < x[lowerIndex]) { lowerIndex = j; } } int temp = x[i]; //交换 x[i] = x[lowerIndex]; x[lowerIndex] = temp; } for (int i = 0; i < x.length; i++) { //循环将重新排序的结果输出 System.out.print(x[i] + " "); } } public static void insertSort(int[] x) { //插入排序 for (int i = 1; i < x.length; i++) { //i从1开始,因为第一个数已经是排好序的 for (int j = i; j > 0; j--) { if (x[j] < x[j - 1]) { int temp = x[j]; //元素交换 x[j] = x[j - 1]; x[j - 1] = temp; } } } for (int i = 0; i < x.length; i++) { //循环将重新排序的结果输出 System.out.print(x[i] + " "); } } public static void shellSort(int[] x){ //希尔排序 for (int increment = x.length / 2; increment > 0; increment /= 2){ //循环进行分组 for (int i = increment; i < x.length; i++) { //循环每个组内排序 int temp = x[i]; int j = 0; for (j = i; j >= increment; j -= increment) { if (temp < x[j - increment]) { //元素进行判断、交换 x[j] = x[j - increment]; } else { break; } } x[j] = temp; } } for (int i = 0; i < x.length; i++) { //循环将重新排序的结果输出 System.out.print(x[i] + " "); } } public static void main(String[] args) { //Java程序的主入口处 int[] arr = { 1, 5, 6, 12, 4, 9, 3, 23, 39, 403, 596, 87 }; System.out.println("----冒泡排序的结果:"); bubbleSort(arr); //调用冒泡排序方法 System.out.println(); System.out.println("----选择排序的结果:"); selectSort(arr); //调用选择排序方法 System.out.println(); System.out.println("----插入排序的结果:"); insertSort(arr); //调用插入排序方法 System.out.println(); System.out.println("----希尔(Shell)排序的结果:"); shellSort(arr); //调用希尔(Shell)排序方法 } }
(3)运行结果如下所示:
----冒泡排序的结果: 1 3 4 5 6 9 12 23 39 87 403 596 ----选择排序的结果: 1 3 4 5 6 9 12 23 39 87 403 596 ----插入排序的结果: 1 3 4 5 6 9 12 23 39 87 403 596 ----希尔(Shell)排序的结果: 1 3 4 5 6 9 12 23 39 87 403 596
源程序解读
(1)在bubbleSort()方法中,运用双重循环对数组进行冒泡排序。其中i、j控制数组的元素。将下标为i的元素与下标为j的元素进行比较,将较小的元素与较大的元素依次用中间变量进行转换。得到的结果是较小的元素在前,较大的元素在后。然后将排好序的数组元素打印到控制台。
(2)在selectSort()方法中,运用选择排序法对数组进行排序。方法中第一层循环从起始元素开始选到最后一个元素,主要是在每次进入第二层循环之前,将外层循环的下标赋值给临时变量lowerIndex,接下来在第二层循环中,如果发现有比下标为lowerIndex更小的元素,则将值赋给lowerIndex,最后,在二层循环退出后,如果临时变量改变,则说明有比当前外层循环位置更小的元素,需要将这两个元素交换。
(3)在insertSort()方法中,运用插入排序对数组进行排序。从第一个元素开始,该元素可以认为已经被排序。循环取出下一个元素,在已经排序的元素序列中从后向前扫描,如果该元素(已排序)大于新元素,则将该元素移到下一位置,重复进行操作,直到找到已排序的元素小于或者等于新元素的位置,再将新元素插入到该位置中。
(4)在shellSort()方法中,先将数组进行分组,对分得的每个小组进行组内排序。在每个小组内对元素进行比较,将每个小组组内的元素由小到大进行排序。再次循环分组,直到元素排序完毕。
实例15 数组搜索
一个数组是具有同一数据类型的对象的集合。对数组元素进行逐个查找显然是费时费力的工作。本实例将讲解如何快速地搜索出数组中元素的指定位置。
技术要点
本实例根据二分搜索法对数组元素进行搜索。技术要点如下:
• 二分搜索法充分利用了元素间的次序关系,基本思想:将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止。如果x<a[n/2],则只要在数组a的左半部继续搜索x(这里假设数组元素呈升序排列)。如果x>a[n/2],则只要在数组a的右半部继续搜索x。
• 二分搜索法的局限性:必须是在有序的元素中进行的,不能在无序的元素中使用。
实现步骤
(1)创建一个类名为TextArraySearch.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 import java.util.Arrays; public class TextArraySearch { //运用递归和二分搜索法搜索数组元素的类 //运用递归和二分搜索法查找特定整数在整型数组中的位置 public static int binarySearch1(int[] array, int index, int beginIndex, int endIndex) { int midIndex = (beginIndex + endIndex) / 2; if (index < array[beginIndex] || index > array[endIndex] || beginIndex > endIndex) //判断要搜索的数字是否合理 return -1; if (index < array[midIndex]) { //搜索数字位于数组前半部分 return binarySearch1(array, index, beginIndex, midIndex - 1);//运用递归 } else if (index > array[midIndex]) { //搜索数字位于数组后半部分 return binarySearch1(array, index, midIndex+1, endIndex); //运用递归 } else { return midIndex; //搜索数字位于数组中间 } } //运用非递归和二分搜索法查找特定整数在整型数组中的位置 public static int binarySearch2(int[] array, int index) { int beginIndex = 0; //起始位置,指的是元素下标 int endIndex = array.length - 1; //结束位置 int midIndex = -1; if (index < array[beginIndex] || index > array[endIndex] || beginIndex > endIndex) //判断要搜索的数字是否合理 return -1; //循环判断,根据起始位置与结束位置是否相等 while (beginIndex <= endIndex) { midIndex = (beginIndex + endIndex)/2; //获得数组中间位置 if (index < array[midIndex]) { //搜索数字位于数组前半部分 endIndex = midIndex - 1; //重新定位结束位置 } else if (index > array[midIndex]) { //搜索数字位于数组后半部分 beginIndex = midIndex + 1; //重新定位起始位置 } else { return midIndex; //搜索数字位于数组中间 } } return -1; } public static void main(String []args){ //Java程序的主入口处 int []array=new int[]{12,3,2,18,24,15,20}; //声明数组并初始化 int number=18; //声明变量 int num=1; //二分搜索法搜索元素之前必须对数组进行排序 Arrays.sort(array); System.out.println("元素"+number+"所在的位置为:"+binarySearch1(array, number,0,array.length-1)); System.out.println("元素"+number+"所在的位置为:"+binarySearch2(array,number)); System.out.println("元素"+num+"所在的位置为:"+binarySearch2(array,num)); } }
(3)运行结果如下所示:
元素18所在的位置为:4 元素18所在的位置为:4 元素1所在的位置为:-1
源程序解读
(1)在binarySearch1()方法中,获得传递的起始位置与结束位置的中间位置作为二分点。判断搜索的数字的位置是在二分点的前面还是后面。位于前面则搜索的数字小于位于二分点的数字,位于后面则搜索的数字大于二分点的数字。运用递归方式再调用binarySearch1()方法进行二分搜索。
(2)在binarySearch2()方法中,设置起始位置与结束位置再获得二分点。根据起始位置与结束位置进行while()循环,判断搜索的数字在数组二分点的数字前面还是后面。位于前面则将结束位置重新设置。位于后面则将起始位置重新设置。
实例16 去掉数组重复数字
在上一实例中讲解了常用的排序方法,接下来要去掉一个已经排好序的数组的重复数字,执行速度也是要考虑的问题。
技术要点
本实例根据数组下标删除指定的数组元素。技术要点如下。
提供两种思路解决去掉重复数字:第一种是增加一个数组,但是长度无法确定,记录没有重复的值;第二种是增加一个数组,用于记录原数组中存在的数,再增加一个数组可以确定数组的长度,用于存放原数组的值。用来比较哪种方法最适用。
实现步骤
(1)创建一个类名为TextDelRepeat.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 public class TextDelRepeat { //操作去掉数组中重复数字的类 public static int[] changeMethodOne(int src[]) { //方法1 去掉重复数字 int length = src.length; //获得传入数组的长度 int[] taget = new int[length]; //声明一个数组,长度为传入数组的长度
int index = 0; taget[0] = src[0]; //设置数组的初始值 for (int i = 1; i < length; i++) { //循环遍历传入数组 if (taget[index] != src[i]) { //遍历数组与初始值进行比较 index++; //等价于index=index+1 taget[index] = src[i]; //元素赋值 } } return taget; //返回结果数组 } public static int[] changeMethodTwo(int src[]) { //方法2 去掉重复数字 int length = src.length; //获得传入数组的长度 int[] tagetIndex = new int[length]; //声明一个数组,长度为传入数组的长度 int tagetLength = length; for (int i = 0; i < length; i++) { tagetIndex[i] = 0; //初始化tagetIndex } for (int j = 1; j < length; j++) { //记录重复的值 if (src[j] == src[j - 1]) { tagetIndex[j] = 1; tagetLength--; } } //声明数组,长度为传入数组长度-重复数字的个数 int[] target = new int[tagetLength]; int index = 0; for (int k = 0; k < length; k++) { //循环将数组赋值 if (tagetIndex[k] == 0) { //数组元素等于1是存放重复数字 target[index++] = src[k]; } } return target; } public static void main(String[] args) { int[] a = { 1, 1, 1, 2, 3, 4, 5, 6, 9, 9, 12, 53 }; //声明数组并初始化 int[] b = changeMethodOne(a); //调用第一种方法去掉重复数字 System.out.println("第一种方法去掉数组中重复数字结果:"); for (int i = 0; i < b.length; i++) { //将返回的数组输出 System.out.print(b[i] + " "); } System.out.println(); System.out.println("第二种方法去掉数组中重复数字结果:"); int c[] = changeMethodTwo(a); //调用第二种方法去掉重复数字 for (int i = 0; i < c.length; i++) { //将返回的数组输出 System.out.print(c[i] + " "); } } }
(3)运行结果如下所示:
第一种方法去掉数组重复数字结果: 1 2 3 4 5 6 9 12 53 0 0 0 第二种方法去掉数组重复数字结果: 1 2 3 4 5 6 9 12 53
源程序解读
(1)在changeMethodOne()方法中,声明一个与传入数组长度相等的数组,根据循环判断是否有重复的数字,并将不重复的数字放入声明的数组中,如果传入数组中存在重复的数字,那么这个数组的长度应会小于传入数组的长度,这样就造成了数组长度的浪费。
(2)在changeMethodTwo()方法中,也声明一个与传入数组长度相等的数组tagetIndex,值初始化全为0。循环进行判断重复数字的个数,如果存在重复数字则数组tagetIndex元素赋值为1。然后再声明一个数组target,长度为(tagetIndex.length-重复数字个数)。当tagetIndex数组中元素为0的,将数组src元素下标等于tagetIndex元素中不为0的下标的元素值赋值给target。
实例17 求质数(素数)
在所有比1大的整数中,除了1和它本身以外,不再有别的因数,这种整数叫做质数或素数。还可以说成质数只有1和它本身两个约数。本实例将讲解输出在指定范围内所有的质数。
技术要点
本实例主要采用筛选法快速求出指定范围内的质数。技术要点如下:
具体做法是:先把N个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面的所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数。
实现步骤
(1)创建一个类名为TextPrimeNumber.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 import java.util.Arrays; //导入类 public class TextPrimeNumber { //操作求指定范围内的质数的类 private static boolean[] filterNumber(int num){ //筛选法求质数 if(num<=0){ //判断指定的范围 System.out.println("范围必须大于0"); return null; } boolean[]isPrime=new boolean[num+1]; //声明布尔类型数组,长度为范围+1 //数组标注是否为质数,下标值为质数,那么对应数组元素值为true //例如2是质数,isPrime[2]=true isPrime[1]=false; //1不是质数 Arrays.fill(isPrime, 2,num+1,true); //将布尔数组元素的值都赋为true int n=(int)Math.sqrt(num); //Math.sqrt方法用于求开方 for(int i=1;i<n;i++){ if(isPrime[i]){ //如果是质数,那么i的倍数不是质数 for(int j=2*i;j<=num;j+=i){ isPrime[j]=false; } } } return isPrime; } public static void showAppointArea(int number){ //显示指定范围内的质数 boolean [] primes=FilterNumber(number); //调用方法赋值给布尔类型的数组 int num=0; if(primes!=null){ for(int i=1;i<primes.length;i++){ //循环数组操作数组的元素 if(primes[i]){ //如果数组元素值为true,则下标值为质数 System.out.print(i+" "); //输出质数 if(++num%10==0) //每输出10个质数换行 System.out.println(); } } System.out.println(); } System.out.println("一共有"+num+"个"); } public static void main(String[] args) { //Java程序的主入口处 int number = 200; //声明范围 System.out.println("范围在"+number+"内的质数有:"); showAppointArea(number); //调用方法显示质数 } }
(3)运行结果如下所示:
范围在200内的质数有: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 一共有46个
源程序解读
(1)filterNumber()方法使用筛选法求传入参数值范围内的所有的质数。声明一个布尔类型的数组,数组元素的值为true时,表示该元素的下标为质数。如果number是一个质数,那么number的位数都不是质数,利用Array.fill()方法初始化布尔类型数组,然后利用循环将数组下标为number的倍数的元素值设置为false。这样就能判断哪部分元素是质数了。
(2)showAppointArea()方法调用FilterNumber()方法,得到布尔类型数组,将值为true的元素的下标在控制台输出。
实例18 矩阵的加减和转置
在数学上,矩阵是由方程组的系数及常数所构成的方阵。用在解线性方程组上既方便又直观。生活中通过矩阵多因素探索解决问题。本实例实现了矩阵的基本运算,包括矩阵的构造、矩阵的加法、矩阵的减法和转置矩阵等。
技术要点
技术要点如下:
• 对数组进行初始化有两种方法:在声明变量时直接初始化;运用循环对其进行初始化。
• Java支持多维数组,可以用二维数组表示矩阵。
• 对矩阵进行操作前,需要进行合法性验证,判断它们是否能进行运算。
实现步骤
(1)新建一个类名为TextMatrix.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 import java.text.DecimalFormat; //引入类 public class TextMatrix { //操作矩阵的类,使用二维数组 private double[][]data; //矩阵数据 public TextMatrix(){} //默认构造函数 public TextMatrix(double[][]data){ //初始化矩阵 if(CanTransToMatrix(data)) //判断数组是否能转换成矩阵 this.data=this.cloneArray(data); } private static boolean CanTransToMatrix(double[][]data){//判断二维数组能够转换成矩阵 if(data==null) //判断二维数组是否为空 return false; for(int i=0;i<=data.length-2;i++){ //循环依次比较如果长度不等则返回false if(data[i].length!=data[i+1].length) //数组长度比较 return false; } return true; } public String showArray(double [][]data){ //格式化数组 DecimalFormat format=new DecimalFormat("0.00"); //数据格式化保留两位小数 StringBuffer buffer=new StringBuffer(""); //声明StringBuffer可以修改数据 for(int i=0;i<data.length;i++){ for(int j=0;j<data.length;j++){ //遍历二维数组按格式显示 //将数组元素转换为指定格式 buffer.append(format.format(data[i][j])).append(" "); } buffer.append("\n");//换行 } return buffer.toString(); } public void showData(){ //调用方法显示二维数组 System.out.println(showArray(this.data)); } private double[][]cloneArray(double [][]data){ //克隆一个二维数组 if(data==null) return null; return (double [][])data.clone(); } public double [][]getMatrixData(){ //获得矩阵 return cloneArray(this.data); } public TextMatrix add(TextMatrix t){ //矩阵加法运算 if(t==null) return null; TextMatrix text=null; double [][]tmData=t.getMatrixData(); //获得一个矩阵 if((this.data.length!=tmData.length) || (this.data[0].length!=tmData[0].length)){ //判断矩阵行数列数是否相等 System.out.println("两个矩阵大小不一"); return text; }else{ double [][]result=new double[this.data.length][this.data[0].length]; for(int i=0;i<this.data.length;i++){ //依次循环行数 for(int j=0;j<this.data[0].length;j++){ //依次循环列数 result[i][j]=this.data[i][j]+tmData[i][j]; //两个矩阵相加 } } text=new TextMatrix(result); //将新生成的矩阵传入对象中 return text; //返回对象 } } public TextMatrix subtration(TextMatrix t){ //矩阵减法运算 if(t==null) return null; TextMatrix text=null; double [][]tmData=t.getMatrixData(); //获得一个矩阵 if((this.data.length!=tmData.length) || (this.data[0].length!=tmData[0].length)){//判断矩阵行数列数是否相等 System.out.println("两个矩阵大小不一"); return text; }else{ double [][]result=new double[this.data.length][this.data[0].length]; for(int i=0;i<this.data.length;i++){ //依次循环行数 for(int j=0;j<this.data[0].length;j++){ //依次循环列数 result[i][j]=this.data[i][j]-tmData[i][j]; //两个矩阵相减 } } text=new TextMatrix(result); //将新生成的矩阵传入对象中 return text; //返回对象 } } public TextMatrix transposeMatrix() { //矩阵转置,格式为a[i][j]=b[j][i] int Row = this.data[0].length; int Column = this.data.length; double[][] change = new double[Row][Column]; //声明一个二维数组,长度指定 for (int i = 0; i < Row; i++) { for (int j = 0; j < Column; j++) { change[i][j] = this.data[j][i]; //循环进行转置 } } return new TextMatrix(change); } public static void main(String []args){ //Java程序的主入口处 //初始化的方式构造一个二维数组 double[][]data1=new double[][]{{1.0,2.0,3.0},{4.0,5.0,6.0},{7.0,8.0,9.0}}; double[][]data2=new double[3][3]; //声明一个二维数组,没有初始化 for(int i=0;i<3;i++){ //for循环依次初始化二维数组 for(int j=0;j<3;j++){ data2[i][j]=2*i+j; //进行赋值 } } TextMatrix matrix1=new TextMatrix(data1); TextMatrix matrix2=new TextMatrix(data2); System.out.println("两组二维数组展示:"); matrix1.showData(); //二维数组展示 matrix2.showData(); System.out.println("矩阵加法运算结果:"); matrix1.add(matrix2).showData(); //显示矩阵加法运算结果 System.out.println("矩阵减法运算结果:"); matrix1.subtration(matrix2).showData(); //显示矩阵减法运算结果 System.out.println("矩阵matrix1转置结果:"); matrix1.transposeMatrix().showData(); //矩阵转置的结果 } }
(3)运行结果如下所示:
两组二维数组展示: 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 0.00 1.00 2.00 2.00 3.00 4.00 4.00 5.00 6.00 矩阵加法运算结果: 1.00 3.00 5.00 6.00 8.00 10.00 11.00 13.00 15.00 矩阵减法运算结果: 1.00 1.00 1.00 2.00 2.00 2.00 3.00 3.00 3.00 矩阵matrix1转置结果: 1.00 4.00 7.00 2.00 5.00 8.00 3.00 6.00 9.00
源程序解读
(1)本实例根据不同的方法对二维数组进行初始化,并采用二维数组存放矩阵的数据。并对要操作的两个数组进行行和列是否一致的判断。
(2)add()方法实现了矩阵的加法运算,根据矩阵加法的条件,判断参与加法的两个矩阵是否可加,并将结果数组变换为矩阵对象返回。
(3)sub()方法实现了矩阵减法运算,当前对象是被减数,输入参数对象是减数。
(4)clone()方法用于克隆一个矩阵。
实例19 数组实现顺序栈与队列
栈和队列是两种特殊的线性表,它们的逻辑结构和线性表相同,只是其运算规则较线性表有更多的限制。本实例介绍如何使用顺序栈、顺序队列和优先队列以及使用的规则和要领。
技术要点
数组实现顺序栈和队列的技术要点如下:
• 栈是一种数据结构,限制仅在表的一端进行插入和删除运算的线性表。其数据项的插入和删除(获取)都只能在称为栈顶的一端完成。因为最后插入的数据项就是最先要删除的数据项。当数据项中没有元素时称为空栈。栈为后进先出(Last In First Out)的线性表,简称LIFO表。
• 顺序栈:栈的顺序存储结构,它是运算受限的顺序表。顺序栈的元素用向量存放。栈底位置是固定不变的,可设置在向量两端的任意一个端点。栈顶位置是随着进栈和出栈操作而变化的,用一个整型量top(栈顶指针)来指示。
• 当程序中同时使用两个栈时,可以将两个栈的栈底设在向量空间的两端,让两个栈各自向中间延伸。当一个栈里的元素较多,超过向量空间的一半时,只要另一个栈的元素不多,那么前者就可以占用后者的部分存储空间。只有当整个向量空间被两个栈占满时,才会发生上溢。因此,两个栈共享一个长度为m的向量空间和两个栈分别占用两个长度」m/2」和 m/2」为的向量空间比较,前者发生上溢的概率比后者要小得多。」
• 队列是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。允许删除的一端称为队头,允许插入的一端称为队尾,当队列中没有元素时称为空队列。队列可称为先进先出(First In First Out)的线性表,简称FIFO表。
• 顺序队列:队列的顺序存储结构,实际上是运算受限的顺序表。和顺序表一样,顺序队列用一个向量空间来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,设置两个指针front和rear分别指示队头元素和队尾元素在向量空间中的位置,它们的初值在队列初始化时均应置为0。
实现步骤
(1)新建一个类名为TextStackAndQueue.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 class Stack { //实现顺序栈的类 long stackArray[]; //栈数组 int size; //栈的大小 int top; //栈的顶部 public Stack(int size) { //构造方法初始化大小为size的栈 this.size = size; this.stackArray = new long[size]; this.top = -1; } public long pop() { //出栈操作 return stackArray[top--]; } public void push(long value) { //入栈操作 stackArray[++top] = value; } public boolean isEmpty() { //判断栈是否为空 return top == -1; } public boolean isFull() { //判断栈是否已满 return top == size - 1; } public long peek() { //取栈顶元素 return stackArray[top]; } } class Queue { // 实现顺序队列的类 private long queueArray[]; //队列数组 private int front; //队列的前端下标 private int rear; //队列的尾端下标 private int size; //队列的大小 private int count; //队列中元素的个数 public Queue(int size) { //构造方法初始化大小为size的队列 this.queueArray = new long[size]; this.size = size; this.front = 0; this.rear = -1; this.count = 0; } public void insert(long value) { //插入操作 if (rear == size - 1) //队列已满 rear = -1; queueArray[++rear] = value; count++; } public long remove() { //删除操作 long temp = queueArray[front++]; if (front == size) front = 0; count--; return temp; } public long peakFront() { //返回队列第一个元素 return queueArray[front]; } public boolean isEmpty() { //判断是否为空 return count == 0; } public boolean isFull() { //判断是否已满 return count == size; } public int Count() { //返回队列中元素的个数 return count; } public void print() { //输出队列元素 for (int i = front; i < front + count; i++) { System.out.print(queueArray[i] + "\t"); } System.out.println(); } } class PriorityQueue { //实现优先队列的类 private int count; //队列中元素的个数 private long priorityArray[]; //队列数组 private int size; //队列的大小 public PriorityQueue(int size) { //构造方法初始化大小为size的队列 this.size = size; this.priorityArray = new long[size]; this.count = 0; } public void insert(long value) { //插入操作 int i; if (count == 0) priorityArray[count++] = value; else { for (i = count - 1; i >= 0; i--) { //循环找到比插入值大的位置 if (value < priorityArray[i]) { priorityArray[i + 1] = priorityArray[i]; //依次移动位置 } else break; } priorityArray[i + 1] = value; //插入值放到指定位置 count++; } } public long remove() { //删除操作 return priorityArray[--count]; } public boolean isEmpty() { //判断是否为空 return count == 0; } public boolean isFull() { //判断是否已满 return count == size; } public void print() { //输出队列元素 for (int i = 0; i < count; i++) System.out.print(priorityArray[i] + "\t"); System.out.println(); } } public class TextStackAndQueue { //操作顺序栈与队列的类 public static void main(String[] args) { //Java程序主入口处 System.out.println("1.数组实现顺序栈"); Stack stack = new Stack(6); //实例化顺序栈,栈的大小为6 while (!stack.isFull()) { //只要栈不满便循环 long r = (long) (Math.random() * 20); stack.push(r); //入栈 System.out.print(r + "\t"); } System.out.println(); while (!stack.isEmpty()) { //只要栈不空便循环 long value = stack.pop(); //获得栈顶元素 System.out.print(value + "\t"); } System.out.println(); System.out.println("-------------------------------------"); System.out.println("2.数组实现顺序队列"); Queue queue = new Queue(6); //实例化顺序队列,队列的大小为6 while (!queue.isFull()) { //只要队列不满便循环 long value = (long) (Math.random() * 20); queue.insert(value); //元素插入队列 } queue.print(); //输出队列元素 while (!queue.isEmpty()) { //只要栈不空便循环 queue.remove(); //元素移除 queue.print(); //输出队列元素 } queue.print(); //输出队列元素 System.out.println(queue.isEmpty()); //队列是否为空? System.out.println("-------------------------------------"); System.out.println("3.数组实现优先队列"); PriorityQueue priority = new PriorityQueue(6); //实例化顺序队列,队列的大小为6 while (!priority.isFull()) { //只要队列不满便循环 long value = (long) (Math.random() * 20); priority.insert(value); //元素插入队列 } priority.print(); //输出队列元素 } }
(3)运行结果如下所示:
1.数组实现顺序栈 10 17 17 6 0 9 9 0 6 17 17 10 ------------------------------------- 2.数组实现顺序队列 16 8 18 8 8 9 8 18 8 8 9 18 8 8 9 8 8 9 8 9 9 true ------------------------------------- 3.数组实现优先队列 0 2 6 7 13 18
源程序解读
(1)Stack类实现顺序栈,包括入栈、出栈、置栈空、判断栈是否为空以及判断栈是否已满和取栈内的元素。入栈的push()方法需要将栈顶top加1,需要注意的是,top =size-1表示栈满,当栈满时再做入栈运算产生空间溢出的现象,简称“上溢”。出栈的pop()方法需要将栈顶top减1,top<0表示空栈。当栈空时,做出栈运算产生溢出现象,简称“下溢”。当取栈内元素时,由于栈是先进后出的,则取到的元素是最后放入的元素。
(2)Queue类实现顺序队列,包括入队、出队、置空队列、判断队列是否为空以及判断队列是否已满和取队列中的元素。入队的insert()方法将新元素插入到rear所指的位置,然后rear加1。需要注意的是,rear=size-1表示队列已满,当队列满时,做进栈运算产生空间溢出的现象,简称“真上溢”。当队列中实际的元素个数远远小于向量空间的规模时,也可以由于尾指针已超越向量空间的上界而不能做入队操作,该现象称为“假上溢”。出队的remove()方法删去front所指的元素,然后将front加1并返回被删的元素。当count为空时,做出队运算产生溢出,称为“下溢”。“下溢”常用作程序控制转移的条件。当取队列中的元素时,由于队列是先进先出的,则取到的元素是最先放入的元素。
(3)PriorityQueue类实现优化队列,对队列中的元素进行从小到大的排序。入队的insert()方法循环查找一个大于要插入元素的位置,当找到大于要插入的元素时就跳出,然后为要插入的元素留出位置,将要插入的元素保存到该位置。再根据队列元素是否为空和是否已满条件输出队列中已排序的元素,这样就实现了队列的优化操作。
实例20 Arrays数组的应用
Java中提供Arrays类协助我们对数组的一些基本操作。像排序、搜索与比较等是很常见的。本实例将详细讲解Arrays类操作数组的一些方法和用途。
技术要点
本实例通过操作Arrays类的静态方法来操作数组。技术要点如下:
• 赋值:当配置一个数组之后,会依数据类型来给定默认值。可以使用Arrays.fill()方法将所有的元素设定为指定的值。
• 排序:sort()方法帮助对指定的数组排序,所使用的是快速排序法。
• 查找元素:binarySearch()对已排序的数组进行二分搜索,如果找到指定的值就返回这个值所在的索引,否则就返回负值。
• 比较:equals()比较两个数组中的元素值是否全相等,如果是则返回true,否则返回false。
• 深层比较:deepEquals()对数组作深层比较,简单地说,可以对二维乃至三维以上的数组进行比较,判断是否相等。
• 深层输出:将数组值作深层输出,简单地说,可以对二维乃至三维以上的数组输出其字符串值。
实现步骤
(1)新建一个类名为TextArray.java。
(2)代码如下所示:
package com.zf.s4; //创建一个包 import java.util.Arrays; //引入类 import java.util.Scanner; public class TextArraysMethod { //操作Arrays方法的类 public static void arraysSortAndSearch() { //使用Arrays对数组排序与搜索 int[] arr = { 93, 5, 3, 55, 57, 7, 2, 73, 41, 91 }; //声明数组并初始化 System.out.print("排序前: "); for (int i = 0; i < arr.length; i++) //循环显示数组元素 System.out.print(arr[i] + " "); System.out.println(); Arrays.sort(arr); //进行排序 System.out.print("排序后: "); for (int i = 0; i < arr.length; i++) //循环显示数组元素 System.out.print(arr[i] + " "); System.out.println("\n请输入搜索值:"); Scanner scan = new Scanner(System.in); //接受键盘输出的值 int number = scan.nextInt(); //接受一个整数 int find = -1; if((find = Arrays.binarySearch(arr, number))> -1){ //判断是否找到搜索数字 System.out.println("找到值的索引在第" + find + "位置"); } else System.out.println("找不到指定值"); } public static void arraysCompareAndFill() { //使用Arrays对数组填充与比较 int[] arr1 = new int[10]; //声明数组 int[] arr2 = new int[10]; int[] arr3 = new int[10]; Arrays.fill(arr1, 5); //将数组arr1的元素填充为5 Arrays.fill(arr2, 5); //将数组arr2的元素填充为5 Arrays.fill(arr3, 10); //将数组arr3的元素填充为10 System.out.print("数组arr1中的元素为:"); for (int i = 0; i < arr1.length; i++) System.out.print(arr1[i] + " "); System.out.println("\narr1 = arr2 ? " + //判断数组arr1与arr2是否相等 Arrays.equals(arr1, arr2)); System.out.println("arr1 = arr3 ? " + //判断数组arr1与arr3是否相等 Arrays.equals(arr1, arr3)); } public static void deepCompareAndDeepToString() { //使用深层比较和深层输出 int[][] arr1 = {{1, 2, 3},{4, 5, 6},{7, 8, 9}}; //声明三维数组 int[][] arr2 = {{1, 2, 3},{4, 5, 6},{7, 8, 9}}; int[][] arr3 = {{0, 1, 3},{4, 6, 4},{7, 8, 9}}; System.out.println("数组arr1 内容等于 数组arr2 ? " + Arrays.deepEquals(arr1, arr2)); //深层比较数组arr1和数组arr2 System.out.println("数组arr1 内容等于 数组arr3 ? " + Arrays.deepEquals(arr1, arr3)); //深层比较数组arr1和数组arr3 System.out.println("数组arr1 深层输出(deepToString())\n\t" + Arrays.deepToString(arr1)); //深层输出数组arr1 } public static void main(String[] args) { //Java程序的主入口处 System.out.println("1.使用Arrays类的填充和比较方法:"); arraysCompareAndFill(); //调用填充与比较的方法 System.out.println("2.使用Arrays类的深层比较和深层输出方法:"); deepCompareAndDeepToString(); System.out.println("3.使用Arrays类的排序与搜索方法:"); arraysSortAndSearch(); //调用排序与搜索的方法 } }
(3)运行结果如下所示:
1.使用Arrays类的填充和比较方法: 数组arr1中的元素为:5 5 5 5 5 5 5 5 5 5 arr1 = arr2 ? true arr1 = arr3 ? false 2.使用Arrays类的深层比较和深层输出方法: 数组arr1 内容等于 数组arr2 ? true 数组arr1 内容等于 数组arr3 ? false 数组arr1 深层输出(deepToString()) [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 3.使用Arrays类的排序与搜索方法: 排序前: 93 5 3 55 57 7 2 73 41 91 排序后: 2 3 5 7 41 55 57 73 91 93 请输入搜索值: 55 找到值的索引在第5位置
源程序解读
(1)arraysSortAndSearch()方法对数组进行排序和搜索。sort()方法可以对整个数组进行排序,也可以根据数组起始位置与结束位置之间的元素进行排序。语法:Arrays.sort(数组,起始位置,结束位置)。应该注意的是:结束位置的元素不会进行排序。binarySearch()方法对数组进行搜索,使用的是二分法。即如果数组中间元素的值比查找的元素大,则只从数组的前半部分元素中查找,否则只从数组的后半部分元素中查找,如此递归下去。因此,在查找元素前必须对数组元素进行升序排序。返回的是查找元素的下标值。如果在数组中找不到查找元素,则返回负数。
(2)arraysCompareAndFill()方法对数组进行填充和比较。fill()方法可以对整个数组赋以初值,也可以对指定位置进行赋值。数组arr1与数组arr2元素值相同,但实际上arr1与arr2是引用自不同的两个组对象。equals()方法用来比较元素值是否相同。不可以用==来比较两个数组的元素值是否相等。
(3)deepCompareAndDeepToString()方法是对多维数组元素的值进行深层比较和输出。方法适用于任意深度的嵌套数组。如果两个数组引用均为null,或者它们引用了包含相同元素数量的数组,并且两个数组中的所有相应元素对都是深层相等的,则认为这两个数组引用是深层相等的。数组arr1与数组arr2中的相应元素对都是深层相等的,所以进行深层比较之后,返回true。如果满足以下任意条件之一,则两个null元素el和e2可以是深层相等:
e1和e2都是对象引用类型的数组,并且Arrays.deepEquals(e1,e2)将返回true。
e1和e2都是相同基本类型的数组,并且Arrays.equals(e1,e2)的适当重载将返回true。
e1.equals(e2)将返回true。
如果指定数组中的任意一个数组,直接或间接通过一个或多个数组级别,包含数组本身作为其元素,则此方法的行为是不确定的。