数据结构学习笔记(2)_一维数组及其简单运用
不知不觉大学过了三年,最遗憾的就是在大二没把《数据结构》和《算法》这两门课程学好~~~
现在每天花一点时间把这两个内容回顾一下,算是给自己的一点目标吧!
<1>数组(Array):
数组算是最简单的也是最基本的结构之一。在Java中,我们声明一个数组有两种形式:
int[] array = { 1, 2, 3 };
或
int array[] = { 1, 2, 3 };
这两种形式都是可以的,但推荐第一种,为什么呢?
首先,int[]和int是两种完全不同的类型!int是原生数据类型(Primitive Data Type)而int[]是引用数据类型(Reference Type)。比如像上面的array,它是有属性(Field)有方法(Method)的。第一种直接在语法上看,array就是一种新的类型,第二种应该是为了和C语言拉近关系而引入的。
数组的元素类型必须是一致并且长度是固定的。为什么呢?
这应该是与数组在设计时的目标有关。数组有一个特点就是可以随机访问(Random Access)。而所谓的随机访问是指可以快速的访问数组的任一元素,怎么做到的?数组在内存中是连续存储的,所以我们只要知道数组的首地址和每个元素占用多大的内存空间我们就可以算出任意元素的具体位置。因为这两点,数组必须是元素类型一致,并且长度固定的。
数组中的元素可以是原生数据类型,也可以是引用数据类型。这两种的存储形式是完全不同的!一种存储的是数据,一种存的是对象的地址。
假如有这样两个数组:
int[] array1 = { 1, 2, 3, 4 };和People[] array2 = { p1, p2, p3 };
那么这两者的内存表示应该是这样的:
这是一个比较重要也比较容易让人迷惑的地方。数组元素可以是引用!多维数组就是利用这一点实现的。也就是说,在Java中,实现上并没有直接意义上的多维数组!我们这样理解:所谓的二维数组就是元素都是一维数组的一维数组。有点绕口,但确实是这样的。
<2>数组的使用:
(1)冒泡排序
这个是比较简单的排序,其基本思路是这样的:
将相邻两个数据进行比较,如果左边的值大于右边的值,则将两个值相互交换(所谓的大东西沉底~~~)。如果左边的值小于右边的值,则不变。然后右边的值继续比较。重复前面的过程。这样完成一次之后就可以把最大的值放到最右边。重复执行直到排序完成。
来看具体代码:
public int[] sort(int[] array) { // 先复制出一份 int[] result = array.clone(); for (int i = 0; i < result.length; i++) { for (int j = 0; j < result.length - 1; j++) { // 如果左边的数大于右边的数要进行两数交换 if (result[j] > result[j + 1]) { int temp = result[j]; result[j] = result[j + 1]; result[j + 1] = temp; } } } return result; }
这段代码运行是没有问题的,但却不是最好的。为什么呢?
首先,当我们完成第一次遍历的时候,最大的元素已经确定。第二次的时候我们还有必要让最后两个元素进行比较吗?同理,进行完第二次的时候,第二大的数也已经确定,没必要在第三次的时候让倒数第三个数和倒数第四个数进行比较。所以改进后是这样的。
public int[] sort(int[] array) { // 先复制出一份 int[] result = array.clone(); for (int i = 0; i < result.length; i++) { // 减去i也就是最后确定了几个数了。 for (int j = 0; j < result.length - i - 1; j++) { // 如果左边的数大于右边的数要进行两数交换 if (result[j] > result[j + 1]) { int temp = result[j]; result[j] = result[j + 1]; result[j + 1] = temp; } } } return result; }
就改动一个地方,但整个性能提高了一倍!
完整的代码:
public class BubbleSort { public static void main(String[] args) { // 假定要进行排序的数组 int[] array = { 8, 7, 14, 8, 11, 12, 15, 1, 6 }; // 先打印出原数组 for (int i : array) { System.out.print(i + " "); } System.out.println("\n-----------------------------"); int[] result = sort(array); // 打印排序后的结果 for (int i : result) { System.out.print(i + " "); } } public static int[] sort(int[] array) { // 先复制出一份 int[] result = array.clone(); for (int i = 0; i < result.length; i++) { // 减去i也就是最后确定了几个数了。 for (int j = 0; j < result.length - i - 1; j++) { // 如果左边的数大于右边的数要进行两数交换 if (result[j] > result[j + 1]) { int temp = result[j]; result[j] = result[j + 1]; result[j + 1] = temp; } } } return result; } }
(2)选择排序:
算法的思路:
用一个数(int max)来记录数组下标。先假定第一个数是最大的(max = 0),然后与后面的数进行比较,如果array[max]比后面的数小,则修改max的值为比它大的数组元素的下标。这样一次下来就可以找出最大的数。然后放在数组后面就行了。
具体的代码:
public class SelectionSort { public static void main(String[] args) { // 假定要进行排序的数组 int[] array = { 8, 7, 14, 8, 11, 12, 15, 1, 6 }; // 先打印出原数组 for (int i : array) { System.out.print(i + " "); } System.out.println("\n-----------------------------"); int[] result = sort(array); // 打印排序后的结果 for (int i : result) { System.out.print(i + " "); } } public static int[] sort(int[] array) { // 先复制出一份 int[] result = array.clone(); int max; for (int i = 0; i < result.length; i++) { // 每一次都默认第一个是最大的 max = 0; // 遍历,找出最大值的下标 for (int j = 0; j < result.length - i; j++) { if (result[max] < result[j]) { max = j; } } // 进行值交换 int temp = result[result.length - i - 1]; result[result.length - i - 1] = result[max]; result[max] = temp; } return result; } }
冒泡和选择的区别在于冒泡是找到大的就交换,而选择是先找出最大值的下标,然后再交换。显然,选择排序的效率会高一些。运行结果都是相同的。