學(xué)習(xí)目標(biāo)
- 理解容器的概念
- 掌握一維的聲明和初始化
- 使用索引訪問數(shù)組的元素
- 掌握數(shù)組的遍歷
- 了解數(shù)組的內(nèi)存圖解
- 熟悉空指針和數(shù)組角標(biāo)越界異常
- 掌握數(shù)組基礎(chǔ)算法
- 掌握數(shù)組元素的統(tǒng)計(jì)分析
- 掌握數(shù)組最大值的獲取
- 掌握數(shù)組元素的查找
- 掌握數(shù)組元素的反轉(zhuǎn)
- 掌握數(shù)組的排序
1. 數(shù)組的概述
1.1 容器概述
需求分析1:
現(xiàn)在需要統(tǒng)計(jì)某公司50個(gè)員工的工資情況,例如計(jì)算平均工資、找到最高工資等。用前面所學(xué)的知識(shí)答毫,首先需要聲明50個(gè)變量來(lái)分別記住每位員工的工資锦秒,這樣做會(huì)顯得很麻煩娄猫。因此我們可以使用容器進(jìn)行操作卓起,將所有的數(shù)據(jù)全部存儲(chǔ)到一個(gè)容器中兴蒸,統(tǒng)一操作衫生。
容器概念:
- 生活中的容器:水杯(裝水等液體)裳瘪,衣柜(裝衣服等物品),教室(裝學(xué)生等人員)罪针。
- 程序中的容器:是將多個(gè)數(shù)據(jù)存儲(chǔ)到一起彭羹,每個(gè)數(shù)據(jù)稱為該容器的元素。
1.2 數(shù)組的概念
數(shù)組(Array)泪酱,是多個(gè)相同類型數(shù)據(jù)按一定順序排列的集合派殷,并使用一個(gè)名字命名还最,并通過(guò)編號(hào)的方式對(duì)這些數(shù)據(jù)進(jìn)行統(tǒng)一管理。
-
數(shù)組中的概念
- 數(shù)組名
- 下標(biāo)(或索引)
- 元素
- 數(shù)組的長(zhǎng)度
數(shù)組的特點(diǎn):
數(shù)組本身是
引用數(shù)據(jù)類型
毡惜,而數(shù)組中的元素可以是任何數(shù)據(jù)類型
拓轻,包括基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。創(chuàng)建數(shù)組對(duì)象會(huì)在內(nèi)存中開辟一整塊
連續(xù)的空間
经伙,而數(shù)組名中引用的是這塊連續(xù)空間的首地址扶叉。數(shù)組的
長(zhǎng)度一旦確定,就不能修改
帕膜。我們可以直接通過(guò)下標(biāo)(或索引)的方式調(diào)用指定位置的元素枣氧,速度很快。
1.3 數(shù)組的分類
1垮刹、按照維度分:
- 一維數(shù)組:存儲(chǔ)一組數(shù)據(jù)
- 二維數(shù)組:存儲(chǔ)多組數(shù)據(jù)达吞,相當(dāng)于二維表,一行代表一組數(shù)據(jù)危纫,只是這里的二維表每一行長(zhǎng)度不要求一樣。
2乌庶、按照元素類型分:
- 基本數(shù)據(jù)類型的元素:存儲(chǔ)基本數(shù)據(jù)類型的值
- 引用數(shù)據(jù)類型的元素:存儲(chǔ)對(duì)象(本質(zhì)上存儲(chǔ)對(duì)象的首地址)(這個(gè)在面向?qū)ο蟛糠种v解)
2. 一維數(shù)組的使用
2.1 一維數(shù)組的聲明
- 一維數(shù)組的聲明/定義格式
//推薦
元素的數(shù)據(jù)類型[] 數(shù)組的名稱;
//不推薦
元素的數(shù)據(jù)類型 數(shù)組名[];
舉例:
int a[];
int[] a1;
double b[];
String[] c; //引用類型變量數(shù)組
- 數(shù)組的聲明种蝶,就是要確定:
(1)數(shù)組的維度:在Java中數(shù)組的標(biāo)點(diǎn)符號(hào)是[],[]表示一維瞒大,[][]表示二維
(2)數(shù)組的元素類型:即創(chuàng)建的數(shù)組容器可以存儲(chǔ)什么數(shù)據(jù)類型的數(shù)據(jù)螃征。可以是基本數(shù)據(jù)類型透敌,也可以是引用數(shù)據(jù)類型盯滚。例如:int, String, Student等
(3)數(shù)組名:就是代表某個(gè)數(shù)組的標(biāo)識(shí)符,數(shù)組名其實(shí)也是變量名酗电,按照變量的命名規(guī)范來(lái)命名魄藕。數(shù)組名是個(gè)引用數(shù)據(jù)類型的變量,因?yàn)樗硪唤M數(shù)據(jù)撵术。
- 示例
public class Test01ArrayDeclare {
public static void main(String[] args) {
//比如背率,要存儲(chǔ)一個(gè)小組的成績(jī)
int[] scores;
int grades[];
// System.out.println(scores);//未初始化不能使用
//比如,要存儲(chǔ)一組字母
char[] letters;
//比如嫩与,要存儲(chǔ)一組姓名
String[] names;
//比如寝姿,要存儲(chǔ)一組價(jià)格
double[] prices;
}
}
注意:Java語(yǔ)言中聲明數(shù)組時(shí)不能指定其長(zhǎng)度(數(shù)組中元素的數(shù))。 例如: int a[5]; //非法
2.2 一維數(shù)組的初始化
2.2.1 靜態(tài)初始化
什么是靜態(tài)初始化划滋?
靜態(tài)初始化就是用靜態(tài)數(shù)據(jù)(編譯時(shí)已知)為數(shù)組初始化饵筑。此時(shí)數(shù)組的長(zhǎng)度由靜態(tài)數(shù)據(jù)的個(gè)數(shù)決定。
-
一維數(shù)組靜態(tài)初始化格式1:
數(shù)據(jù)類型[] 數(shù)組名 = new 數(shù)據(jù)類型[]{元素1,元素2,元素3...}; 或 數(shù)據(jù)類型[] 數(shù)組名; 數(shù)組名 = new 數(shù)據(jù)類型[]{元素1,元素2,元素3...};
- new:關(guān)鍵字处坪,創(chuàng)建數(shù)組使用的關(guān)鍵字根资。因?yàn)閿?shù)組本身是引用數(shù)據(jù)類型架专,所以要用new創(chuàng)建數(shù)組對(duì)象。
例如嫂冻,定義存儲(chǔ)1胶征,2,3桨仿,4睛低,5整數(shù)的數(shù)組容器。
int[] arr = new int[]{1,2,3,4,5};//正確 //或 int[] arr; arr = new int[]{1,2,3,4,5};//正確
-
一維數(shù)組靜態(tài)初始化格式2:
數(shù)據(jù)類型[] 數(shù)組名 = {元素1,元素2,元素3...};//必須在一個(gè)語(yǔ)句中完成服傍,不能分開兩個(gè)語(yǔ)句寫
例如钱雷,定義存儲(chǔ)1,2吹零,3罩抗,4,5整數(shù)的數(shù)組容器
int[] arr = {1,2,3,4,5};//正確
int[] arr;
arr = {1,2,3,4,5};//錯(cuò)誤
- 舉例
public class Test02ArrayInitialize {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};//右邊不需要寫new int[]
int[] nums;
nums = new int[]{10,20,30,40}; //聲明和初始化在兩個(gè)語(yǔ)句完成灿椅,就不能使用new int[]
char[] word = {'h','e','l','l','o'};
String[] heros = {"袁隆平","鄧稼先","錢學(xué)森"};
System.out.println("arr數(shù)組:" + arr);//arr數(shù)組:[I@1b6d3586
System.out.println("nums數(shù)組:" + nums);//nums數(shù)組:[I@4554617c
System.out.println("word數(shù)組:" + word);//word數(shù)組:[C@74a14482
System.out.println("heros數(shù)組:" + heros);//heros數(shù)組:[Ljava.lang.String;@1540e19d
}
}
2.2.2 動(dòng)態(tài)初始化
- 什么是動(dòng)態(tài)初始化套蒂?
動(dòng)態(tài)初始化就是先確定元素的個(gè)數(shù)(即數(shù)組的長(zhǎng)度),而元素值此時(shí)只是默認(rèn)值茫蛹,還并未真正附自己期望的值操刀。真正期望的數(shù)據(jù)需要后續(xù)單獨(dú)一個(gè)一個(gè)賦值。
- 格式:
數(shù)組存儲(chǔ)的元素的數(shù)據(jù)類型[] 數(shù)組名字 = new 數(shù)組存儲(chǔ)的元素的數(shù)據(jù)類型[長(zhǎng)度];
或
數(shù)組存儲(chǔ)的數(shù)據(jù)類型[] 數(shù)組名字; 或 數(shù)據(jù)類型 數(shù)組名[]; (括號(hào)在數(shù)組名后也可以)
數(shù)組名字 = new 數(shù)組存儲(chǔ)的數(shù)據(jù)類型[長(zhǎng)度];
[長(zhǎng)度]:數(shù)組的長(zhǎng)度婴洼,表示數(shù)組容器中可以最多存儲(chǔ)多少個(gè)元素骨坑。
注意:數(shù)組有定長(zhǎng)特性,長(zhǎng)度一旦指定柬采,不可更改欢唾。和水杯道理相同,買了一個(gè)2升的水杯粉捻,總?cè)萘烤褪?升是固定的礁遣。
舉例1
int[] arr = new int[5];
int[] arr;
arr = new int[5];
int[] arr = new int[5]{1,2,3,4,5};//錯(cuò)誤的,后面有{}指定元素列表肩刃,就不需要在[]中指定元素個(gè)數(shù)了亡脸。
-
錯(cuò)誤情況:
//double[] arr = new double[2]{12.3,34.5}; //double[2] arr1 = new double[2];
2.3 一維數(shù)組的使用
2.3.1 數(shù)組的長(zhǎng)度
- 數(shù)組的元素總個(gè)數(shù),即數(shù)組的長(zhǎng)度
- 每個(gè)數(shù)組都有一個(gè)屬性length指明它的長(zhǎng)度树酪,例如:a.length 指明數(shù)組a的長(zhǎng)度(元素個(gè)數(shù))
- 每個(gè)數(shù)組都具有長(zhǎng)度浅碾,而且一旦初始化,其長(zhǎng)度是不可變的
2.3.2 數(shù)組元素的引用
- 如何表示數(shù)組中的一個(gè)元素续语?
每一個(gè)存儲(chǔ)到數(shù)組的元素垂谢,都會(huì)自動(dòng)的擁有一個(gè)編號(hào),從0開始疮茄,這個(gè)自動(dòng)編號(hào)稱為數(shù)組索引(index)或下標(biāo)
滥朱,可以通過(guò)數(shù)組的索引/下標(biāo)訪問到數(shù)組中的元素根暑。
數(shù)組名[索引/下標(biāo)]
-
數(shù)組的下標(biāo)范圍?
Java中數(shù)組的下標(biāo)從[0]開始徙邻,下標(biāo)范圍是[0, 數(shù)組的長(zhǎng)度-1]排嫌,即[0, 數(shù)組名.length-1]
數(shù)組元素下標(biāo)可以是
整型常量或整型表達(dá)式
。如a[3] , b[i] , c[6*i];舉例
public class Test03ArrayUse {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println("arr數(shù)組的長(zhǎng)度:" + arr.length);
System.out.println("arr數(shù)組的第1個(gè)元素:" + arr[0]);//下標(biāo)從0開始 1
System.out.println("arr數(shù)組的第2個(gè)元素:" + arr[1]); //2
System.out.println("arr數(shù)組的第3個(gè)元素:" + arr[2]); //3
System.out.println("arr數(shù)組的第4個(gè)元素:" + arr[3]); //4
System.out.println("arr數(shù)組的第5個(gè)元素:" + arr[4]); //5
//修改第1個(gè)元素的值
//此處arr[0]相當(dāng)于一個(gè)int類型的變量
arr[0] = 100;
System.out.println("arr數(shù)組的第1個(gè)元素:" + arr[0]); //100
}
}
2.4 一維數(shù)組的遍歷
數(shù)組遍歷: 就是將數(shù)組中的每個(gè)元素分別獲取出來(lái)缰犁,就是遍歷淳地。遍歷也是數(shù)組操作中的基石。for循環(huán)與數(shù)組的遍歷是絕配帅容。
- 舉例1
public class Test05ArrayIterate {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印數(shù)組的屬性颇象,輸出結(jié)果是5
System.out.println("數(shù)組的長(zhǎng)度:" + arr.length); //5
//遍歷輸出數(shù)組中的元素
System.out.println("數(shù)組的元素有:");
for(int i=0; i<arr.length; i++){
System.out.println(arr[i]);
}
}
}
//1
//2
//3
//4
//5
- 舉例2
public class Test06ArrayInitialize {
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println("arr數(shù)組的長(zhǎng)度:" + arr.length); //5
System.out.print("存儲(chǔ)數(shù)據(jù)到arr數(shù)組之前:[");
for (int i = 0; i < arr.length; i++) { //存儲(chǔ)數(shù)據(jù)到arr數(shù)組之前:[0,0,0,0,0]
if(i==0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
//初始化
arr[0] = 2;
arr[1] = 4;
arr[2] = 6;
arr[3] = 8;
arr[4] = 10;
for (int i = 0; i < arr.length; i++) { //arr = {2,4,6,8,10}
arr[i] = (i+1) * 2;
}
System.out.print("存儲(chǔ)數(shù)據(jù)到arr數(shù)組之后:[");
for (int i = 0; i < arr.length; i++) { //存儲(chǔ)數(shù)據(jù)到arr數(shù)組之后:[2,4,6,8,10]
if(i==0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
}
}
2.5 數(shù)組元素的默認(rèn)值
數(shù)組是引用類型,當(dāng)我們使用動(dòng)態(tài)初始化方式創(chuàng)建數(shù)組時(shí)并徘,元素值只是默認(rèn)值遣钳。例如:
public class Test {
public static void main(String argv[]){
int a[]= new int[5];
System.out.println(a[3]); //a[3]的默認(rèn)值為0
}
}
對(duì)于基本數(shù)據(jù)類型而言,默認(rèn)初始化值各有不同麦乞。
對(duì)于引用數(shù)據(jù)類型而言蕴茴,默認(rèn)初始化值為null(注意與0不同!)
public class Test07ArrayElementDefaultValue {
public static void main(String[] args) {
//存儲(chǔ)26個(gè)字母
char[] letters = new char[26];
System.out.println("letters數(shù)組的長(zhǎng)度:" + letters.length); //letters數(shù)組的長(zhǎng)度:26
System.out.print("存儲(chǔ)字母到letters數(shù)組之前:[");
for (int i = 0; i < letters.length; i++) { //存儲(chǔ)字母到letters數(shù)組之前:[0,0,0......0] 26個(gè)
if(i==0){
System.out.print(letters[i]);
}else{
System.out.print("," + letters[i]);
}
}
System.out.println("]");
//存儲(chǔ)5個(gè)姓名
String[] names = new String[5];
System.out.println("names數(shù)組的長(zhǎng)度:" + names.length); //names數(shù)組的長(zhǎng)度:5
System.out.print("存儲(chǔ)姓名到names數(shù)組之前:[");
for (int i = 0; i < names.length; i++) { //存儲(chǔ)姓名到names數(shù)組之前:[null,null,null,null,null]
if(i==0){
System.out.print(names[i]);
}else{
System.out.print("," + names[i]);
}
}
System.out.println("]");
}
}
3. 一維數(shù)組內(nèi)存分析
3.1 內(nèi)存概述
內(nèi)存是計(jì)算機(jī)中重要的部件之一姐直,它是與CPU進(jìn)行溝通的橋梁倦淀。其作用是用于暫時(shí)存放CPU中的運(yùn)算數(shù)據(jù),以及與硬盤等外部存儲(chǔ)器交換的數(shù)據(jù)简肴。只要計(jì)算機(jī)在運(yùn)行中晃听,CPU就會(huì)把需要運(yùn)算的數(shù)據(jù)調(diào)到內(nèi)存中進(jìn)行運(yùn)算百侧,當(dāng)運(yùn)算完成后CPU再將結(jié)果傳送出來(lái)砰识。我們編寫的程序是存放在硬盤中的,在硬盤中的程序是不會(huì)運(yùn)行的佣渴,必須放進(jìn)內(nèi)存中才能運(yùn)行辫狼,運(yùn)行完畢后會(huì)清空內(nèi)存。
Java虛擬機(jī)要運(yùn)行程序辛润,必須要對(duì)內(nèi)存進(jìn)行空間的分配和管理膨处。
3.2 Java虛擬機(jī)的內(nèi)存劃分
為了提高運(yùn)算效率,就對(duì)空間進(jìn)行了不同區(qū)域的劃分砂竖,因?yàn)槊恳黄瑓^(qū)域都有特定的處理數(shù)據(jù)方式和內(nèi)存管理方式真椿。
區(qū)域名稱 | 作用 |
---|---|
程序計(jì)數(shù)器 | 程序計(jì)數(shù)器是CPU中的寄存器,它包含每一個(gè)線程下一條要執(zhí)行的指令的地址 |
本地方法棧 | 當(dāng)程序中調(diào)用了native的本地方法時(shí)乎澄,本地方法執(zhí)行期間的內(nèi)存區(qū)域 |
方法區(qū) | 存儲(chǔ)已被虛擬機(jī)加載的類信息突硝、常量、靜態(tài)變量带迟、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)钦讳。 |
堆內(nèi)存 | 存儲(chǔ)對(duì)象(包括數(shù)組對(duì)象),new來(lái)創(chuàng)建的芹助,都存儲(chǔ)在堆內(nèi)存护盈。 |
虛擬機(jī)棧 | 用于存儲(chǔ)正在執(zhí)行的每個(gè)Java方法的局部變量表等挟纱。局部變量表存放了編譯期可知長(zhǎng)度的各種基本數(shù)據(jù)類型、對(duì)象引用腐宋,方法執(zhí)行完紊服,自動(dòng)釋放。 |
3.3 一維數(shù)組在內(nèi)存中的存儲(chǔ)
1脏款、一個(gè)一維數(shù)組內(nèi)存圖
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr);//[I@5f150435
}
思考:打印arr為什么是[I@5f150435围苫,它是數(shù)組的地址嗎?
答:它不是數(shù)組的地址撤师。
問剂府?不是說(shuō)arr中存儲(chǔ)的是數(shù)組對(duì)象的首地址嗎?
答:arr中存儲(chǔ)的是數(shù)組的首地址剃盾,但是因?yàn)閿?shù)組是引用數(shù)據(jù)類型腺占,打印arr時(shí),會(huì)自動(dòng)調(diào)用arr數(shù)組對(duì)象的toString()方法痒谴,該方法默認(rèn)實(shí)現(xiàn)的是對(duì)象類型名@該對(duì)象的hashCode()值的十六進(jìn)制值衰伯。
問?對(duì)象的hashCode值是否就是對(duì)象內(nèi)存地址积蔚?
答:不一定意鲸,因?yàn)檫@個(gè)和不同品牌的JVM產(chǎn)品的具體實(shí)現(xiàn)有關(guān)。例如:Oracle的OpenJDK中給出了5種實(shí)現(xiàn)尽爆,其中有一種是直接返回對(duì)象的內(nèi)存地址怎顾,但是OpenJDK默認(rèn)沒有選擇這種方式。
2漱贱、數(shù)組下標(biāo)為什么是0開始
因?yàn)榈谝粋€(gè)元素距離數(shù)組首地址間隔0個(gè)單元格槐雾。
3、兩個(gè)一維數(shù)組內(nèi)存圖
兩個(gè)數(shù)組獨(dú)立
public static void main(String[] args) {
int[] arr = new int[3];
int[] arr2 = new int[2];
System.out.println(arr);
System.out.println(arr2);
}
4幅狮、兩個(gè)變量指向一個(gè)一維數(shù)組
- 兩個(gè)數(shù)組變量本質(zhì)上
代表同一個(gè)數(shù)組
募强。首地址值相同
。
public static void main(String[] args) {
// 定義數(shù)組崇摄,存儲(chǔ)3個(gè)元素
int[] arr = new int[3];
//數(shù)組索引進(jìn)行賦值
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;
//輸出3個(gè)索引上的元素值
System.out.println(arr[0]); //5
System.out.println(arr[1]); //6
System.out.println(arr[2]); //7
//定義數(shù)組變量arr2擎值,將arr的地址賦值給arr2
int[] arr2 = arr;
arr2[1] = 9;
System.out.println(arr[1]); //9
}
4. 一維數(shù)組的應(yīng)用
4.1 案例1
升景坊單間短期出租4個(gè)月,550元/月(水電煤公攤逐抑,網(wǎng)費(fèi)35元/月)鸠儿,空調(diào)、衛(wèi)生間泵肄、廚房齊全捆交。屋內(nèi)均是IT行業(yè)人士淑翼,喜歡安靜。所以要求來(lái)租者最好是同行或者剛畢業(yè)的年輕人品追,愛干凈玄括、安靜。
public class ArrayTest {
public static void main(String[] args) {
int[] arr = new int[]{8,2,1,0,3}; //號(hào)碼中包含的數(shù)
int[] index = new int[]{2,0,3,2,4,0,1,3,2,3,3}; //對(duì)應(yīng)數(shù)的位置
String tel = "";
for(int i = 0;i < index.length;i++){
tel += arr[index[i]];
}
System.out.println("聯(lián)系方式:" + tel); //18013820100
}
}
4.2 案例2:
從鍵盤讀入學(xué)生成績(jī)肉瓦,找出最高分遭京,并輸出學(xué)生成績(jī)等級(jí)。
成績(jī)>=最高分-10 等級(jí)為’A’
成績(jī)>=最高分-20 等級(jí)為’B’
成績(jī)>=最高分-30 等級(jí)為’C’
其余 等級(jí)為’D’
提示:先讀入學(xué)生人數(shù)泞莉,根據(jù)人數(shù)創(chuàng)建int數(shù)組哪雕,存放學(xué)生成績(jī)。
//1. 實(shí)例化Scanner,獲取學(xué)生人數(shù)
Scanner scan = new Scanner(System.in);
System.out.println("請(qǐng)輸入學(xué)生人數(shù):");
int num = scan.nextInt();
//2.根據(jù)人數(shù)鲫趁,創(chuàng)建指定長(zhǎng)度的數(shù)組 (使用動(dòng)態(tài)初始化)
int[] scores = new int[num];
//3. 遍歷數(shù)組斯嚎,通過(guò)控制臺(tái)的方式給每個(gè)數(shù)組元素賦值
System.out.println("請(qǐng)輸入" + num + "個(gè)成績(jī):");
for(int i = 0;i < scores.length;i++){
int score = scan.nextInt();
scores[i] = score;
}
//4. 獲取數(shù)組中元素的最大值
int maxScore = scores[0];
for(int i = 1;i < scores.length;i++){
if(maxScore < scores[i]){
maxScore = scores[i];
}
}
System.out.println("最高分為:" + maxScore);
//5. 遍歷數(shù)組,判斷每個(gè)學(xué)生成績(jī)與最高分的差值挨厚,根據(jù)差值堡僻,決定每個(gè)學(xué)生的等級(jí),并輸出
for(int i = 0;i < scores.length;i++){
char grade;
if(scores[i] >= maxScore - 10){
grade = 'A';
}else if(scores[i] >= maxScore - 20){
grade = 'B';
}else if(scores[i] >= maxScore - 30){
grade = 'C';
}else{
grade = 'D';
}
System.out.println("student " + i + " score is " + scores[i] + " grade is " + grade);
}
5. 多維數(shù)組的使用
5.1 概述
- Java 語(yǔ)言里提供了支持多維數(shù)組的語(yǔ)法疫剃。
- 如果說(shuō)可以把一維數(shù)組當(dāng)成幾何中的線性圖形钉疫,那么二維數(shù)組就相當(dāng)于是一個(gè)表格,像右圖Excel中的表格一樣巢价。
- 對(duì)于二維數(shù)組的理解牲阁,我們可以看成是一維數(shù)組array1又作為另一個(gè)一維數(shù)組array2的元素而存在。其實(shí)壤躲,從數(shù)組底層的運(yùn)行機(jī)制來(lái)看城菊,其實(shí)沒有多維數(shù)組。
5.2 聲明與初始化
5.2.1 聲明
二維數(shù)組聲明的語(yǔ)法格式:
//推薦
元素的數(shù)據(jù)類型[][] 二維數(shù)組的名稱;
//不推薦
元素的數(shù)據(jù)類型 二維數(shù)組名[][];
//不推薦
元素的數(shù)據(jù)類型[] 二維數(shù)組名[];
例如:
public class Test20TwoDimensionalArrayDefine {
public static void main(String[] args) {
//存儲(chǔ)多組成績(jī)
int[][] grades;
//存儲(chǔ)多組姓名
String[][] names;
}
}
面試:
int[] x, y[];
//x是一維數(shù)組柒爵,y是二維數(shù)組
5.2.2 靜態(tài)初始化
格式:
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};
定義一個(gè)名稱為arr的二維數(shù)組役电,二維數(shù)組中有三個(gè)一維數(shù)組
- 每一個(gè)一維數(shù)組中具體元素也都已初始化
- 第一個(gè)一維數(shù)組 arr[0] = {3,8,2};
- 第二個(gè)一維數(shù)組 arr[1] = {2,7};
- 第三個(gè)一維數(shù)組 arr[2] = {9,0,1,6};
- 第三個(gè)一維數(shù)組的長(zhǎng)度表示方式:arr[2].length;
- 注意特殊寫法情況:int[] x,y[]; x是一維數(shù)組赚爵,y是二維數(shù)組棉胀。
- 示例:
舉例1:
int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//聲明與初始化必須在一句完成
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
int[][] arr;
arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//錯(cuò)誤,靜態(tài)初始化右邊new 數(shù)據(jù)類型[][]中不能寫數(shù)字
舉例2:
public class TwoDimensionalArrayInitialize {
public static void main(String[] args) {
//存儲(chǔ)多組成績(jī)
int[][] grades = {
{89,75,99,100},
{88,96,78,63,100,86},
{56,63,58},
{99,66,77,88}
};
//存儲(chǔ)多組姓名
String[][] names = {
{"張三","李四", "王五", "趙六"},
{"劉備","關(guān)羽","張飛","諸葛亮","趙云","馬超"},
{"曹丕","曹植","曹沖"},
{"孫權(quán)","周瑜","魯肅","黃蓋"}
};
}
}
5.2.3 動(dòng)態(tài)初始化
如果二維數(shù)組的每一個(gè)數(shù)據(jù)冀膝,甚至是每一行的列數(shù)唁奢,需要后期單獨(dú)確定,那么就只能使用動(dòng)態(tài)初始化方式了窝剖。動(dòng)態(tài)初始化方式分為兩種格式:
格式1:規(guī)則二維表:每一行的列數(shù)是相同的
//(1)確定行數(shù)和列數(shù)
元素的數(shù)據(jù)類型[][] 二維數(shù)組名 = new 元素的數(shù)據(jù)類型[m][n];
//其中麻掸,m:表示這個(gè)二維數(shù)組有多少個(gè)一維數(shù)組〈蜕矗或者說(shuō)一共二維表有幾行
//其中脊奋,n:表示每一個(gè)一維數(shù)組的元素有多少個(gè)熬北。或者說(shuō)每一行共有一個(gè)單元格
//此時(shí)創(chuàng)建完數(shù)組诚隙,行數(shù)讶隐、列數(shù)確定,而且元素也都有默認(rèn)值
//(2)再為元素賦新值
二維數(shù)組名[行下標(biāo)][列下標(biāo)] = 值;
舉例:
int[][] arr = new int[3][2];
定義了名稱為arr的二維數(shù)組
二維數(shù)組中有3個(gè)一維數(shù)組
每一個(gè)一維數(shù)組中有2個(gè)元素
一維數(shù)組的名稱分別為arr[0], arr[1], arr[2]
給第一個(gè)一維數(shù)組1腳標(biāo)位賦值為78寫法是:
arr[0][1] = 78;
格式2:不規(guī)則:每一行的列數(shù)不一樣
//(1)先確定總行數(shù)
元素的數(shù)據(jù)類型[][] 二維數(shù)組名 = new 元素的數(shù)據(jù)類型[總行數(shù)][];
//此時(shí)只是確定了總行數(shù)久又,每一行里面現(xiàn)在是null
//(2)再確定每一行的列數(shù)巫延,創(chuàng)建每一行的一維數(shù)組
二維數(shù)組名[行下標(biāo)] = new 元素的數(shù)據(jù)類型[該行的總列數(shù)];
//此時(shí)已經(jīng)new完的行的元素就有默認(rèn)值了,沒有new的行還是null
//(3)再為元素賦值
二維數(shù)組名[行下標(biāo)][列下標(biāo)] = 值;
舉例:
int[][] arr = new int[3][];
- 二維數(shù)組中有3個(gè)一維數(shù)組地消。
- 每個(gè)一維數(shù)組都是默認(rèn)初始化值null (注意:區(qū)別于格式1)
- 可以對(duì)這個(gè)三個(gè)一維數(shù)組分別進(jìn)行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
- 注:
int[][]arr = new int[][3];
//非法
練習(xí):
/*
1
2 2
3 3 3
4 4 4 4
5 5 5 5 5
*/
public class Test25DifferentElementCount {
public static void main(String[] args){
//1炉峰、聲明一個(gè)二維數(shù)組,并且確定行數(shù)
//因?yàn)槊恳恍械牧袛?shù)不同脉执,這里無(wú)法直接確定列數(shù)
int[][] arr = new int[5][];
//2疼阔、確定每一行的列數(shù)
for(int i=0; i<arr.length; i++){
/*
arr[0] 的列數(shù)是1
arr[1] 的列數(shù)是2
arr[2] 的列數(shù)是3
arr[3] 的列數(shù)是4
arr[4] 的列數(shù)是5
*/
arr[i] = new int[i+1];
}
//3、確定元素的值
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
arr[i][j] = i+1;
}
}
//4半夷、遍歷顯示
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
小結(jié):
Java中多維數(shù)組不必都是規(guī)則矩陣形式
5.3 使用說(shuō)明
因?yàn)槎S數(shù)組是用來(lái)存儲(chǔ)多組數(shù)據(jù)的竿开,因此要比一維數(shù)組麻煩一些,需要我們搞清楚如下幾個(gè)概念:
- 二維數(shù)組的長(zhǎng)度/行數(shù):二維數(shù)組名.length
- 二維數(shù)組的某一行:二維數(shù)組名[行下標(biāo)]玻熙,此時(shí)相當(dāng)于獲取其中一組數(shù)據(jù)否彩。它本質(zhì)上是一個(gè)一維數(shù)組。行下標(biāo)的范圍:[0, 二維數(shù)組名.length-1]嗦随。此時(shí)把二維數(shù)組看成一維數(shù)組的話列荔,元素是行對(duì)象。
- 某一行的列數(shù):二維數(shù)組名[行下標(biāo)].length枚尼,因?yàn)槎S數(shù)組的每一行是一個(gè)一維數(shù)組贴浙。
- 某一個(gè)元素:二維數(shù)組名[行下標(biāo)][列下標(biāo)],即先確定行/組署恍,再確定列崎溃。
public class Test22TwoDimensionalArrayUse {
public static void main(String[] args){
//存儲(chǔ)3個(gè)小組的學(xué)員的成績(jī),分開存儲(chǔ)盯质,使用二維數(shù)組袁串。
/*
int[][] scores1;
int scores2[][];
int[] scores3[];*/
int[][] scores = {
{85,96,85,75},
{99,96,74,72,75},
{52,42,56,75}
};
System.out.println(scores);//[[I@15db9742
System.out.println("一共有" + scores.length +"組成績(jī).");
//[[:代表二維數(shù)組,I代表元素類型是int
System.out.println(scores[0]);//[I@6d06d69c
//[:代表一維數(shù)組呼巷,I代表元素類型是int
System.out.println(scores[1]);//[I@7852e922
System.out.println(scores[2]);//[I@4e25154f
//System.out.println(scores[3]);//ArrayIndexOutOfBoundsException: 3
System.out.println("第1組有" + scores[0].length +"個(gè)學(xué)員.");
System.out.println("第2組有" + scores[1].length +"個(gè)學(xué)員.");
System.out.println("第3組有" + scores[2].length +"個(gè)學(xué)員.");
System.out.println("第1組的每一個(gè)學(xué)員成績(jī)?nèi)缦拢?);
//第一行的元素
System.out.println(scores[0][0]);//85
System.out.println(scores[0][1]);//96
System.out.println(scores[0][2]);//85
System.out.println(scores[0][3]);//75
//System.out.println(scores[0][4]);//java.lang.ArrayIndexOutOfBoundsException: 4
}
}
二維數(shù)組的遍歷:
- 格式:
for(int i=0; i<二維數(shù)組名.length; i++){ //二維數(shù)組對(duì)象.length
for(int j=0; j<二維數(shù)組名[i].length; j++){//二維數(shù)組行對(duì)象.length
System.out.print(二維數(shù)組名[i][j]);
}
System.out.println();
}
- 舉例:
public class Test23TwoDimensionalArrayIterate {
public static void main(String[] args) {
//存儲(chǔ)3個(gè)小組的學(xué)員的成績(jī)囱修,分開存儲(chǔ),使用二維數(shù)組王悍。
int[][] scores = {
{85,96,85,75},
{99,96,74,72,75},
{52,42,56,75}
};
System.out.println("一共有" + scores.length +"組成績(jī).");
for (int i = 0; i < scores.length; i++) {
System.out.print("第" + (i+1) +"組有" + scores[i].length + "個(gè)學(xué)員破镰,成績(jī)?nèi)缦拢?);
for (int j = 0; j < scores[i].length; j++) {
System.out.print(scores[i][j]+"\t");
}
System.out.println();
}
}
}
5.4 內(nèi)存解析
二維數(shù)組本質(zhì)上是元素類型是一維數(shù)組的一維數(shù)組。
int[][] arr = {
{1},
{2,2},
{3,3,3},
{4,4,4,4},
{5,5,5,5,5}
};
//1、聲明二維數(shù)組鲜漩,并確定行數(shù)和列數(shù)
int[][] arr = new int[4][5];
//2源譬、確定元素的值
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
arr[i][j] = i + 1;
}
}
//1、聲明一個(gè)二維數(shù)組孕似,并且確定行數(shù)
//因?yàn)槊恳恍械牧袛?shù)不同瓶佳,這里無(wú)法直接確定列數(shù)
int[][] arr = new int[5][];
//2、確定每一行的列數(shù)
for(int i=0; i<arr.length; i++){
/*
arr[0] 的列數(shù)是1
arr[1] 的列數(shù)是2
arr[2] 的列數(shù)是3
arr[3] 的列數(shù)是4
arr[4] 的列數(shù)是5
*/
arr[i] = new int[i+1];
}
//3鳞青、確定元素的值
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
arr[i][j] = i+1;
}
}
5.6 應(yīng)用舉例
案例1:獲取arr數(shù)組中所有元素的和霸饲。
提示:使用for的嵌套循環(huán)即可。
int[][] arr = {{3,5,8},{12,9},{7,0,6,4}};
int sum = 0;
for(int i = 0;i < arr.length ;i++){
for(int j = 0; j < arr[i].length;j++){
sum+=arr[i][j];
}
}
System.out.println("所有元素的和:"+sum);
案例2:聲明:int[] x,y[]; 在給x,y變量賦值以后臂拓,以下選項(xiàng)允許通過(guò)編譯的是:
聲明:int[] x,y[]; 在給x,y變量賦值以后厚脉,以下選項(xiàng)允許通過(guò)編譯的是:
a) x[0] = y; //no
b) y[0] = x; //yes
c) y[0][0] = x; //no
d) x[0][0] = y; //no
e) y[0][0] = x[0]; //yes
f) x = y; //no
提示:
一維數(shù)組:int[] x 或者int x[]
二維數(shù)組:int[][] y 或者 int[] y[] 或者 int y[][]
案例3:使用二維數(shù)組打印一個(gè) 10 行楊輝三角。
提示:
第一行有 1 個(gè)元素, 第 n 行有 n 個(gè)元素
每一行的第一個(gè)元素和最后一個(gè)元素都是 1
-
從第三行開始, 對(duì)于非第一個(gè)元素和最后一個(gè)元素的元素胶惰。即:
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
//1.二維數(shù)組的聲明和初始化(動(dòng)態(tài)初始化)
int[][] yangHui = new int[10][];
//2.通過(guò)for循環(huán)給外層數(shù)組元素yangHui[i]初始化
for(int i = 0;i <yangHui.length;i++){
yangHui[i] = new int[i+1];
//3.賦值:每一行的第一個(gè)元素和最后一個(gè)元素都是 1
yangHui[i][0] = 1;
yangHui[i][i] = 1;
//4.賦值:從第三行開始傻工,對(duì)于非第一個(gè)元素和最后一個(gè)元素的元素的賦值
for (int j = 1; j < i ;j++){
yangHui[i][j] = yangHui[i-1][j-1] + yangHui[i-1][j];
}
}
for (int i = 0; i < yangHui.length; i++) {
//System.out.println(Arrays.toString(yangHui[i]));
for(int j = 0; j < yangHui[i].length;j++){
System.out.print(yangHui[i][j] + " ");
}
System.out.println();
}
6. 數(shù)組的常見算法
6.1 數(shù)值型數(shù)組特征值統(tǒng)計(jì)
- 這里的特征值涉及到:平均值、最大值孵滞、最小值中捆、總和等
舉例1:數(shù)組統(tǒng)計(jì):求總和、均值
public class TestArrayElementSum {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//求總和坊饶、均值
int sum = 0;//因?yàn)?加上任何數(shù)都不影響結(jié)果
for(int i=0; i<arr.length; i++){
sum += arr[i];
}
double avg = (double)sum/arr.length;
System.out.println("sum = " + sum);
System.out.println("avg = " + avg);
}
}
舉例2:求數(shù)組元素的總乘積
public class TestArrayElementMul {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//求總乘積
long result = 1;//因?yàn)?乘以任何數(shù)都不影響結(jié)果
for(int i=0; i<arr.length; i++){
result *= arr[i];
}
System.out.println("result = " + result);
}
}
舉例3:求數(shù)組元素中偶數(shù)的個(gè)數(shù)
public class TestArrayElementEvenCount {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//統(tǒng)計(jì)偶數(shù)個(gè)數(shù)
int evenCount = 0;
for(int i=0; i<arr.length; i++){
if(arr[i]%2==0){
evenCount++;
}
}
System.out.println("evenCount = " + evenCount);
}
}
舉例4:求數(shù)組元素的最大值
public class TestArrayMax {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//找最大值
int max = arr[0];
for(int i=1; i<arr.length; i++){//此處i從1開始泄伪,是max不需要與arr[0]再比較一次了
if(arr[i] > max){
max = arr[i];
}
}
System.out.println("max = " + max);
}
}
舉例5:找最值及其第一次出現(xiàn)的下標(biāo):
public class TestMaxIndex {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//找最大值以及第一個(gè)最大值下標(biāo)
int max = arr[0];
int index = 0;
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
index = i;
}
}
System.out.println("max = " + max);
System.out.println("index = " + index);
}
}
舉例6:找最值及其所有最值的下標(biāo)
public class Test13AllMaxIndex {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9,9,3};
//找最大值
int max = arr[0];
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
System.out.println("最大值是:" + max);
System.out.print("最大值的下標(biāo)有:");
//遍歷數(shù)組,看哪些元素和最大值是一樣的
for(int i=0; i<arr.length; i++){
if(max == arr[i]){
System.out.print(i+"\t");
}
}
System.out.println();
}
}
優(yōu)化
public class Test13AllMaxIndex2 {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9,9,3};
//找最大值
int max = arr[0];
String index = "0";
for(int i=1; i<arr.length; i++){
if(arr[i] > max){
max = arr[i];
index = i + "";
}else if(arr[i] == max){
index += "," + i;
}
}
System.out.println("最大值是" + max);
System.out.println("最大值的下標(biāo)是[" + index+"]");
}
}
舉例7:輸入一個(gè)整形數(shù)組匿级,數(shù)組里有正數(shù)也有負(fù)數(shù)蟋滴。數(shù)組中連續(xù)的一個(gè)或多個(gè)整數(shù)組成一個(gè)子數(shù)組,每個(gè)子數(shù)組都有一個(gè)和痘绎。求所有子數(shù)組的和的最大值津函。要求時(shí)間復(fù)雜度為O(n)。
例如:輸入的數(shù)組為1, -2, 3, -10, -4, 7, 2, -5孤页,和最大的子數(shù)組為3, 10, -4, 7, 2尔苦,因此輸出為該子數(shù)組的和18。
public class Test5 {
public static void main(String[] args) {
int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5};
int i = getGreatestSum(arr);
System.out.println(i);
}
public static int getGreatestSum(int[] arr){
int greatestSum = 0;
if(arr == null || arr.length == 0){ //當(dāng)arr為空時(shí)返回null
return 0;
}
int temp = greatestSum; //將子數(shù)組最大值賦給temp
for(int i = 0;i < arr.length;i++){ //遍歷arr數(shù)組
temp += arr[i]; //將arr元素的值加到temp上
if(temp < 0){ //如果temp小于0行施,將temp重新復(fù)制成0
temp = 0;
}
if(temp > greatestSum){ //temp總和值 大于 子數(shù)組的值greatestSum 允坚,將temp 賦給 greatestSum
greatestSum = temp;
}
}
if(greatestSum == 0){ //如果子集合的值為0 (如果arr中都為負(fù)數(shù),選擇最大值返回)
greatestSum = arr[0]; //將arr數(shù)組的第一個(gè)值賦給 greatestSum
for(int i = 1;i < arr.length;i++){ //遍歷arr數(shù)組
if(greatestSum < arr[i]){ //將arr中最大值賦給greatestSum
greatestSum = arr[i];
}
}
}
return greatestSum;
}
}
6.2 數(shù)組元素的賦值
舉例1:楊輝三角(前面已講過(guò))
舉例2:使用簡(jiǎn)單數(shù)組
(1)創(chuàng)建一個(gè)名為ArrayTest的類悲龟,在main()方法中聲明array1和array2兩個(gè)變量屋讶,他們是int[]類型的數(shù)組冰寻。
(2)使用大括號(hào){}须教,把a(bǔ)rray1初始化為8個(gè)素?cái)?shù):2,3,5,7,11,13,17,19。
(3)顯示array1的內(nèi)容。
(4)賦值array2變量等于array1轻腺,修改array2中的偶索引元素乐疆,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1贬养。 array2 = array1;
思考:array1和array2是什么關(guān)系挤土?
array1 和 array2 是兩個(gè)變量,共同指向了堆空間中的同一個(gè)數(shù)組误算,則二者的地址值相同
拓展:修改題目仰美,實(shí)現(xiàn)array2對(duì)array1數(shù)組的復(fù)制
舉例3:一個(gè)數(shù)組,讓數(shù)組的每個(gè)元素去除第一個(gè)元素儿礼,得到的商作為被除數(shù)所在位置的新值咖杂。
public class Test3 {
public static void main(String[] args) {
int[] arr = new int[]{12,43,65,3,-8,64,2};
// for(int i = 0;i < arr.length;i++){ 沒有考慮第一個(gè)元素與第一個(gè)之間的結(jié)果為1,后面的值都是去除以1的蚊夫。
// arr[i] = arr[i] / arr[0];
// }
for(int i = arr.length -1;i >= 0;i--){ //逆序 或者 將第一個(gè)元素賦值給一個(gè)變量诉字,然后用數(shù)組的元素去除以變量
arr[i] = arr[i] / arr[0];
}
//遍歷arr
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
}
}
舉例4:創(chuàng)建一個(gè)長(zhǎng)度為6的int型數(shù)組,要求數(shù)組元素的值都在1-30之間知纷,且是隨機(jī)賦值壤圃。同時(shí),要求元素的值各不相同琅轧。
public class Test4 {
// 5-67 Math.random() * 63 + 5;
@Test
public void test1() {
int[] arr = new int[6];
for (int i = 0; i < arr.length; i++) {// [0,1) [0,30) [1,31)
arr[i] = (int) (Math.random() * 30) + 1;
boolean flag = false;
while (true) {
for (int j = 0; j < i; j++) {
if (arr[i] == arr[j]) {
flag = true;
break;
}
}
if (flag) {
arr[i] = (int) (Math.random() * 30) + 1;
flag = false;
continue;
}
break;
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
//更優(yōu)的方法
@Test
public void test2(){
int[] arr = new int[6];
for (int i = 0; i < arr.length; i++) {// [0,1) [0,30) [1,31)
arr[i] = (int) (Math.random() * 30) + 1;
for (int j = 0; j < i; j++) {
if (arr[i] == arr[j]) {
i--;
break;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
舉例5:回形數(shù)
從鍵盤輸入一個(gè)整數(shù)(1~20) 伍绳,則以該數(shù)字為矩陣的大小,把1,2,3…n*n 的數(shù)字按照順時(shí)針螺旋的形式填入其中乍桂。
例如: 輸入數(shù)字2墨叛,則程序輸出:
1 2
4 3
輸入數(shù)字3,則程序輸出:
1 2 3
8 9 4
7 6 5
輸入數(shù)字4模蜡, 則程序輸出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
//方式1
public class RectangleTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("輸入一個(gè)數(shù)字");
int len = scanner.nextInt();
int[][] arr = new int[len][len];
int s = len * len;
/*
* k = 1:向右
* k = 2:向下
* k = 3:向左
* k = 4:向上
*/
int k = 1;
int i = 0,j = 0;
for(int m = 1;m <= s;m++){
if(k == 1){
if(j < len && arr[i][j] == 0){
arr[i][j++] = m;
}else{
k = 2;
i++;
j--;
m--;
}
}else if(k == 2){
if(i < len && arr[i][j] == 0){
arr[i++][j] = m;
}else{
k = 3;
i--;
j--;
m--;
}
}else if(k == 3){
if(j >= 0 && arr[i][j] == 0){
arr[i][j--] = m;
}else{
k = 4;
i--;
j++;
m--;
}
}else if(k == 4){
if(i >= 0 && arr[i][j] == 0){
arr[i--][j] = m;
}else{
k = 1;
i++;
j++;
m--;
}
}
}
//遍歷
for(int m = 0;m < arr.length;m++){
for(int n = 0;n < arr[m].length;n++){
System.out.print(arr[m][n] + "\t");
}
System.out.println();
}
}
}
//方式2
/*
01 02 03 04 05 06 07
24 25 26 27 28 29 08
23 40 41 42 43 30 09
22 39 48 49 44 31 10
21 38 47 46 45 32 11
20 37 36 35 34 33 12
19 18 17 16 15 14 13
*/
public class RectangleTest1 {
public static void main(String[] args) {
int n = 7;
int[][] arr = new int[n][n];
int count = 0; //要顯示的數(shù)據(jù)
int maxX = n-1; //x軸的最大下標(biāo)
int maxY = n-1; //Y軸的最大下標(biāo)
int minX = 0; //x軸的最小下標(biāo)
int minY = 0; //Y軸的最小下標(biāo)
while(minX<=maxX) {
for(int x=minX;x<=maxX;x++) {
arr[minY][x] = ++count;
}
minY++;
for(int y=minY;y<=maxY;y++) {
arr[y][maxX] = ++count;
}
maxX--;
for(int x=maxX;x>=minX;x--) {
arr[maxY][x] = ++count;
}
maxY--;
for(int y=maxY;y>=minY;y--) {
arr[y][minX] = ++count;
}
minX++;
}
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr.length;j++) {
String space = (arr[i][j]+"").length()==1 ? "0":"";
System.out.print(space+arr[i][j]+" ");
}
System.out.println();
}
}
}
6.3 數(shù)組的元素查找
1漠趁、順序查找
順序查找:挨個(gè)查看
要求:對(duì)數(shù)組元素的順序沒要求
public class TestArrayOrderSearch {
//查找value第一次在數(shù)組中出現(xiàn)的index
public static void main(String[] args){
int[] arr = {4,5,6,1,9};
int value = 1;
int index = -1;
for(int i=0; i<arr.length; i++){
if(arr[i] == value){
index = i;
break;
}
}
if(index==-1){
System.out.println(value + "不存在");
}else{
System.out.println(value + "的下標(biāo)是" + index);
}
}
}
2、二分查找
//二分法查找:要求此數(shù)組必須是有序的忍疾。
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("未找打指定的元素");
}
6.4 數(shù)組元素的反轉(zhuǎn)
實(shí)現(xiàn)思想:數(shù)組對(duì)稱位置的元素互換。
public class TestArrayReverse1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println("反轉(zhuǎn)之前:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//反轉(zhuǎn)
/*
思路:首尾對(duì)應(yīng)位置的元素交換
(1)確定交換幾次
次數(shù) = 數(shù)組.length / 2
(2)誰(shuí)和誰(shuí)交換
for(int i=0; i<次數(shù); i++){
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
*/
for(int i=0; i<arr.length/2; i++){
int temp = arr[i];
arr[i] = arr[arr.length-1-i];
arr[arr.length-1-i] = temp;
}
System.out.println("反轉(zhuǎn)之后:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
或
public class TestArrayReverse2 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
System.out.println("反轉(zhuǎn)之前:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//反轉(zhuǎn)
//左右對(duì)稱位置交換
for(int left=0,right=arr.length-1; left<right; left++,right--){
//首 與 尾交換
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
System.out.println("反轉(zhuǎn)之后:");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
6.5 數(shù)組元素排序
6.5.1 算法概述
-
定義
- 排序:假設(shè)含有n個(gè)記錄的序列為{R1卤妒,R2甥绿,...,Rn},其相應(yīng)的關(guān)鍵字序列為{K1,K2则披,...,Kn}共缕。將這些記錄重新排序?yàn)閧Ri1,Ri2,...,Rin},使得相應(yīng)的關(guān)鍵字值滿足條Ki1<=Ki2<=...<=Kin,這樣的一種操作稱為排序。
- 通常來(lái)說(shuō)士复,排序的目的是快速查找图谷。
- 算法的5大特征:
說(shuō)明:滿足確定性的算法也稱為:確定性算法◆婊睿現(xiàn)在人們也關(guān)注更廣泛的概念,例如考慮各種非確定性的算法便贵,如并行算法菠镇、概率算法等。另外承璃,人們也關(guān)注并不要求終止的計(jì)算描述利耍,這種描述有時(shí)被稱為過(guò)程(procedure)。
-
衡量排序算法的優(yōu)劣:
-
時(shí)間復(fù)雜度
:分析關(guān)鍵字的比較次數(shù)和記錄的移動(dòng)次數(shù)- 常見的算法時(shí)間復(fù)雜度由小到大依次為:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
-
一個(gè)算法執(zhí)行所耗費(fèi)的時(shí)間盔粹,從理論上是不能算出來(lái)的隘梨,必須上機(jī)運(yùn)行測(cè)試才能知道。但我們不可能也沒有必要對(duì)每個(gè)算法都上機(jī)測(cè)試舷嗡,只需知道哪個(gè)算法花費(fèi)的時(shí)間多出嘹,哪個(gè)算法花費(fèi)的時(shí)間少就可以了。
并且一個(gè)算法花費(fèi)的時(shí)間與算法中語(yǔ)句的執(zhí)行次數(shù)成正比例咬崔,哪個(gè)算法中語(yǔ)句執(zhí)行次數(shù)多税稼,它花費(fèi)時(shí)間就多。一個(gè)算法中的語(yǔ)句執(zhí)行次數(shù)稱為語(yǔ)句頻度或時(shí)間頻度垮斯。記為T(n)郎仆。n稱為問題的規(guī)模,當(dāng)n不斷變化時(shí)兜蠕,時(shí)間頻度T(n)也會(huì)不斷變化扰肌。但有時(shí)我們想知道它變化時(shí)呈現(xiàn)什么規(guī)律。為此熊杨,我們引入時(shí)間復(fù)雜度概念曙旭。
一般情況下,算法中基本操作重復(fù)執(zhí)行的次數(shù)是問題規(guī)模n的某個(gè)函數(shù)晶府,用T(n)表示桂躏,若有某個(gè)輔助函數(shù)f(n),使得當(dāng)n趨近于無(wú)窮大時(shí),T(n)/f(n)的極限值為不等于零的常數(shù)川陆,則稱f(n)是T(n)的同數(shù)量級(jí)函數(shù)剂习。記作T(n)=O(f(n)),稱O(f(n)) 為算法的漸進(jìn)時(shí)間復(fù)雜度,簡(jiǎn)稱時(shí)間復(fù)雜度较沪。
-
空間復(fù)雜度
:分析排序算法中需要多少輔助內(nèi)存類似于時(shí)間復(fù)雜度的討論鳞绕,一個(gè)算法的空間復(fù)雜度(Space Complexity)S(n)定義為該算法所耗費(fèi)的存儲(chǔ)空間,它也是問題規(guī)模n的函數(shù)尸曼。
-
穩(wěn)定性
:若兩個(gè)記錄A和B的關(guān)鍵字值相等们何,但排序后A、B的先后次序保持不變控轿,則稱這種排序算法是穩(wěn)定的冤竹。
6.5.2 排序算法概述
-
排序算法分類:內(nèi)部排序和外部排序
-
內(nèi)部排序
:整個(gè)排序過(guò)程不需要借助于外部存儲(chǔ)器(如磁盤等)拂封,所有排序操作都在內(nèi)存中完成。 -
外部排序
:參與排序的數(shù)據(jù)非常多贴见,數(shù)據(jù)量非常大烘苹,計(jì)算機(jī)無(wú)法把整個(gè)排序過(guò)程放在內(nèi)存中完成躲株,必須借助于外部存儲(chǔ)器(如磁盤)片部。外部排序最常見的是多路歸并排序∷ǎ可以認(rèn)為外部排序是由多次內(nèi)部排序組成档悠。
-
十大內(nèi)部排序算法
數(shù)組的排序算法很多,實(shí)現(xiàn)方式各不相同望浩,時(shí)間復(fù)雜度辖所、空間復(fù)雜度、穩(wěn)定性也各不相同:
6.5.3 冒泡排序
介紹:
Java中的經(jīng)典算法之冒泡排序(Bubble Sort)磨德。冒泡排序的原理非常簡(jiǎn)單缘回,它重復(fù)地走訪過(guò)要排序的數(shù)列,一次比較兩個(gè)元素典挑,如果他們的順序錯(cuò)誤就把他們交換過(guò)來(lái)酥宴。
排序思想:
比較相鄰的元素。如果第一個(gè)比第二個(gè)大(升序)您觉,就交換他們兩個(gè)拙寡。
對(duì)每一對(duì)相鄰元素作同樣的工作,從開始第一對(duì)到結(jié)尾的最后一對(duì)琳水。這步做完后肆糕,最后的元素會(huì)是最大的數(shù)。
針對(duì)所有的元素重復(fù)以上的步驟在孝,除了最后一個(gè)诚啃。
持續(xù)每次對(duì)越來(lái)越少的元素重復(fù)上面的步驟,直到?jīng)]有任何一對(duì)數(shù)字需要比較為止私沮。
動(dòng)態(tài)演示:https://visualgo.net/zh/sorting
/*
1绍申、冒泡排序(最經(jīng)典)
思想:每一次比較“相鄰(位置相鄰)”元素,如果它們不符合目標(biāo)順序(例如:從小到大)顾彰,
就交換它們极阅,經(jīng)過(guò)多輪比較,最終實(shí)現(xiàn)排序涨享。
(例如:從小到大) 每一輪可以把最大的沉底筋搏,或最小的冒頂。
過(guò)程:arr{6,9,2,9,1} 目標(biāo):從小到大
第一輪:
第1次厕隧,arr[0]與arr[1]奔脐,6>9不成立俄周,滿足目標(biāo)要求,不交換
第2次髓迎,arr[1]與arr[2]峦朗,9>2成立,不滿足目標(biāo)要求排龄,交換arr[1]與arr[2] {6,2,9,9,1}
第3次波势,arr[2]與arr[3],9>9不成立橄维,滿足目標(biāo)要求尺铣,不交換
第4次,arr[3]與arr[4]争舞,9>1成立凛忿,不滿足目標(biāo)要求,交換arr[3]與arr[4] {6,2,9,1,9}
第一輪所有元素{6,9,2,9,1}已經(jīng)都參與了比較竞川,結(jié)束店溢。
第一輪的結(jié)果:第“一”最大值9沉底(本次是后面的9沉底),即到{6,2,9,1,9}元素的最右邊
第二輪:
第1次委乌,arr[0]與arr[1]床牧,6>2成立,不滿足目標(biāo)要求福澡,交換arr[0]與arr[1] {2,6,9,1,9}
第2次叠赦,arr[1]與arr[2],6>9不成立革砸,滿足目標(biāo)要求除秀,不交換
第3次:arr[2]與arr[3],9>1成立算利,不滿足目標(biāo)要求册踩,交換arr[2]與arr[3] {2,6,1,9,9}
第二輪未排序的所有元素 {6,2,9,1}已經(jīng)都參與了比較,結(jié)束效拭。
第二輪的結(jié)果:第“二”最大值9沉底(本次是前面的9沉底)暂吉,即到{2,6,1,9}元素的最右邊
第三輪:
第1次,arr[0]與arr[1]缎患,2>6不成立慕的,滿足目標(biāo)要求,不交換
第2次挤渔,arr[1]與arr[2]肮街,6>1成立,不滿足目標(biāo)要求判导,交換arr[1]與arr[2] {2,1,6,9,9}
第三輪未排序的所有元素{2,6,1}已經(jīng)都參與了比較嫉父,結(jié)束沛硅。
第三輪的結(jié)果:第三最大值6沉底,即到 {2,1,6}元素的最右邊
第四輪:
第1次绕辖,arr[0]與arr[1]摇肌,2>1成立,不滿足目標(biāo)要求仪际,交換arr[0]與arr[1] {1,2,6,9,9}
第四輪未排序的所有元素{2,1}已經(jīng)都參與了比較围小,結(jié)束。
第四輪的結(jié)果:第四最大值2沉底弟头,即到{1,2}元素的最右邊
*/
public class Test19BubbleSort{
public static void main(String[] args){
int[] arr = {6,9,2,9,1};
//目標(biāo):從小到大
//冒泡排序的輪數(shù) = 元素的總個(gè)數(shù) - 1
//輪數(shù)是多輪吩抓,每一輪比較的次數(shù)是多次涉茧,需要用到雙重循環(huán)赴恨,即循環(huán)嵌套
//外循環(huán)控制 輪數(shù),內(nèi)循環(huán)控制每一輪的比較次數(shù)和過(guò)程
for(int i=1; i<arr.length; i++){ //循環(huán)次數(shù)是arr.length-1次/輪
/*
假設(shè)arr.length=5
i=1,第1輪伴栓,比較4次
arr[0]與arr[1]
arr[1]與arr[2]
arr[2]與arr[3]
arr[3]與arr[4]
arr[j]與arr[j+1]伦连,int j=0;j<4; j++
i=2,第2輪,比較3次
arr[0]與arr[1]
arr[1]與arr[2]
arr[2]與arr[3]
arr[j]與arr[j+1]钳垮,int j=0;j<3; j++
i=3,第3輪惑淳,比較2次
arr[0]與arr[1]
arr[1]與arr[2]
arr[j]與arr[j+1],int j=0;j<2; j++
i=4,第4輪饺窿,比較1次
arr[0]與arr[1]
arr[j]與arr[j+1]歧焦,int j=0;j<1; j++
int j=0; j<arr.length-i; j++
*/
for(int j=0; j<arr.length-i; j++){
//希望的是arr[j] < arr[j+1]
if(arr[j] > arr[j+1]){
//交換arr[j]與arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//完成排序,遍歷結(jié)果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}
冒泡排序優(yōu)化
/*
思考:冒泡排序是否可以優(yōu)化
*/
class Test19BubbleSort2{
public static void main(String[] args){
int[] arr = {1,3,5,7,9};
//從小到大排序
//int lun = 0;//聲明lun變量肚医,統(tǒng)計(jì)比較幾輪
//int count = 0;//聲明count變量绢馍,統(tǒng)計(jì)比較的次數(shù)
for(int i=1; i<arr.length; i++){
//lun++;
boolean flag = true;//假設(shè)數(shù)組已經(jīng)是有序的
for(int j=0; j<arr.length-i; j++){
//count++;
//希望的是arr[j] < arr[j+1]
if(arr[j] > arr[j+1]){
//交換arr[j]與arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = false;//如果元素發(fā)生了交換,那么說(shuō)明數(shù)組還沒有排好序
}
}
if(flag){
break;
}
}
//System.out.println("一共比較了" + lun +"輪");
//System.out.println("一共比較了" + count +"次");
//完成排序肠套,遍歷結(jié)果
for(int i=0; i<arr.length; i++){
System.out.print(arr[i]+" ");
}
}
}
6.5.4 快速排序
介紹:快速排序通常明顯比同為O(nlogn)的其他算法更快舰涌,因此常被采用,而且快排采用了分治法的思想你稚,所以在很多筆試面試中能經(jīng)炒砂遥看到快排的影子〉罄担可見掌握快排的重要性搁痛。
快速排序(Quick Sort)由圖靈獎(jiǎng)獲得者Tony Hoare發(fā)明,被列為20世紀(jì)十大算法之一宇弛,是迄今為止所有內(nèi)排序算法中速度最快的一種鸡典。冒泡排序的升級(jí)版,交換排序的一種涯肩〗文疲快速排序的時(shí)間復(fù)雜度為O(nlog(n))巢钓。
排序思想:
從數(shù)列中挑出一個(gè)元素,稱為"基準(zhǔn)"(pivot)疗垛,
重新排序數(shù)列症汹,所有元素比基準(zhǔn)值小的擺放在基準(zhǔn)前面,所有元素比基準(zhǔn)值大的擺在基準(zhǔn)的后面(相同的數(shù)可以到任一邊)贷腕。在這個(gè)分區(qū)結(jié)束之后背镇,該基準(zhǔn)就處于數(shù)列的中間位置。這個(gè)稱為分區(qū)(partition)操作泽裳。
遞歸地(recursive)把小于基準(zhǔn)值元素的子數(shù)列和大于基準(zhǔn)值元素的子數(shù)列排序瞒斩。
遞歸的最底部情形,是數(shù)列的大小是零或一涮总,也就是永遠(yuǎn)都已經(jīng)被排序好了胸囱。雖然一直遞歸下去,但是這個(gè)算法總會(huì)結(jié)束瀑梗,因?yàn)樵诿看蔚牡╥teration)中烹笔,它至少會(huì)把一個(gè)元素?cái)[到它最后的位置去。
動(dòng)態(tài)演示:https://visualgo.net/zh/sorting
圖示1:
圖示2:
public class quickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
quickSort(arr,0,arr.length-1);
System.out.println("arr="+ Arrays.toString(arr));
}
public static void quickSort(int[] arr,int left,int right){
int l = left; //左下標(biāo)
int r = right; //右下標(biāo)
int temp = 0; //臨時(shí)變量抛丽,交換時(shí)使用
//pivot 中軸值
int pivot = arr[(left + right)/2];
//while 循環(huán)的目的谤职,是讓比pivot 值小的放到左邊
//比 pivot 值放到右邊
while(l < r){ //
//在pivot 的左邊一直找,找到大于等于 pivot 值亿鲜,才退出
while( arr[l] < pivot){
l += 1;
}
//在pivot 的右邊一直找允蜈,找到小于等于 pivot 值,才退出
while(arr[r] > pivot){
r -= 1;
}
//如果l >= r 說(shuō)明 pivot 的左右兩邊的值蒿柳,已經(jīng)按照
// 左邊全部是小于等于pivot 的值饶套,右邊全部大于等于pivot的值
if(l >= r){
break;
}
//交換
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//如果交換完后,發(fā)現(xiàn)這個(gè) arr[l] == pivot 值相等 r--其馏, 前移
if(arr[l] == pivot){
r -= 1;
}
//如果交換完后凤跑,發(fā)現(xiàn)這個(gè) arr[r] == pivot 值相等 l++, 后移
if(arr[l] == pivot){
l += 1;
}
}
//如果 l == r,必須 l++,r--,否則出現(xiàn)棧溢出
if(l == r){
l += 1;
r -= 1;
}
//向左遞歸
if(left < r){
quickSort(arr,left,r);
}
//向右遞歸
if(right > l){
quickSort(arr,l,right);
}
}
}
6.5.5 內(nèi)部排序性能比較與選擇
-
性能比較
- 從平均時(shí)間而言:快速排序最佳叛复。但在最壞情況下時(shí)間性能不如堆排序和歸并排序仔引。
- 從算法簡(jiǎn)單性看:由于直接選擇排序、直接插入排序和冒泡排序的算法比較簡(jiǎn)單褐奥,將其認(rèn)為是簡(jiǎn)單算法咖耘。對(duì)于Shell排序、堆排序撬码、快速排序和歸并排序算法儿倒,其算法比較復(fù)雜,認(rèn)為是復(fù)雜排序。
- 從穩(wěn)定性看:直接插入排序夫否、冒泡排序和歸并排序時(shí)穩(wěn)定的彻犁;而直接選擇排序、快速排序凰慈、 Shell排序和堆排序是不穩(wěn)定排序
- 從待排序的記錄數(shù)n的大小看汞幢,n較小時(shí),宜采用簡(jiǎn)單排序微谓;而n較大時(shí)宜采用改進(jìn)排序渊鞋。
-
選擇
- 若n較小(如n≤50)鼻种,可采用直接插入或直接選擇排序。
當(dāng)記錄規(guī)模較小時(shí)泉沾,直接插入排序較好尝丐;否則因?yàn)橹苯舆x擇移動(dòng)的記錄數(shù)少于直接插入歹茶,應(yīng)選直接選擇排序?yàn)橐恕?/li> - 若文件初始狀態(tài)基本有序(指正序)贫堰,則應(yīng)選用直接插入缕溉、冒泡或隨機(jī)的快速排序?yàn)橐耍?/li>
- 若n較大,則應(yīng)采用時(shí)間復(fù)雜度為O(nlgn)的排序方法:快速排序哼绑、堆排序或歸并排序岩馍。
- 若n較小(如n≤50)鼻种,可采用直接插入或直接選擇排序。
5. Arrays工具類的使用
java.util.Arrays類即為操作數(shù)組的工具類碉咆,包含了用來(lái)操作數(shù)組(比如排序和搜索)的各種方法抖韩。
舉例:java.util.Arrays類的sort()方法提供了數(shù)組元素排序功能:
import java.util.Arrays;
public class SortTest {
public static void main(String[] args) {
int [] numbers = {5,900,1,5,77,30,64,700};
Arrays.sort(numbers);
for(int i = 0; i < numbers.length; i++){
System.out.println(numbers[i]);
}
}
}
6. 數(shù)組中的常見異常
6.1 數(shù)組角標(biāo)越界異常
當(dāng)訪問數(shù)組元素時(shí),下標(biāo)指定超出[0, 數(shù)組名.length-1]的范圍時(shí)疫铜,就會(huì)報(bào)數(shù)組下標(biāo)越界異常:ArrayIndexOutOfBoundsException茂浮。
public class TestArrayIndexOutOfBoundsException {
public static void main(String[] args) {
int[] arr = {1,2,3};
// System.out.println("最后一個(gè)元素:" + arr[3]);//錯(cuò)誤,下標(biāo)越界
// System.out.println("最后一個(gè)元素:" + arr[arr.length]);//錯(cuò)誤壳咕,下標(biāo)越界
System.out.println("最后一個(gè)元素:" + arr[arr.length-1]);//對(duì)
}
}
創(chuàng)建數(shù)組席揽,賦值3個(gè)元素,數(shù)組的索引就是0谓厘,1幌羞,2,沒有3索引竟稳,因此我們不能訪問數(shù)組中不存在的索引属桦,程序運(yùn)行后,將會(huì)拋出 ArrayIndexOutOfBoundsException
數(shù)組越界異常他爸。在開發(fā)中聂宾,數(shù)組的越界異常是不能出現(xiàn)的,一旦出現(xiàn)了诊笤,就必須要修改我們編寫的代碼系谐。
6.2 空指針異常
觀察一下代碼,運(yùn)行后會(huì)出現(xiàn)什么結(jié)果讨跟。
public class TestNullPointerException {
public static void main(String[] args) {
//定義數(shù)組
int[][] arr = new int[3][];
System.out.println(arr[0][0]);//NullPointerException
}
}
因?yàn)榇藭r(shí)數(shù)組的每一行還未分配具體存儲(chǔ)元素的空間纪他,此時(shí)arr[0]是null鄙煤,此時(shí)訪問arr[0][0]會(huì)拋出NullPointerException
空指針異常。
空指針異常在內(nèi)存圖中的表現(xiàn)
小結(jié):空指針異常情況
//舉例一:
// int[] arr1 = new int[10];
// arr1 = null;
// System.out.println(arr1[9]);
//舉例二:
// int[][] arr2 = new int[5][];
// //arr2[3] = new int[10];
// System.out.println(arr2[3][3]);
//舉例三:
String[] arr3 = new String[10];
System.out.println(arr3[2].toString());