04_Java数据结构
4.1 算法
前面我们已经讲过,程序=数据结构+算法。
什么是算法?对一个现有的问题我们采取的解决过程及方法,即为算法。一个用算法实现的程序会耗费两种资源:处理时间和内存。
算法的效率分析标准:
时间复杂度
空间复杂度
简单性和清晰性
对于时间复杂度,可以通过System.currentTimeMillis()方法来测试。例如:
public class Test { public static void main(String args[]) { System.out.println(System.currentTimeMillis()); fun(); System.out.println(System.currentTimeMillis()); } public static void fun() { double a = 0;
for(int i = 0; i < 10000; i++) for(int j = 0; j < 10000; j++) for(int k = 0; k < 100; k++) a++; } }
前后两次获得当前系统时间的差值就是运行所消耗的时间(毫秒为单位)。
通过System.currentTimeMillis()方法来测试的缺点:
a.不同的平台执行的时间不同
b.有些算法随着输入数据的加大,测试时间会变得不切实际!
4.2 查找
4.2.1 查找之线性查找(直接查找)
算法思路:从数组的第一个元素开始查找,并将其与查找值比较,如果相等则停止,否则继续下一个元素查找,直到找到匹配值。
注意:被查找的数组中的元素可以是无序的、随机的。
实例:
import java.util.*; public class Demo1 { public static void main(String args[]) { int iArr[] = {32, 9, 78, 44, 29, 18, 97, 49, 56, 61}; System.out.println("数组的所有元素为:");
for(int i : iArr) System.out.print(i + " ");
System.out.println(); System.out.print("请输入你要查找的元素:"); Scanner scan = new Scanner(System.in); int iNum = scan.nextInt(); int index = straightSearch(iArr, iNum); if(index == -1) System.out.println("没有找到元素" + iNum); else System.out.println("找到元素" + iNum + ", 下标为:" + index); } public static int straightSearch(int[] arr, int num) { int i = 0; for(; i < arr.length; i++) { if(arr[i] == num) return i; } return -1; } }
4.2.2 查找之折半查找(二分查找)
算法思路:假设被查找数组中的元素是升序排列的,那我们查找值时,首先会直接到数组的中间位置(数组长度/2),并将中间值和查找值比较,如果相等则返回,否则,如果当前元素值小于查找值,则继续在数组的后面一半查找(重复上面过程);如果当前元素值大于查找值,则继续在数组的前面一半查找,直到找到目标值或者无法再二分数组时停止。
注意:二分查找只是针对有序排列的各种数组或集合。
实例:
//不利用递归实现 import java.util.*; public class Demo1 { public static void main(String args[]) { int iArr[] = {1, 2, 3, 4, 6, 8, 22, 44, 99, 111, 112, 116}; System.out.println("数组的所有元素为:");
for(int i : iArr) System.out.print(i + " ");
System.out.println(); System.out.print("请输入你要查找的元素:"); Scanner scan = new Scanner(System.in); int iNum = scan.nextInt(); int index = binarySearch(iArr, iNum, 0, iArr.length-1); if(index == -1) System.out.println("没有找到元素" + iNum); else System.out.println("找到元素" + iNum + ", 下标为:" + index); } public static int binarySearch(int[] arr, int num, int iMin, int iMax) { while(iMin <= iMax) { int iMid = (iMin + iMax) / 2; if(num == arr[iMid]) return iMid; else if(num < arr[iMid]) iMax = iMid-1; else iMin = iMid+1; } return -1; } }
//利用递归实现 import java.util.*; public class Demo1 { public static void main(String args[]) { int iArr[] = {1, 2, 3, 4, 6, 8, 22, 44, 99, 111, 112, 116}; System.out.println("数组的所有元素为:"); for(int i : iArr) System.out.print(i + " ");
System.out.println(); System.out.print("请输入你要查找的元素:"); Scanner scan = new Scanner(System.in); int iNum = scan.nextInt(); int index = binarySearch(iArr, iNum, 0, iArr.length-1); if(index == -1) System.out.println("没有找到元素" + iNum); else System.out.println("找到元素" + iNum + ", 下标为:" + index); } public static int binarySearch(int[] arr, int num, int iMin, int iMax) { int iMid = (iMin + iMax) / 2; if (num < arr[iMin] || num > arr[iMax]) return -1; else if (num == arr[iMid]) return iMid; else if (num < arr[iMid]) return binarySearch(arr, num, iMin, iMid - 1); else return binarySearch(arr, num, iMid + 1, iMax); } }
4.3 排序
4.3.1 排序之插入排序
4.3.1.1 插入排序之直接插入排序
算法思路:直接插入排序(straight insertion sort)是一种最简单的排序方法。它的基本操作是将一个记录插入到一个长度为m (假设)的有序表中,使之仍保持有序,从而得到一个新的长度为m + 1的有序表。设有一组关键字{ K1 , K2 ,…, Kn};排序开始就认为K1 是一个有序序列;让 K2 插入上述表长为 1 的有序序列,使之成为一个表长为 2 的有序序列;然后让 K3 插入上述表长为 2 的有序序列,使之成为一个表长为 3 的有序序列;依次类推,最后让 Kn插入上述表长为 n-1 的有序序列,得一个表长为 n 的有序序列。
实例:
//49, 38, 65, 97, 76, 13, 27初始情况 //[49], 38, 65, 97, 76, 13, 27从下标1开始 //[38, 49], 65, 97, 76, 13, 27 //[38, 49, 65], 97, 76, 13, 27 //[38, 49, 65, 97], 76, 13, 27 //[38, 49, 65, 76, 97], 13, 27 //[13, 38, 49, 65, 76, 97], 27 //[13, 27, 38, 49, 65, 76, 97] 代码实现: public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; straightInsertSort(a);
for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void straightInsertSort(int a[])
{ int i, m; for (i = 1; i < a.length; i++) //共进行n-1趟插入 { m = i; while (m >= 1 && a[m] < a[m-1])//短路表达式 { a[m] = (a[m]+a[m-1]) - (a[m-1] = a[m]);//比前一个小, 则与之交换 m--; } } } }
4.3.1.2 插入排序之折半插入排序
算法思路:折半插入排序 (Binary insertion sort)当直接插入排序进行到某一趟时,对于a[i]来讲,前边i-1个记录已经按有序。此时不用直接插入排序的方法,而改为先用折半查找法找出r[i]应插的位置,然后再插入。这种方法就是折半插入排序。
基本步骤:
a、初始化:设定有序区为第一个元素,设定无序区为后面所有元素
b、依次取无序区的每个元素
c、通过二分法查找有序区,返回比这个数小的最大数
d、保留此位置数据
e、从此位置的元素到有序区的最后一个元素,依次后移
f、用保留的数据填充此位置
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; binaryInsertSort(a); for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void binaryInsertSort(int a[])
{ for (int i = 1; i < a.length; i++) //共进行n-1趟插入 { int iTmp = a[i];//将待插入数据临时保存到iTmp中去. int m = findPosition(a, a[i], 0, i - 1); //m是a[i]应该呆的位置 for (int j = i; j > m; j--) a[j] = a[j-1];//整体向后移动一个位置 a[m] = iTmp;//m是a[i]应该呆的位置 } } public static int findPosition(int a[], int num, int iMin, int iMax) { if (num < a[iMin]) return iMin;//超出范围,直接返回 if (num > a[iMax]) return iMax + 1;//超出范围,直接返回 int iMid = (iMin + iMax) / 2;//选取中值,准备二分 if (a[iMid] >= num)//继续二分: 递归 return findPosition(a, num, iMin, iMid - 1); //目标在左边,递归左边(p[m]已经比较过,排出查找范围) else //if (a[m] < num) return findPosition(a, num, iMid + 1, iMax); //目标在右边,递归右边(p[m]已经比较过,排出查找范围) } }
4.3.1.3 插入排序之希尔排序
算法思路:希尔排序(Shell Sort): 是插入排序的一种。因D.L.Shell于1959年提出而得名。先取一个小于数组长度n的整数d1(一般为n/2)作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1(一般为d1/2), 重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; shellSort(a); for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void shellSort(int a[])
{ int k = a.length / 2;//k值代表前文中的增量d值 while (k >= 1)//当增量k值变化到0,结束循环 { for (int i = 0; i < k; i++) //将数组分成k组, 然后对每组进行直接插入排序. { for (int j = i+k; j < a.length; j += k)//共进行 ? 趟插入 { int m = j; while (m >= k && a[m] < a[m-k])//短路表达式 { a[m] = (a[m]+a[m-k]) - (a[m-k]=a[m]);//比前一个小, 则与之交换 m -= k; } } } k = k / 2; } } }
4.3.2 排序之选择排序
4.3.2.1 选择排序之直接选择排序
算法思路:简单选择排序(simple selection sort)也是直接选择排序。是一种较为容易理解的方法。对于一组关键字{K1,K2,…,Kn},首先从K1,K2,…,Kn中选择最小值,假如它是 Kz,则将Kz与 K1对换;然后从K2,K3,… ,Kn中选择最小值 Kz,再将Kz与K2对换。如此进行选择和调换n-2趟,第(n-1)趟,从Kn-1、Kn中选择最小值 Kz将Kz与Kn-1对换,最后剩下的就是该序列中的最大值,一个由小到大的有序序列就这样形成。
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; simpleSelectSort(a);
for (int i = 0; i < a.length; i++) System.out.print(a[i] + " ");
System.out.println(); } public static void simpleSelectSort(int a[])
{ for(int i = 1; i < a.length; i++) { int iMax = 0;
for(int j = 1; j <= a.length - i; j++) { if(a[j] > a[iMax]) iMax = j; } if(iMax != a.length-i) a[iMax] = (a[iMax]+a[a.length-i]) - (a[a.length-i]=a[iMax]); } } }
4.3.3 排序之交换排序
4.3.3.1 交换排序之冒泡排序
算法思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。
在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; bubbleSort(a);
for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void bubbleSort(int a[])
{ for(int i = 1; i < a.length; i++) { for(int j = 0; j < a.length - i; j++) { if(a[j] > a[j+1]) a[j] = (a[j]+a[j+1]) - (a[j+1]=a[j]); } } } }
4.3.3.2 交换排序之双向冒泡排序
算法思路:是冒泡排序的升级。双向冒泡是一个大泡从头往后冒,一个小泡从后往前冒。相对冒泡排序可减少时间。
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; doubleBubbleSort(a);
for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void doubleBubbleSort(int a[])
{ int n = a.length; for(int i = 1; i <= n/2; i++) { for(int j = i-1; j < n - i; j++) { if(a[j] > a[j+1]) a[j] = (a[j]+a[j+1]) - (a[j+1]=a[j]); if(a[n-1-j] < a[n-2-j]) a[n-1-j] = (a[n-1-j]+a[n-2-j]) - (a[n-2-j]=a[n-1-j]); } } } }
4.3.3.3 交换排序之快速排序
算法思路:快速排序(Quicksort)是对冒泡排序的一种改进。由C. A. R. Hoare在1962年提出。它的基本思想是:通过"一趟排序"将要排序的数据分割成独立的两部分,其中左部分的所有数据都比右部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
算法过程:设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];注意该key值在整个过程中永远不变,始终是和key进行比较。
3)从j开始由后向前搜索,找到第一个小于key的值A[j],并与A[i]交换;
4)从i开始由前向后搜索,找到第一个大于key的值A[i],并与A[j]交换;
5)重复第3、4步,直到i=j;
注意:
3)和4)步是在程序中没找到时候才j--和i++,直至找到为止。找到并交换的时候i和j指针位置不变。
值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快排演示:
key = 49
49i 38 65 97 76 13 27j先找j开始,发现27比key小,交换i、j的值
27i 38 65 97 76 13 49j再从i开始,发现27和38都比key小,即i++
27 38 49i 97 76 13 65j直到i对应的值比key大,则i、j交换
27 38 13i 97 76 49j 65以此类推
27 38 13 49i 76 97j 65
27 38 13 49ij 76 97 65
代码实现:
public class Test { public static void main(String [] args)
{ int a[] = {49, 38, 65, 97, 76, 13, 27}; quickSort(a, 0, a.length-1); for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void quickSort(int a[], int iMin, int iMax)
{ if (iMin >= iMax) return; else if (iMin + 1 == iMax)// 若只有两个元素,直接比较 { if (a[iMin] > a[iMax]) { a[iMin] = (a[iMin]+a[iMax]) - (a[iMax]=a[iMin]); } return; } int iPos = partition(a, iMin, iMax); quickSort(a, iMin, iPos - 1);//left quickSort(a, iPos + 1, iMax);//right } public static int partition(int a[], int i, int j)
{ int iKey= a[i];//选取key do { //由后向前找到第一个比nKey小的元素, 并与a[i]交换 while (a[j] >= iKey && i < j) j--; if (i < j) a[i] = (a[i]+a[j]) - (a[j]=a[i]); //由前向后找到第一个比nKey大的元素, 并与a[j]交换 while (a[i] <= iKey && i < j) i++; if (i < j) a[i] = (a[i]+a[j]) - (a[j]=a[i]); } while (i < j); return j; } }
感谢阅读。如果感觉此章对您有帮助,却又不想白瞟