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;   } }

 

 

感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                 

 

 

posted @ 2018-04-27 16:01  SpringL  阅读(168)  评论(0编辑  收藏  举报