一、Java 数组
1、数组定义:数组是有序数据的集合,数组中的每个元素具有相同的数组名和下标来做唯一标识。
2、数组的分类:一维、二维、三维。
3、数组声明及内存分配:
为数组分配内存空间:如果不分配内存,将不能访问它的任何元素。我们使用new关键字来为数组分配内存空间。
int arrayDemo[]; //数组的声明形式1 int[] score; //数组的声明形式2 score=new int[3]; //为数组开辟内存空间,实例化 for(int i=0;i<score.length;i++){ score[i]=i*2+1; } for(int i=0;i<score.length;i++){ System.out.println(score[i]); } System.out.println(score.length);
4、数组静态初始化:
(1)数组初始化分为两种方式:
a.动态初始化
b.静态初始化
(2)之前我们所创建的数组,所采用的方式都是动态初始化,也就是所有的内容不会具体指定,都是默认值。
(3)静态初始化是指:在数组创建之初直接指定其内容。
//静态初始化 int score[]={2,5,6,4,6,7}; //声明 for(int i=0;i<score.length;i++){ System.out.println(score[i]); }
5、数组的使用
(1)求最大值、最小值:
int score[]={43,34,5,66,12}; int max,min; max=min=score[0]; for (int i = 0; i < score.length; i++) { if(score[i]>max){ max=score[i]; } if(score[i]<min){ min=score[i]; } } System.out.println("最大值:"+max); System.out.println("最小值:"+min);
(2)冒泡排序
int score[]={12,45,23,10,300}; for (int i = 0; i < score.length-1; i++) { for (int j = i+1; j < score.length; j++) { if(score[i]<score[j]){ int temp =score[i]; score[i]=score[j]; score[j]=temp; } } System.out.print("第"+(i+1)+"排序:"); for (int j = 0; j < score.length; j++) { System.out.print(score[j]+","); } System.out.println(""); } for (int i = 0; i < score.length; i++) { System.out.println(score[i]); }
第1排序:300,12,23,10,45,
第2排序:300,45,12,10,23,
第3排序:300,45,23,10,12,
第4排序:300,45,23,12,10,
300
45
23
12
10
6、Java二维数组声明内存分配介绍及使用:
二维数组的声明和一维数组类似,内存分配也是使用new关键字。
声明:type arrayName[][];
初始化:arrayName[][] = new type[行][列];
二、排序算法
(一)冒泡排序
冒泡排序是一种简单的排序算法。冒泡排序将一个列表中的两个元素进行比较,并将最小的元素交换到顶部。从最底部的元素开始比较,两个元素中较小的会冒到顶部,而较大的会沉到底部,该过程将被重复执行,直到所有元素都被排序。
以如上图所示的冒泡排序为例,每次比较相邻的两个数值,值小的交换到前面,每轮结束后值最大的数交换到了最后。第一轮需要比较4次;第二轮需要比较3次;第三轮需要比较2次;第四轮只需要比较1次。
那么如何用二重循环将5个数排序呢?5个数存放在一维数组中,外层循环控制比较多少轮,循环变量为i;内层循环控制每轮比较多少次,循环变量为j,如下图所示。
下面是通过二重循环来实现的冒泡排序,实现代码如下:
public class BubbleSort { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int a[]={16,25,9,90,23}; System.out.println("排序之前的数组是:"); printArray(a); for (int i = 0; i < a.length-1; i++) { for (int j = 0; j < a.length-1-i; j++) { //判断两个数的大小 if (a[j]>a[j+1]) { int temp=a[j+1]; a[j+1]=a[j]; a[j]=temp; } } } System.out.println(" 排序后的数组是: "); printArray(a); } public static void printArray(int[] array){ for (int i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } }
结果截图为:
稳定性分析:
如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个元素相邻起来,最终也不会交换它俩的位置,所以相同元素经过排序后顺序并没有改变。所以冒泡排序是一种稳定排序算法。
(二)插入排序
假设有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候,就要用到一种新的排序方法——插入排序法。
插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据。
插入排序算法类似于玩扑克时抓牌的过程,玩家每拿到一张牌,都要插入到手中已有的牌里(这些手牌已经排好序),使之从小到大排好序,如下图所示(该图出自《算法导论》)。
对一个数组进行插入排序的编程也是基于这个道理,但与插入扑克牌有一点不同,不可能在两个相邻的存储单元之间再插入一个单元,因此要将插入点之后的数据依次往后移动一个单元。如对一个数组{15, 3, 56, 1, 78, 12, 7, 99, 123, 90, 63}运用插入排序进行操作,操作的步骤解析如图4.24所示。
从上图可了解插入算法的过程:比较a[0]和a[1]的值,然后再比较a[2]与a[1]的大小。小的在前,大的在后。
代码实现如下:
package com.sort.insert; public class InsertSort { /** * @param args */ public static void main(String[] args) { int[] a={15, 3, 56, 1, 78, 12, 7, 99, 123, 90, 63}; System.out.println("排序之前:"); for (int i = 0; i < a.length; i++) { System.out.print(a[i]+" "); } //从数组的第二个元素开始循环将数组中的元素插入 for (int i = 1; i < a.length; i++) { //设置数组中的第2个元素为第一次循环待插入元素 int temp = a[i]; int j; for (j = i-1; j>=0 && a[j]>temp; j--) { //如果要插入的元素小于第j个元素,就将第j个元素向后移动。将大于temp的往后移动一位 a[j+1] = a[j]; } //直到要插入的元素不小于第j个元素,将temp插入到数组中 a[j+1] = temp; } System.out.println(" 排序之后:"); for (int i = 0; i < a.length; i++) { System.out.print(a[i]+" "); } } public static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } private static void swap(int x[],int a,int b) { int t=x[a]; x[a]=x[b]; x[b]=t; } public static int[] insertSort(int[] a) { for (int i = 0; i < a.length; i++) { for (int j = 0; j>0 && a[j-1]>a[j]; j++) { swap(a, j, j-1); } } return a; } }
结果截图:
稳定性分析:
如果遇见一个与插入元素相等的,那么把待插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序仍是排好序后的顺序,所以插入排序是稳定的。
(三)快速排序(冒泡的改进)
快速排序是一种分而治之的算法。它的基本思想是:将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按这方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此实现整个数据变成有序序列。
假设要排序的数组是a[0] ... a[n],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。一趟快速排序的算法如下。
(1) 设置两个变量i、j,排序开始的时候i=1,j=n。
(2) 以第一个数组元素作为关键数据,赋值给x,即x=a[0]。
(3) 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于x的值,两者交换。
(4) 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于x的值,两者交换。
(5) 重复第3、4步,直到i=j。
上例中的数组a用快速排序进行操作的情况如下图所示:
代码实现如下:
package com.sort.quick; public class QuickSort { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("排序之前的数组:"); int [] a={15, 3, 56, 1, 78, 12, 7, 99, 123, 90, 63}; printArray(a); System.out.println(" 排序后的数组是:"); quickSort(a); printArray(a); } /*把整个序列看做一个数组,把第零个位置看做中轴,和最后一个比,如果比它小交换,比它大不做任何处理; 交换了以后再和小的那端比,比它小不交换,比他大交换。 这样循环往复,一趟排序完成,左边就是比中轴小的,右边就是比中轴大的,然后再用分治法,分别对这两个独立的数组进行排序。*/ //先取得中轴的位置 public static int getMiddle (int[] list,int low,int high) { int temp=list[low];//数组的第一个作为中轴 while (low<high) { while (low<high&&list[high]>temp) { high--; } list[low]=list[high]; //比中轴小的记录移到低端 while (low<high&&list[low]<temp) { low++; } list[high]=list[low]; //比中轴大的记录移到高端 } list[low]=temp; //中轴记录到尾 return low; //返回中轴的位置 } //以递归的形式进行分治排序算法 public static void recursionQuick(int[] list,int low,int high) { if (low<high) { int middle=getMiddle(list, low, high); //将list数组进行一分为二 recursionQuick(list, low, middle-1); //对低字表进行递归排序 recursionQuick(list, middle+1, high); //对高字表进行递归排序 } } //快速排序实现 public static void quickSort(int[] list){ if (list.length>0) { recursionQuick(list, 0, list.length-1); } } //打印出数组 public static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } }
结果截图:
稳定性分析:
快速排序有两个方向,左边的i下标一直往右走(当条件a[i] <= a[center_index]时),其中center_index是中枢元素的数组下标,一般取为数组第0个元素。而右边的j下标一直往左走(当a[j] > a[center_index]时)。如果i和j都走不动了,i <= j, 交换a[i]和a[j],重复上面的过程,直到i>j。交换a[j]和a[center_index],完成一趟快速排序。在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11 。现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱。所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j]交换的时刻。
(四)选择排序
其指导思想是先并不急于调换位置,先从a[0]开始逐个检查,看哪个数最小,就记下该数所在的位置p,等一躺扫描完毕,再把a[p]和a[1]对调,这时a[0]~a[9]最小的数据就换到了最前面的位置。算法的步骤如下:
(1) 先假设a[0]的数最小,记下此时的位置p。
(2) 依次把a[p]和a[i](i从2变化到9)进行比较,每次比较时,若a[i]的数比a[p]中的数小,则把i的值赋给p,使p总是指向当前所扫描过的最小数的位置,也就是说a[p]总是等于所有扫描过的数中最小的那个数。在依次一一比较后,p就指向10个数中最小的数所在位置,即a[p]就是10个数中最小的那个数。
(3) 把a[p]和a[0]的数对调,那么最小的数就在a[0]中了,也就是在最前面了。
如果现在重复此算法,但每重复一次,进行比较的数列范围就向后移动一个位置,即第二遍比较时范围就从第2个数一直到第n个数,在此范围内找最小的数的位置p,然后把a[p]与a[2]对调,这样从第2个数开始到第n个数中,最小数就在a[2]中了,第三遍就从第3个数到第n个数中去找最小的数,再把a[p]与a[3]对调……此过程重复n-1次后,就把a数组中n个数按从小到大的顺序排好了。这种排序的方法就是选择排序法。如果用图形来描述选择排序的过程,如下图所示:
代码实现如下:
package com.sort.select; public class SelectSort { public static void printArray(int[] c) { for (int i = 0; i < c.length; i++) { System.out.print(" "+c[i]); } } /*** @param args*/ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("选择排序之前的数组:"); int [] a={15, 3, 56, 1, 78, 12, 7, 99, 123, 90, 63}; printArray(a); System.out.println(" 选择排序后的数组是:"); selectSort(a); printArray(a); } public static void selectSort(int[] list) { if (list==null || list.length<=0) { return; } for (int i = 0; i < list.length; i++) { int min=i; //将当前下标定义为最小值的下标 for (int j = i+1; j < list.length; j++) { if (list[min]>list[j]) { //如果有小于当前最小值的关键字 min=j; //将此关键字的下标值赋值给min } } if (i!=min) { //若min不等于i,说明找到最小值,交换 int temp=list[min]; list[min]=list[i]; list[i]=temp; } } } }
结果截图:
稳定性分析:
举个例子:序列5 8 5 2 9, 我们知道第一趟选择第1个元素5会与2进行交换,那么原序列中两个5的相对先后顺序也就被破坏了。所以选择排序不是一个稳定的排序算法。
所有排序算法的比较:
三、增强for 循环
1.在Java SE 5.0之前,输出一列数据可以用如下方法:
int[] arr = {1, 2, 3, 4, 5};
for(int i=0; i<arr.length; i++)
System.out.println(arr[i]);
2.但在Java SE 5.0之后,提出了一个增强for循环的语句,语法为:
for(type 变量名 : 集合变量名) {...}
用这个增强for循迭代arr数组的代码如下:
for(int temp : arr)
System.out.println(temp);
其中,temp是一个迭代变量,它必须在()中定义,arr是集合变量,它可以是数组或实现了Iterable接口的集合类。