02_陣列

courageous十二發表於2021-01-03

注:本部分為自學內容,學習過程中整理筆記方便以後查閱。課件內容來自尚學堂,教程地址:https://www.bilibili.com/video/BV1Kb411W75N?p=63

2.1 陣列概述(陣列有序排列)

陣列(Array):是多個相同型別資料按一定順序排列的集合,並使用一個名字命名,並通過編號的方式對這些資料進行統一管理。
常見概念:
 陣列名
 下標(或索引)
 元素
 陣列的長度
概述:
 陣列本身是引用資料型別,而陣列中的元素可以是任何資料型別,包括基本資料型別和引用資料型別。
 建立陣列物件會在記憶體中開闢一整塊連續的空間,而陣列名中引用的是這塊連續空間的首地址。
 陣列的長度一旦確定,就不能修改
 我們可以直接通過下標(或索引)的方式呼叫指定位置的元素,速度很快。
 陣列的分類:

  • 按照維度:一維陣列、二維陣列、三維陣列、…
  • 按照元素的資料型別分:基本資料型別元素的陣列、引用資料型別元素的陣列(即物件陣列)

2.2 一維陣列的使用

2.2.1 一維陣列的使用:宣告

 一維陣列的宣告方式:
type var[] 或 type[] var;

例如:
int a[];
int[] a1;
double b[];
String[] c; //引用型別變數陣列

注: Java語言中宣告陣列時不能指定其長度(陣列中元素的數), 例如: int a[5]; //非法

2.2.2 一維陣列的使用:初始化

  1. 動態初始化:陣列宣告且為陣列元素分配空間與賦值的操作分開進行
int[] arr = new int[3];
arr[0] = 3;
arr[1] = 9;
arr[2] = 8;

String names[];
names = new String[3];
names[0] = “錢學森”;
names[1] = “鄧稼先”;
names[2] = “袁隆平”;
  1. 靜態初始化:在定義陣列的同時就為陣列元素分配空間並賦值。
int arr[] = new int[]{ 3, 9, 8};int[] arr = {3,9,8};

String names[] = {
“李四光”,“茅以升”,“華羅庚”
}

總結:陣列一旦初始化完成,其長度就確定了(定義陣列時要麼指定長度,要麼賦值)。

2.2.3 一維陣列的使用:陣列元素的使用

  1. 定義並用運算子new為之分配空間後,才可以引用陣列中的每個元素;
  2. 陣列元素的引用方式:陣列名[陣列元素下標]
  • 陣列元素下標可以是整型常量或整型表示式。如a[3] , b[i] , c[6*i];
  • 陣列元素下標從0開始;長度為n的陣列合法下標取值範圍: 0 —>n-1;如int a[]=new int[3]; 可引用的陣列元素為a[0]、a[1]、a[2]
  1. 每個陣列都有一個屬性length指明它的長度,例如:a.length 指明陣列a的長
    度(元素個數)
  • 陣列一旦初始化,其長度是不可變的

2.2.4 一維陣列的使用:陣列元素的預設初始化值

  • 陣列是引用型別,它的元素相當於類的成員變數,因此陣列一經分配空間,其中的每個元素也被按照成員變數同樣的方式被隱式初始化。例如:
public class Test {
	public static void main(String argv[]){
				int a[]= new int[5];
				System.out.println(a[3]); //a[3]的預設值為0
	}
}
  • 對於基本資料型別而言,預設初始化值各有不同
  • 對於引用資料型別而言,預設初始化值為null(注意與0不同!)
    在這裡插入圖片描述

2.2.5 一維陣列的記憶體解析:

記憶體結構簡圖:
在這裡插入圖片描述
在這裡插入圖片描述
解析:arr1在棧空間中宣告;new的結構在堆空間中說明,長度為4,堆開闢連續的空間,比如首地址為0x12ab,將首地址值賦給 arr1,通過棧空間地址值可以指向堆空間實體結構,然後是賦值操作。

Java中使用關鍵字new來建立陣列

step1:定義陣列,分配棧空間
public class Test{
	public static void main(String args[]){
				int[] s;
				s = new int[10];
				for ( int i=0; i<10; i++ ) {
					s[i] =2*i+1;
					System.out.println(s[i]);
				}
	}
}

在這裡插入圖片描述

step2:初始化
public class Test{
	public static void main(String args[]){
		int[] s;
		s = new int[10];
		//int[] s=new int[10];
		//基本資料型別陣列在顯式賦值之前,
		//Java會自動給他們賦預設值。
		for ( int i=0; i<10; i++ ) {
			s[i] =2*i+1;
			System.out.println(s[i]);
		}
	}
}

在這裡插入圖片描述

step3:賦值
public class Test{
	public static void main(String args[]){
			int[] s;
			s = new int[10];
			for ( int i=0; i<10; i++ ) {
				s[i] =2*i+1;
				System.out.println(s[i]);
			}
	}
}

在這裡插入圖片描述

練習1:輸出聯絡方式
public class ArrayTest {
	public static void main(String[] args) {
		int[] arr = new int[] { 8, 2, 1, 0, 3 };
		int[] index = new int[] { 2, 0, 3, 2, 4, 0, 1, 3, 2, 3, 3 };
		String tel = "";
		for (int i = 0; i < index.length; i++) { // index.length=11
			tel += arr[index[i]];
		}
		System.out.println("聯絡方式:" + tel); // 18013820100
	}
}

練習2:
從鍵盤讀入學生成績,找出最高分,並輸出學生成績等級。
成績>=最高分-10 等級為’A’
成績>=最高分-20 等級為’B’
成績>=最高分-30 等級為’C’
其餘 等級為’D’
提示:先讀入學生人數,根據人數建立int陣列,存放學生成績。
package com.atguigu.contact;

import java.util.Scanner;

public class ArrayDemo1 {
	public static void main(String[] args) {
		// 1.使用Scanner讀取學生個數
		Scanner scanner = new Scanner(System.in);
		System.out.println("請輸入學生人數:");
		int number = scanner.nextInt();
		// 2.建立陣列,儲存學生成績,動態初始化
		int[] scores = new int[number];
		// 3.給陣列中的元素賦值
		System.out.println("請輸入" + number + "個學生成績");
		for (int i = 0; i < scores.length; i++) {
			scores[i] = scanner.nextInt();
		}
		// 4.獲取陣列中的元素的最大值
		int maxscore = 0;
		for (int i = 0; i < scores.length; i++) {
			if (scores[i] > maxscore) {
				maxscore = scores[i];
			}
		}
		// 5.根據每個學生與最高分的差值,得到每個學生的 等級,並輸出登記和成績
		char level;
		for (int i = 0; i < scores.length; i++) {
			if (maxscore - scores[i] <= 10) {
				level = 'A';
			} else if (maxscore - scores[i] <= 20) {
				level = 'B';
			} else if (maxscore - scores[i] <= 30) {
				level = 'C';
			} else {
				level = 'D';
			}
			System.out.println("student " + i + " score is " + scores[i] + ",gread is " + level);
		}
	}

}

--------優化:將第3步和第4步合併,少一次遍歷--------
public class ArrayDemo1 {
	public static void main(String[] args) {
		// 1.使用Scanner讀取學生個數
		Scanner scanner = new Scanner(System.in);
		System.out.println("請輸入學生人數:");
		int number = scanner.nextInt();
		// 2.建立陣列,儲存學生成績,動態初始化
		int[] scores = new int[number];
		// 3.給陣列中的元素賦值
		System.out.println("請輸入" + number + "個學生成績");
		int maxscore = 0;
		for (int i = 0; i < scores.length; i++) {
			scores[i] = scanner.nextInt();
			// 4.獲取陣列中的元素的最大值
			if (scores[i] > maxscore) {
				maxscore = scores[i];
			}
		}
		// 5.根據每個學生與最高分的差值,得到每個學生的 等級,並輸出登記和成績
		char level;
		for (int i = 0; i < scores.length; i++) {
			if (maxscore - scores[i] <= 10) {
				level = 'A';
			} else if (maxscore - scores[i] <= 20) {
				level = 'B';
			} else if (maxscore - scores[i] <= 30) {
				level = 'C';
			} else {
				level = 'D';
			}
			System.out.println("student " + i + " score is " + scores[i] + ",gread is " + level);
		}
	}

}

2.3 多維陣列的使用

  • 如果說可以把一維陣列當成幾何中的線性圖形,那麼二維陣列就相當於是一個表格,像右圖Excel中的表格一樣。
  • 對於二維陣列的理解,我們可以看成是一維陣列array1又作為另一個一維陣列array2的元素而存在。其實,從陣列底層的執行機制來看,其實沒有多維陣列。
    在這裡插入圖片描述
    在這裡插入圖片描述
package com.atguigu.contact;

public class ArrayTest02 {
	public static void main(String[] args) {
		// 一維陣列
		int[] arr = new int[] { 1, 2, 3 };
		// 二維陣列
		// 靜態初始化
		int[][] arr1 = new int[][] { { 1, 2, 3 }, { 4, 5, 9, 10 }, { 6, 7, 8 } };
		// 動態初始化1
		String[][] arr2 = new String[3][2];
		// 動態初始化2
		String[][] arr3 = new String[3][];
		// 遍歷二維陣列
		for (int i = 0; i < arr1.length; i++) {
			for (int j = 0; j < arr1[i].length; j++) {
				System.out.print(arr1[i][j] + " ");
			}
			System.out.println();
		}
	}
}

二維陣列初始化:

package com.atguigu.contact;
/*
 * 二維陣列的使用:
 *  規定:二維陣列分為外層陣列的元素,內層陣列的元素
 *  	int[][] arr = new int[4][3];
 *  	外層元素:arr[0],arr[1]等
 *  	內層元素:arr[0][0],arr[1][2]等
 *  
 *  陣列元素的預設初始化:
 *  針對於初始化方式一:int[][] arr = new int[4][3];
 *  	外層元素的初始化值為:地址值
 *  	內層元素的初始化值為:與一維陣列初始化情況相同
 *  針對於初始化方式二:int[][] arr = new int[4][];
 *  	外層元素的初始化值為:null
 *  	內層元素的初始化值為:不能呼叫,否則報錯。
 */
public class ArrayTest03 {
	public static void main(String[] args) {
		int[][] arr = new int[4][3];
		System.out.println(arr[0]); // [I@15db9742 地址值
		System.out.println(arr[0][0]); // 0;其他型別跟一維陣列相同
		System.out.println(arr); //[[I@6d06d69c
		
		double[][] arr1 = new double[4][];
		System.out.println(arr1[1]); // null 內層元素沒有初始化
		System.out.println(arr1[1][0]); //報錯:java.lang.NullPointerException  空指標異常
	}
}

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
楊輝三角:

package com.atguigu.contact;

/*
 * 使用二維陣列列印一個 10 行楊輝三角。
 * 
 * 【提示】
1. 第一行有 1 個元素, 第 n 行有 n 個元素
2. 每一行的第一個元素和最後一個元素都是 1
3. 從第三行開始, 對於非第一個元素和最後一個元素的元素。即:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
 */
public class YangHuiTest {
	public static void main(String[] args) {
		// 1.宣告並初始化二維陣列,動態
		int[][] yangHui = new int[10][];

		// 2.給陣列元素賦值
		for (int i = 0; i < yangHui.length; i++) {
			yangHui[i] = new int[i + 1];

			// 2.1 給首末元素賦值
			yangHui[i][0] = yangHui[i][i] = 1;
			// 2.2 給每行非首末元素賦值
			if (i > 1) {
				for (int j = 1; j < yangHui[i].length - 1; j++) {
					yangHui[i][j] = yangHui[i - 1][j - 1] + yangHui[i - 1][j];
				}
			}
		}

		// 3.遍歷二維陣列
		for (int i = 0; i < yangHui.length; i++) {
			for (int j = 0; j < yangHui[i].length; j++) {
				System.out.print(yangHui[i][j] + " ");
			}
			System.out.println();
		}
	}

}

2.4 陣列中涉及的常見演算法

  1. 陣列元素的賦值(楊輝三角、回形數等)
  2. 求數值型陣列中元素的最大值、最小值、平均數、總和等
  3. 陣列的複製、反轉、查詢(線性查詢、二分法查詢)
  4. 陣列元素的排序演算法
    在這裡插入圖片描述
    陣列的複製:
    在這裡插入圖片描述

2.4.1 二分法查詢演算法

在這裡插入圖片描述

//二分法查詢:要求此陣列必須是有序的。
int[] arr3 = new int[]{-99,-54,-2,0,2,33,43,256,999};
boolean isFlag = true;
int number = 256;
//int number = 25;
int head = 0;//首索引位置
int end = arr3.length - 1;//尾索引位置
while(head <= end){
	int middle = (head + end) / 2;
	if(arr3[middle] == number){
		System.out.println("找到指定的元素,索引為:" + middle);
		isFlag = false;
		break;
	}else if(arr3[middle] > number){
		end = middle - 1;
	}else{//arr3[middle] < number
		head = middle + 1;
	}
}
if(isFlag){
	System.out.println("未找打指定的元素");
}

2.4.2 排序演算法

排序:假設含有n個記錄的序列為{R1,R2,…,Rn},其相應的關鍵字序列為{K1,K2,…,Kn}。將這些記錄重新排序為{Ri1,Ri2,…,Rin},使得相應的關鍵字值滿足條Ki1<=Ki2<=…<=Kin,這樣的一種操作稱為排序。

  • 通常來說,排序的目的是快速查詢。

衡量排序演算法的優劣
1.時間複雜度:分析關鍵字的比較次數和記錄的移動次數
2.空間複雜度:分析排序演算法中需要多少輔助記憶體
3.穩定性:若兩個記錄A和B的關鍵字值相等,但排序後A、B的先後次序保持不變,則稱這種排序演算法是穩定的。

排序演算法分類內部排序外部排序

  • 內部排序:整個排序過程不需要藉助於外部儲存器(如磁碟等),所有排
    序操作都在記憶體中完成。
  • 外部排序:參與排序的資料非常多,資料量非常大,計算機無法把整個排
    序過程放在記憶體中完成,必須藉助於外部儲存器(如磁碟)。外部排序最
    常見的是多路歸併排序。可以認為外部排序是由多次內部排序組成。
    在這裡插入圖片描述
    在這裡插入圖片描述
    說明:滿足確定性的演算法也稱為:確定性演算法。現在人們也關注更廣泛的概念,例如考慮各種非確定性的演算法,如並行演算法、概率演算法等。另外,人們也關注並不要求終止的計算描述,這種描述有時被稱為過程(procedure)。

#氣泡排序
介紹:
氣泡排序的原理非常簡單,它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。

排序思想:

  1. 比較相鄰的元素。如果第一個比第二個大(升序),就交換他們兩個。
  2. 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步
    做完後,最後的元素會是最大的數。
  3. 針對所有的元素重複以上的步驟,除了最後一個。
  4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要
    比較為止。
package com.atguigu.contact;

/*
 * 陣列的氣泡排序
 */
public class BubbleSortTest {
	public static void main(String[] args) {
		int[] arr = new int[] { 43, 32, 76, -98, 0, 64, 33, -21, 32, 99 };

		// 氣泡排序
		for (int i = 0; i < arr.length - 1; i++) {
			for (int j = 0; j < arr.length - 1 - i; j++) {
				if (arr[j] > arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}

		for (int i = 0; i < arr.length - 1; i++) {
			System.out.print(arr[i] + " ");
		}
	}

}

#快速排序
介紹:
快速排序通常明顯比同為O(nlogn)的其他演算法更快,因此常被採用,而且快排採用了分治法的思想,所以在很多筆試面試中能經常看到快排的影子。可見掌握快排的重要性。
快速排序是迄今為止所有內排序演算法中速度最快的一種。氣泡排序的升
級版,交換排序的一種。快速排序的時間複雜度為O(nlog(n))

排序思想:

  1. 從數列中挑出一個元素,稱為"基準"(pivot),
  2. 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準
    值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽結束之後,
    該基準就處於數列的中間位置。這個稱為分割槽(partition)操作。
  3. 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數
    列排序。
  4. 遞迴的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好
    了。雖然一直遞迴下去,但是這個演算法總會結束,因為在每次的迭代
    (iteration)中,它至少會把一個元素擺到它最後的位置去。
    在這裡插入圖片描述
    在這裡插入圖片描述
private static void subSort(int[] data, int start, int end) {
	if (start < end) {
		int base = data[start];
		int low = start;
		int high = end + 1;
		while (true) {
			while (low < end && data[++low] - base <= 0)
				;
			while (high > start && data[--high] - base >= 0)
				;
			if (low < high) {
				swap(data, low, high);
			} else {
				break;
			}
		}
		swap(data, start, high);
		
		subSort(data, start, high - 1);//遞迴呼叫
		subSort(data, high + 1, end);
	}
}

#排序演算法效能對比
在這裡插入圖片描述
1.從平均時間而言:快速排序最佳。但在最壞情況下時間效能不如堆排序和歸併排序。
2.從演算法簡單性看:由於直接選擇排序、直接插入排序和氣泡排序的演算法比較簡單,將其認為是簡單演算法。對於Shell排序、堆排序、快速排序和歸併排序演算法,其演算法比較複雜,認為是複雜排序。
3.從穩定性看:直接插入排序、氣泡排序和歸併排序時穩定的;而直接選擇排序、快速排序、 Shell排序和堆排序是不穩定排序
4.從待排序的記錄數n的大小看:n較小時,宜採用簡單排序;而n較大時宜採用改進排序。
#排序演算法的選擇
(1)若n較小(如n≤50),可採用直接插入或直接選擇排序。
當記錄規模較小時,直接插入排序較好;否則因為直接選擇移動的記錄數少於直接插入,應選直接選擇排序為宜。
(2)若檔案初始狀態基本有序(指正序),則應選用直接插入、冒泡或隨機的快速排序為宜;
(3)若n較大,則應採用時間複雜度為O(nlgn)的排序方法:快速排序、堆排序或歸併排序。

2.5 Arrays工具類的使用

java.util.Arrays類即為運算元組的工具類,包含了用來運算元組(比如排序和搜尋)的各種方法。
在這裡插入圖片描述

package com.atguigu.contact;

import java.util.Arrays;

/*
 * java.util.Arrays:運算元組的工具類,裡面定義了很多運算元組的方法
 */
public class ArraysTest {
	public static void main(String[] args) {
		// 1.boolean equals(int[] a,int[] b) :判斷兩個陣列是否相等
		int[] arr1 = new int[] { 1, 2, 3, 4 };
		int[] arr2 = new int[] { 1, 3, 2, 4 };
		boolean isEuales = Arrays.equals(arr1, arr2);
		System.out.println(isEuales); // false
		// 2.boolean equals(int[] a,int[] b) :輸出陣列資訊
		System.out.println(Arrays.toString(arr1)); // [1, 2, 3, 4]

		// 3.void fill(int[] a,int val):將指定值填充到陣列之中
		Arrays.fill(arr1, 10);
		System.out.println(Arrays.toString(arr1)); // [10, 10, 10, 10]

		// 4.void sort(int[] a) :對陣列進行排序。
		Arrays.sort(arr2);
		System.out.println(Arrays.toString(arr2)); // [1, 2, 3, 4]

		// 5.int binarySearch(int[] a,int key) :對排序後的陣列進行二分法檢索指定的值
		int[] arr3 = new int[] { -98, -34, 2, 34, 54, 66, 79, 105, 210, 333 };
		int index = Arrays.binarySearch(arr3, 210);
		if (index >= 0) {
			System.out.println(index); // 8
		} else {
			System.out.println("未找到");
		}
	}

}

2.6 陣列使用中的常見異常

在這裡插入圖片描述

相關文章