Java 中的数据结构和算法,第 2 部分:一维数组

开始使用一维数组和数组变量,然后尝试在 Java 程序中搜索和排序数组的五种算法

一个阵列是一个基本的数据结构的类别,和用于更复杂的数据结构的构造块。在数据结构和算法系列的第二篇教程中,您将学习如何在 Java 编程中理解和使用数组。我将从数组的概念以及数组在 Java 语言中的表示方式开始。然后,我将向您介绍一维数组以及在 Java 程序中使用它们的三种方法。最后,我们将探讨用于搜索和排序一维数组的五种算法:线性搜索、二分搜索、冒泡排序、选择排序和插入排序。

请注意,本教程基于数据结构和算法,第 1 部分,其中介绍了数据结构的理论方面以及与之相关的算法。该教程包括对算法的深入讨论,以及如何使用空间和时间复杂度因素来评估和选择最有效的 Java 程序算法。我们将在本教程中获得更多实践,因为我假设您已经阅读了第 1 部分。

什么是数组?

一个阵列是其中每个元素与至少一个索引相关联元素的序列。一个元素是一组存储的单个数据项的存储位置。一个指数是一个非负整数,而在这种情况下,用于唯一地标识的元素。这种关系类似于箱号如何唯一标识给定街道上的房屋。

与任何元素关联的索引数是数组的维度。在本文中,我们将讨论一维数组。本系列的下一篇文章将介绍多维数组。

Java 支持数组。每个元素占用相同的字节数,确切的数字取决于元素的数据项的类型。此外,所有元素共享相同的类型。

Java 数组不可调整大小

Java 数组具有固定大小。创建数组后,您无法更改数组的大小。相反,如果您需要更改数组的大小,您将创建另一个所需大小的数组,并将所有所需元素从原始数组复制到新数组。

一维数组

最简单的一种数组只有一维。阿一维阵列的每个元素具有一个索引相关联。一维数组用于存储数据项列表。在 Java 中创建一维数组的技术有以下三种:

  • 仅使用初始化程序
  • 仅使用关键字 new
  • 将关键字new与初始化程序一起使用

仅使用初始值设定项创建一维数组

以下是仅使用初始化程序创建一维数组的语法:

'{' [expr (',' expr)*] '}'

此语法声明一维数组是一个可选的、以逗号分隔的表达式列表,它们出现在左括号和右括号字符之间。此外,所有表达式都必须评估为兼容的类型。例如,在doubles的二元素一维数组中,两个元素可能都是 类型double,或者一个元素可能是 adouble而另一个元素是 afloat或整数类型(例如int)。

例子:

使用关键字 new 创建一维数组

关键字new为数组分配内存并返回其引用。这是这种方法的语法:

'new' type '[' int_expr ']'

此语法指出,一维数组是int_expr共享相同的(正)元素的区域type。此外,所有元素都归零,并被解释为0、0L、0.0F、0.0、false、null、'\u0000'

例子:

new char[4]

使用“new”关键字和初始值设定项创建一维数组

下面是使用new带有初始值设定项的关键字创建一维数组的语法。如您所见,它融合了前两种方法的语法:

'new' type '[' ']' '{' [expr (',' expr)*] '}'

在这种情况下,因为可以从逗号分隔的表达式列表中确定元素的数量,所以没有必要(或允许)int_expr在方括号之间提供。

例子:

new char[] {  'J', 'a', 'v', 'a' }

需要注意的是,仅使用初始值设定项创建数组的语法与使用初始值设定项和关键字的语法没有区别。仅初始化程序语法是语法糖的一个示例,这意味着使语言更甜美或更易于使用的语法。

数组变量

就其本身而言,新创建的一维数组是无用的。它的引用必须直接或通过方法调用分配给兼容类型的数组变量。以下两行语法显示了如何声明此变量:

type var_name '[' ']'
type '[' ']' var_name

每个语法声明一个数组变量,用于存储对一维数组的引用。虽然你可以使用语法,将方括号后type的首选。

例子:

char[] name1 = {  'J', 'a', 'v', 'a' };
char[] name2 = new char[4];
char[] name3 = new char[] {  'J', 'a', 'v', 'a' };
output(new char[] {  2, 3 }); // output({ 2, 3 }); results in a compiler error
static void output(char[] name)
{ 
   // ...
}

在示例中name1,name2、name3、 和name是数组变量。一对方括号表示每个方括号都存储对一维数组的引用。

关键字char表示每个元素必须存储一个char类型的值。但是,char如果 Java 可以将其转换为char. 例如,char[] chars = { 'A', 10 };是合法的,因为它10是一个足够小的正数int(意味着它适合char065535的范围)要转换为char。相比之下,char[] chars = { 'A', 80000 };就违法了。

数组变量与一个.length属性相关联,该属性将关联的一维数组的长度作为正数返回int;例如,name1.length返回 4

给定一个数组变量,您可以通过指定符合以下语法的表达式来访问一维数组中的任何元素:

array_var '[' index ']'

这里,index是一个正数int,范围从 0(Java 数组从零开始)到比从.length属性返回的值小 1 。

例子:

char ch = names[0]; // Get value.
names[1] = 'A';     // Set value.

如果指定负索引或大于或等于数组变量.length属性返回值的索引,Java 将创建并抛出一个java.lang.ArrayIndexOutOfBoundsException对象。

搜索和排序算法

搜索特定数据项的一维数组是一项非常常见的任务,并且有多种算法可以执行此操作。最流行的搜索算法之一称为线性搜索。另一种选择是二分搜索,它通常性能更高,但要求也更高:为了使用二分搜索,必须首先对数组的数据项进行排序或排序。虽然性能不高,但冒泡排序、选择排序和插入排序都是对一维数组进行排序的简单算法。对于较短的数组,每个都足够好。

下一节将介绍这些用于搜索和排序一维数组的算法。

空间复杂度

本节中讨论的每个算法——线性搜索、二分搜索、冒泡排序、选择排序和插入排序——都展示了 O(1)(常数)空间复杂度的变量存储。本系列的第 1 部分将算法的空间复杂度定义为算法完成其任务所需的额外内存量。

线性搜索算法

线性搜索在n 个数据项的一维数组中搜索特定项。它的功能是比较从最低索引到最高索引的数据项,直到找到指定的数据项,或者直到没有更多的数据项可供比较。

以下伪代码表示用于一维整数数组的线性搜索:


DECLARE INTEGER i, srch = ...
DECLARE INTEGER x[] = [ ... ]
FOR i = 0 TO LENGTH(x) - 1
   IF x[i] EQ srch THEN
      PRINT "Found ", srch
      END
   END IF
NEXT i
PRINT "Not found", srch
END

考虑一个由五个整数 [ 1, 4, 3, 2, 6 ] 组成的一维无序数组,其中整数 1 位于索引 0,整数 6 位于索引 4。伪代码执行以下任务以在其中找到整数 3这个数组:

  1. 将索引 0 (1) 处的整数与 3 进行比较。
  2. 由于没有匹配项,请将索引 1 (4) 处的整数与 3 进行比较。
  3. 由于仍然没有匹配项,请将索引 2 (3) 处的整数与 3 进行比较。
  4. 因为有匹配项,所以打印Found 3并退出。

线性搜索的时间复杂度为 O( n ),发音为“Big Oh of n”。对于n 个数据项,该算法最多需要n 次比较。平均而言,它执行n / 2 次比较。线性搜索提供线性性能。

效率

线性搜索算法的一个缺点是效率低下。对于包含 4,000,000 个数据项的数组,它将执行平均 2,000,000 次比较以找到指定的项。

探索线性搜索

为了让您试验线性搜索,我在清单 1 中创建了Java 应用程序。

清单 1. 使用线性搜索算法的 Java 示例


{ 
   public static void main(String[] args)
   { 
      // Validate command line arguments count.

      if (args.length != 2)
      { 
         System.err.println("usage: java LinearSearch integers integer");
         return;
      }

      // Read integers from first command-line argument. Return if integers 
      // could not be read.

      int[] ints = readIntegers(args[0]);
      if (ints == null)
         return;

      // Read search integer; NumberFormatException is thrown if the integer
      // isn't valid.

      int srchint = Integer.parseInt(args[1]);

      // Perform the search and output the result.

      System.out.println(srchint + (search(ints, srchint) ? " found"
                                                          : " not found"));
   }

   private static int[] readIntegers(String s)
   { 
      String[] tokens = s.split(",");
      int[] integers = new int[tokens.length];
      for (int i = 0; i < tokens.length; i++)
         integers[i] = Integer.parseInt(tokens[i]);
      return integers;
   }

   private static boolean search(int[] x, int srchint)
   { 
      for (int i = 0; i < x.length; i++)
         if (srchint == x[i])
            return true;

      return false;
   }
}

该LinearSearch应用程序读取一个逗号分隔从其第一命令行参数的整数列表。它在数组中搜索由第二个命令行参数标识的整数,并输出找到/未找到的消息。

注意数字格式异常

仅在每个命令行参数中指定数字和 +/- 符号字符。否则,此应用程序(以及随后的搜索和排序应用程序)将创建并抛出一个java.lang.NumberFormatException对象。

  • 为了试验这个应用程序,首先编译清单 1:
javac LinearSearch.java
  • 接下来,运行生成的应用程序,如下所示:
java LinearSearch "4,5,8" 5
  • 您应该观察到以下输出:
5 found
  • 再次运行生成的应用程序,如下所示:
java LinearSearch "4,5,8" 15
  • 您应该观察到以下输出:
15 not found

二分搜索算法

二分搜索算法在n 个数据项的有序一维数组中搜索特定数据项。该算法由以下步骤组成:

  1. 分别将低索引变量和高索引变量设置为数组的第一个和最后一个数据项的索引。
  2. 如果低索引大于高索引,则终止。搜索到的数据项不在数组中。
  3. 通过将低指数和高指数相加并将总和除以 2 来计算中间指数。
  4. 将搜索到的数据项与中间索引的数据项进行比较。如果它们相同则终止。已找到搜索到的数据项。
  5. 如果搜索到的数据项大于中间索引的数据项,则将低索引设置为中间索引加一并执行到步骤2。二分搜索在数组的上半部分重复搜索。
  6. 搜索到的数据项必须小于中间索引的数据项,因此将高索引设置为中间索引减一并将执行转移到步骤2。二分搜索在数组的下半部分重复搜索。

以下是表示一维整数数组的二分搜索算法的伪代码:


DECLARE INTEGER x[] = [ ... ]
DECLARE INTEGER loIndex = 0
DECLARE INTEGER hiIndex = LENGTH(x) - 1
DECLARE INTEGER midIndex, srch = ...
WHILE loIndex LE hiIndex
   midIndex = (loIndex + hiIndex) / 2
   IF srch GT x[midIndex] THEN
      loIndex = midIndex + 1
   ELSE
   IF srch LT x[midIndex] THEN
      hiIndex = midIndex - 1
   ELSE
      EXIT WHILE
   END IF
END WHILE
IF loIndex GT hiIndex THEN
   PRINT srch, " not found"
ELSE
   PRINT srch, " found"
END IF
END

二分查找并不难理解。例如,考虑一个由六个整数 [ 3, 4, 5, 6, 7, 8 ] 组成的一维有序数组,其中整数 3 位于索引 0,整数 8 位于索引 5。伪代码执行以下操作在这个数组中找到整数 6:

  1. 获取低指数 (0) 和高指数 (5)。
  2. 计算中间指数:(0+5)/2 = 2。
  3. 由于索引 2 (5) 处的整数小于 6,因此将低索引设置为 2+1 = 3。
  4. 计算中间指数:(3+5)/2 = 4。
  5. 由于索引 4 (7) 处的整数大于 6,因此将高索引设置为 4-1 = 3。
  6. 计算中间指数:(3+3)/2 = 3。
  7. 因为索引 3 (6) 处的整数等于 6,所以打印3 found并退出。

二分搜索的时间复杂度为 O(log 2 n ),发音为“log n到基数 2 的Big Oh ”。对于n 个数据项,二分搜索最多需要 1+log 2 n 次比较,这使得该算法在大多数情况下比线性搜索高效得多。该算法提供对数性能(有关对数性能的更多信息,请参见本系列第 1 部分中的图 3 )。

当线性搜索优于二分搜索时

尽管二分搜索通常比线性搜索更有效,但二分搜索对于短数组的效率不高。

清单 2 是一个BinarySearchJava 应用程序,可让您试验 Binary Search。

清单 2. 一个带有二分搜索算法的 Java 示例 ( BinarySearch.java)


{ 
   public static void main(String[] args)
   { 
      // Validate command line arguments count.

      if (args.length != 2)
      { 
         System.err.println("usage: java BinarySearch integers integer");
         return;
      }

      // Read integers from first command-line argument. Return if integers 
      // could not be read.

      int[] ints = readIntegers(args[0]);
      if (ints == null)
         return;

      // Read search integer; NumberFormatException is thrown if the integer
      // isn't valid.

      int srchint = Integer.parseInt(args[1]);

      // Perform the search and output the result.

      System.out.println(srchint + (search(ints, srchint) ? " found"
                                                          : " not found"));
   }

   private static int[] readIntegers(String s)
   { 
      String[] tokens = s.split(",");
      int[] integers = new int[tokens.length];
      for (int i = 0; i < tokens.length; i++)
         integers[i] = Integer.parseInt(tokens[i]);
      return integers;
   }

   private static boolean search(int[] x, int srchint)
   { 
     int hiIndex = x.length - 1, loIndex = 0, midIndex;

      while (loIndex <= hiIndex)
      { 
         midIndex = (loIndex + hiIndex) / 2;
         if (srchint > x[midIndex])
            loIndex = midIndex + 1;
         else
         if (srchint < x[midIndex])
            hiIndex = midIndex - 1;
         else
            return true;
      }

      return false;
   }
}

该BinarySearch应用程序读取一个逗号分隔从其第一命令行参数的整数列表。它在数组中搜索由第二个命令行参数标识的整数,并输出找到/未找到的消息。

二分搜索中的错误

在二分搜索算法中发现了一个错误,这会导致ArrayIndexOutOfBoundsException在 Java 中抛出类的实例。此错误在长度为 2 30(大约 10 亿)或更大的数组中表现出来。

考虑midIndex = (loIndex + hiIndex) / 2;清单2.在表达式语句如果总和loIndex和hiIndex超过最大正值为32位int为基础的整数(这恰好是),则和值溢出到一个负值,这停留负时,它除以2。232-1

要修复BinarySearch应用程序中的此错误,您将替换midIndex = (loIndex + hiIndex) / 2;为midIndex = loIndex + ((hiIndex – loIndex) / 2);. 这段代码可能更快,而且可以说同样清晰midIndex = (loIndex + hiIndex) >>> 1;。

有趣的是,尽管第一个二分搜索算法是在 1946 年发布的,但第一个对 n 的所有值都有效的二分搜索算法直到 1962 年才出现。

要探索二分搜索算法,请执行以下操作:

  • 编译清单 2 中的代码如下:
javac BinarySearch.java
  • 运行生成的应用程序,如下所示:
java BinarySearch "4,5,8" 5
  • 您应该观察到以下输出:
5 found
  • 再次运行生成的应用程序,如下所示:
java BinarySearch "4,5,8" 15
  • 您应该观察到以下输出:
15 not found

冒泡排序算法

冒泡排序算法将n 个数据项的一维数组按升序或降序排序。外循环使n -1 通过数组。每次传递都使用一个内部循环来交换数据项,以便下一个最小(升序)或最大(降序)数据项朝数组的开头“冒泡”。

“冒泡”动作发生在内循环中,其中每次迭代都将通过编号的数据项与每个连续的数据项进行比较。如果后继数据项小于(升序排序)或大于(降序排序)通过编号的数据项,则后继数据项与通过编号的数据项交换。

这是在一维整数数组/升序排序上下文中表示冒泡排序的伪代码:


DECLARE INTEGER i, pass
DECLARE INTEGER x[] = [ ... ]
FOR pass = 0 TO LENGTH(x) - 2
   FOR i = LENGTH(x) - 1 DOWNTO pass + 1
      IF x[i] LT x[pass] THEN // switch to > for descending sort
         EXCHANGE x[i], x[pass]
      END IF
   NEXT i
NEXT pass
END

冒泡排序相当容易理解。例如,考虑一个由四个整数组成的一维无序数组:[ 18, 16, 90, -3 ],其中整数 18 位于索引 0 处,整数 -3 位于索引 3 处。当请求对该数组进行排序时升序,冒泡排序将执行如下:


Pass 0               Pass 1               Pass 2
======               ======               ======
18  16  90  -3       -3  16  90  18       -3  16  90  18
^           ^            ^       ^                ^   ^
|           |            |       |                |   |
-------------            ---------                -----
-3  16  90  18       -3  16  90  18       -3  16  18  90
^       ^                ^   ^ 
|       |                |   |
---------                -----
-3  16  90  18       -3  16  90  18
^   ^
|   |
-----
-3  16  90  18

在比较和交换方面,冒泡排序的时间复杂度为 O( n 2 ),发音为“ n平方的大哦”。冒泡排序提供二次性能,这对于长度较短的数组来说不是问题——尤其是当您认为冒泡排序易于编码时。(有关二次性能的更多信息,请参阅第 1 部分。)

BubbleSort清单中的Java 应用程序让您可以试验冒泡排序。

清单 3. 带有冒泡排序算法的 Java 示例 ( BubbleSort.java)


{ 
   public static void main(String[] args)
   { 
      // Validate command line arguments count.

      if (args.length != 1)
      { 
         System.err.println("usage: java BubbleSort integers");
         return;
      }

      // Read integers from first command-line argument. Return if integers 
      // could not be read.

      int[] ints = readIntegers(args[0]);
      if (ints == null)
         return;

      // Output integer array's length and number of inversions statistics to
      // standard output device.

      System.out.println("N = " + ints.length);
      int inversions = 0;
      for (int i = 0; i < ints.length - 1; i++)
         for (int j = i + 1; j < ints.length; j++)
            if (ints[i] > ints[j])
               inversions++;
      System.out.println("I = " + inversions);

      // Output unsorted integer values to standard output, sort the array, 
      // and output sorted values to standard output.

      dump(ints);
      sort(ints);
      dump(ints);
   }

   static void dump(int[] a)
   { 
      for (int i = 0; i < a.length; i++)
         System.out.print(a[i] + " ");
      System.out.print('\n');
   }

   static int[] readIntegers(String s)
   { 
      String[] tokens = s.split(",");
      int[] integers = new int[tokens.length];
      for (int i = 0; i < tokens.length; i++)
         integers[i] = Integer.parseInt(tokens[i]);
      return integers;
    }

   static void sort(int[] x)
   { 
      for (int pass = 0; pass < x.length - 1; pass++)
         for (int i = x.length - 1; i > pass; i--)
            if (x[i] < x[pass])
            { 
               int temp = x[i];
               x[i] = x[pass];
               x[pass] = temp;
            }
   }
}

BubbleSort从其命令行参数中读取以逗号分隔的整数列表。它输出数组长度,计算并输出倒数(未排序数组中较小项左侧较大项),输出未排序数组,对数组进行排序,并输出已排序数组。(我接下来将介绍的选择排序和插入排序的行为类似。)

  • 编译清单 3 如下:
javac BubbleSort.java
  • 运行生成的应用程序,如下所示:
java BubbleSort "18,16,90,-3"
  • 您应该观察到以下输出:

N = 4
I = 4
18 16 90 -3
-3 16 18 90

选择排序算法

选择排序算法将n 个数据项的一维数组按升序或降序排序。外循环使n -1 通过数组。每次传递使用一个内部循环来查找下一个最小(升序排序)或最大(降序排序)数据项,该数据项与传递编号的数据项进行交换。

选择排序假定传递编号索引处的数据项是剩余数据项中的最小(升序)或最大(降序)。它在数组的其余部分搜索比该数据项更小/更大的数据项,并在找到更小/更大的数据项时在搜索结束时执行交换。

以下伪代码表示在一维整数数组/升序排序上下文中的选择排序:


DECLARE INTEGER i, min, pass
DECLARE INTEGER x[] = [ ... ]
FOR pass = 0 TO LENGTH(x) - 2
   min = pass
   FOR i = pass + 1 TO LENGTH(x) - 1
      IF x[i] LT x[min] THEN
         min = i
      END IF
   NEXT i
   IF min NE pass THEN
      EXCHANGE x[min], x[pass]
   END IF
NEXT pass
END

选择排序相当容易理解。例如,考虑一个由四个整数组成的一维无序数组:[ 18, 16, 90, -3 ],其中整数 18 位于索引 0,整数 -3 位于索引 3。当请求将此数组排序为升序,选择排序伪代码执行如下排序:


Pass 0                        Pass 1                        Pass 2
======                        ======                        ======
18  16  90  -3                -3  16  90  18                -3  16  90  18
^                                 ^                                 ^
|                                 |                                 |
min = 0                           min = 1                           min = 2

18  16  90  -3                -3  16  90  18                -3  16  90  18
    ^                                 ^                                 ^
    |                                 |                                 |
    16 < 18, min = 1                  90 > 16, min = 1                  18 < 90, min = 3
                                                                    ^   ^
18  16  90  -3                -3  16  90  18                        |   |
        ^                                 ^                         -----
        |                                 |                 -3  16  18  90
        90 > 16, min = 1                  18 > 16, min = 1

18  16  90  -3                               
            ^   
            |
            -3 < 16 min = 3
^           ^
|           |
------------- 
-3  16  90  18

选择排序的时间复杂度为 O( n 2 ) 次比较和 O( n ) 次交换。该算法在比较方面提供二次性能,在交换方面提供线性性能,这使其比冒泡排序更有效。

清单 4 显示了SelectionSortJava 代码中的应用程序。

清单 4. 使用选择排序算法的 Java 示例 ( SelectionSort.java)


public final class SelectionSort
{ 
   public static void main(String[] args)
   { 
      // Validate command line arguments count.

      if (args.length != 1)
      { 
         System.err.println("usage: java SelectionSort integers");
         return;
      }

      // Read integers from first command-line argument. Return if integers 
      // could not be read.

      int[] ints = readIntegers(args[0]);
      if (ints == null)
         return;

      // Output integer array's length and number of inversions statistics to
      // standard output device.

      System.out.println("N = " + ints.length);
      int inversions = 0;
      for (int i = 0; i < ints.length - 1; i++)
         for (int j = i + 1; j < ints.length; j++)
            if (ints[i] > ints[j])
               inversions++;
      System.out.println("I = " + inversions);

      // Output unsorted integer values to standard output, sort the array, 
      // and output sorted values to standard output.

      dump(ints);
      sort(ints);
      dump(ints);
   }

   static void dump(int[] a)
   { 
      for (int i = 0; i < a.length; i++)
         System.out.print(a[i] + " ");
      System.out.print('\n');
   }

   static int[] readIntegers(String s)
   { 
      String[] tokens = s.split(",");
      int[] integers = new int[tokens.length];
      for (int i = 0; i < tokens.length; i++)
         integers[i] = Integer.parseInt(tokens[i]);
      return integers;
    }

   static void sort(int[] x)
   { 
      for (int pass = 0; pass < x.length - 1; pass++)
      { 
         int min = pass;

         for (int i = pass + 1; i < x.length; i++)
            if (x[i] < x[min])
               min = i;

         if (min != pass)
         { 
            int temp = x[min];
            x[min] = x[pass];
            x[pass] = temp;
         }
      }
   }
}
  • 编译清单 4 如下:
javac SelectionSort.java
  • 运行生成的应用程序,如下所示:

java SelectionSort "18,16,90,-3"
  • 您应该观察到以下输出:

N = 4
I = 4
18 16 90 -3
-3 16 18 90

插入排序算法

插入排序算法将n 个数据项的一维数组按升序或降序排序。外循环使n -1 通过数组。每次传递都会选择下一个要插入到适当位置的数据项。它使用内部循环来找到这个位置,移动数据项以腾出空间。

插入排序首先将数据结构划分为已排序和未排序的部分。最初,排序部分包含索引 0 处的数据项;未排序部分包含所有其他数据项。在排序过程中,每个未排序部分数据项被插入到已排序部分中的适当位置,并且未排序部分缩小一个数据项。

这是一维整数数组中插入排序算法的伪代码,您在其中进行升序排序:


DECLARE INTEGER a, i, j
DECLARE INTEGER x[] = [ ... ]
FOR i = 1 TO LENGTH(x) - 1
   a = x[i]
   j = i
   WHILE j GT 0 AND x[j - 1] GT a
      x[j] = x[j - 1]
      j = j - 1
   END WHILE
   x[j] = a
NEXT i
END

与冒泡排序一样,插入排序也很容易理解。例如,考虑一个由四个整数组成的一维无序数组:[ 18, 16, 90, -3 ],其中整数 18 位于索引 0 处,整数 -3 位于索引 3 处。当指示将此数组排序为升序,算法执行如下排序:


i = 1                 i = 2                 i = 3
      =====                 =====                 =====
18 | 16   90   -3     16   18 | 90   -3     16   18   90 | -3     -3   16   18   90
     ^                          ^                          ^
     |                          |                          | 
     a,j                        a,j                        a,j

排序后的部分出现在左侧,最初由 [18] 组成。未排序部分出现在右侧,最初由 [ 16, 90, -3 ] 组成。

插入排序在最佳情况下(数据已经排序或接近排序)的时间复杂度为 O( n ) 比较,平均和最坏情况下的时间复杂度为 O ( n 2 )。该算法提供线性(最佳情况)或二次(平均/最坏情况)性能。

清单 5 显示了InsertionSort应用程序的源代码。

清单 5. 使用插入排序算法的 Java 示例 ( InsertionSort.java)


{ 
   public static void main(String[] args)
   { 
      // Validate command line arguments count.

      if (args.length != 1)
      { 
         System.err.println("usage: java InsertionSort integers");
         return;
      }

      // Read integers from first command-line argument. Return if integers 
      // could not be read.

      int[] ints = readIntegers(args[0]);
      if (ints == null)
         return;

      // Output integer array's length and number of inversions statistics to
      // standard output device.

      System.out.println("N = " + ints.length);
      int inversions = 0;
      for (int i = 0; i < ints.length - 1; i++)
         for (int j = i + 1; j < ints.length; j++)
            if (ints[i] > ints[j])
               inversions++;
      System.out.println("I = " + inversions);

      // Output unsorted integer values to standard output, sort the array, 
      // and output sorted values to standard output.

      dump(ints);
      sort(ints);
      dump(ints);
   }

   static void dump(int[] a)
   { 
      for (int i = 0; i < a.length; i++)
         System.out.print(a[i] + " ");
      System.out.print('\n');
   }

   static int[] readIntegers(String s)
   { 
      String[] tokens = s.split(",");
      int[] integers = new int[tokens.length];
      for (int i = 0; i < tokens.length; i++)
         integers[i] = Integer.parseInt(tokens[i]);
      return integers;
    }

   static void sort(int[] x)
   { 
      int j, a;

      // For all integer values except the leftmost value ...

      for (int i = 1; i < x.length; i++)
      { 
         // Get integer value a.

         a = x[i];

         // Get index of a. This is the initial insert position, which is
         // used if a is larger than all values in the sorted section.

         j = i;

         // While values exist to the left of a's insert position and the
         // value immediately to the left of that insert position is
         // numerically greater than a's value ...

         while (j > 0 && x[j - 1] > a)
         { 
            // Shift left value -- x[j - 1] -- one position to its right --
            // x[j].

            x[j] = x[j - 1];

            // Update insert position to shifted value's original position
            // (one position to the left).

            j--;
         }

         // Insert a at insert position (which is either the initial insert
         // position or the final insert position), where a is greater than
         // or equal to all values to its left.

         x[j] = a;
      }
   }
}
  • 编译清单 5 如下:
javac InsertionSort.java
  • 运行生成的应用程序,如下所示:
java InsertionSort "18,16,90,-3"
  • 您应该观察到以下输出:

N = 4
I = 4
18 16 90 -3
-3 16 18 90

本教程向您介绍了简单的数组和可用于处理它们的五种算法

    原文作者:ITnababy
    原文地址: https://blog.csdn.net/ITnababy/article/details/118464497
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞