Java数据结构和算法(二):数组
上篇博客我們簡單介紹了數(shù)據(jù)結構和算法的概念,對此模糊很正常,后面會慢慢通過具體的實例來介紹。本篇博客我們介紹數(shù)據(jù)結構的鼻祖——數(shù)組,可以說數(shù)組幾乎能表示一切的數(shù)據(jù)結構,在每一門編程語言中,數(shù)組都是重要的數(shù)據(jù)結構,當然每種語言對數(shù)組的實現(xiàn)和處理也不相同,但是本質(zhì)是都是用來存放數(shù)據(jù)的的結構,這里我們以Java語言為例,來詳細介紹Java語言中數(shù)組的用法。
1、Java數(shù)組介紹
在Java中,數(shù)組是用來存放同一種數(shù)據(jù)類型的集合,注意只能存放同一種數(shù)據(jù)類型(Object類型數(shù)組除外)。
①、數(shù)組的聲明
第一種方式:
| 1 | 數(shù)據(jù)類型 []? 數(shù)組名稱 =?new?數(shù)據(jù)類型[數(shù)組長度]; |
這里 [] 可以放在數(shù)組名稱的前面,也可以放在數(shù)組名稱的后面,我們推薦放在數(shù)組名稱的前面,這樣看上去 數(shù)據(jù)類型 [] 表示的很明顯是一個數(shù)組類型,而放在數(shù)組名稱后面,則不是那么直觀。
第二種方式:
| 1 | 數(shù)據(jù)類型 [] 數(shù)組名稱 = {數(shù)組元素1,數(shù)組元素2,......} |
這種方式聲明數(shù)組的同時直接給定了數(shù)組的元素,數(shù)組的大小由給定的數(shù)組元素個數(shù)決定。
| 1 2 3 4 | //聲明數(shù)組1,聲明一個長度為3,只能存放int類型的數(shù)據(jù) int?[] myArray =?new?int[3]; //聲明數(shù)組2,聲明一個數(shù)組元素為 1,2,3的int類型數(shù)組 int?[] myArray2 = {1,2,3}; |
②、訪問數(shù)組元素以及給數(shù)組元素賦值
數(shù)組是存在下標索引的,通過下標可以獲取指定位置的元素,數(shù)組小標是從0開始的,也就是說下標0對應的就是數(shù)組中第1個元素,可以很方便的對數(shù)組中的元素進行存取操作。
前面數(shù)組的聲明第二種方式,我們在聲明數(shù)組的同時,也進行了初始化賦值。
| 1 2 3 4 5 6 | //聲明數(shù)組,聲明一個長度為3,只能存放int類型的數(shù)據(jù) int?[] myArray =?new?int[3]; //給myArray第一個元素賦值1 myArray[0] =?1; //訪問myArray的第一個元素 System.out.println(myArray[0]); |
上面的myArray 數(shù)組,我們只能賦值三個元素,也就是下標從0到2,如果你訪問 myArray[3] ,那么會報數(shù)組下標越界異常。
③、數(shù)組遍歷
數(shù)組有個 length 屬性,是記錄數(shù)組的長度的,我們可以利用length屬性來遍歷數(shù)組。
| 1 2 3 4 5 | //聲明數(shù)組2,聲明一個數(shù)組元素為 1,2,3的int類型數(shù)組 int?[] myArray2 = {1,2,3}; for(int?i =?0?; i < myArray2.length ; i++){ ????System.out.println(myArray2[i]); } |
2、用類封裝數(shù)組實現(xiàn)數(shù)據(jù)結構
上一篇博客我們介紹了一個數(shù)據(jù)結構必須具有以下基本功能:
①、如何插入一條新的數(shù)據(jù)項
②、如何尋找某一特定的數(shù)據(jù)項
③、如何刪除某一特定的數(shù)據(jù)項
④、如何迭代的訪問各個數(shù)據(jù)項,以便進行顯示或其他操作
而我們知道了數(shù)組的簡單用法,現(xiàn)在用類的思想封裝一個數(shù)組,實現(xiàn)上面的四個基本功能:
ps:假設操作人是不會添加重復元素的,這里沒有考慮重復元素,如果添加重復元素了,后面的查找,刪除,修改等操作只會對第一次出現(xiàn)的元素有效。
1 package com.ys.array;2 3 public class MyArray {4 //定義一個數(shù)組5 private int [] intArray;6 //定義數(shù)組的實際有效長度7 private int elems;8 //定義數(shù)組的最大長度9 private int length;10 11 //默認構造一個長度為50的數(shù)組12 public MyArray(){13 elems = 0;14 length = 50;15 intArray = new int[length];16 }17 //構造函數(shù),初始化一個長度為length 的數(shù)組18 public MyArray(int length){19 elems = 0;20 this.length = length;21 intArray = new int[length];22 }23 24 //獲取數(shù)組的有效長度25 public int getSize(){26 return elems;27 }28 29 /**30 * 遍歷顯示元素31 */32 public void display(){33 for(int i = 0 ; i < elems ; i++){34 System.out.print(intArray[i]+" ");35 }36 System.out.println();37 }38 39 /**40 * 添加元素41 * @param value,假設操作人是不會添加重復元素的,如果有重復元素對于后面的操作都會有影響。42 * @return添加成功返回true,添加的元素超過范圍了返回false43 */44 public boolean add(int value){45 if(elems == length){46 return false;47 }else{48 intArray[elems] = value;49 elems++;50 }51 return true;52 }53 54 /**55 * 根據(jù)下標獲取元素56 * @param i57 * @return查找下標值在數(shù)組下標有效范圍內(nèi),返回下標所表示的元素58 * 查找下標超出數(shù)組下標有效值,提示訪問下標越界59 */60 public int get(int i){61 if(i<0 || i>elems){62 System.out.println("訪問下標越界");63 }64 return intArray[i];65 }66 /**67 * 查找元素68 * @param searchValue69 * @return查找的元素如果存在則返回下標值,如果不存在,返回 -170 */71 public int find(int searchValue){72 int i ;73 for(i = 0 ; i < elems ;i++){74 if(intArray[i] == searchValue){75 break;76 }77 }78 if(i == elems){79 return -1;80 }81 return i;82 }83 /**84 * 刪除元素85 * @param value86 * @return如果要刪除的值不存在,直接返回 false;否則返回true,刪除成功87 */88 public boolean delete(int value){89 int k = find(value);90 if(k == -1){91 return false;92 }else{93 if(k == elems-1){94 elems--;95 }else{96 for(int i = k; i< elems-1 ; i++){97 intArray[i] = intArray[i+1];98 99 } 100 elems--; 101 } 102 return true; 103 } 104 } 105 /** 106 * 修改數(shù)據(jù) 107 * @param oldValue原值 108 * @param newValue新值 109 * @return修改成功返回true,修改失敗返回false 110 */ 111 public boolean modify(int oldValue,int newValue){ 112 int i = find(oldValue); 113 if(i == -1){ 114 System.out.println("需要修改的數(shù)據(jù)不存在"); 115 return false; 116 }else{ 117 intArray[i] = newValue; 118 return true; 119 } 120 } 121 122 }測試:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package?com.ys.test; import?com.ys.array.MyArray; public?class?MyArrayTest { ????public?static?void?main(String[] args) { ????????//創(chuàng)建自定義封裝數(shù)組結構,數(shù)組大小為4 ????????MyArray array =?new?MyArray(4); ????????//添加4個元素分別是1,2,3,4 ????????array.add(1); ????????array.add(2); ????????array.add(3); ????????array.add(4); ????????//顯示數(shù)組元素 ????????array.display(); ????????//根據(jù)下標為0的元素 ????????int?i = array.get(0); ????????System.out.println(i); ????????//刪除4的元素 ????????array.delete(4); ????????//將元素3修改為33 ????????array.modify(3,?33); ????????array.display(); ????} } |
打印結果為:
?3、分析數(shù)組的局限性
通過上面的代碼,我們發(fā)現(xiàn)數(shù)組是能完成一個數(shù)據(jù)結構所有的功能的,而且實現(xiàn)起來也不難,那數(shù)據(jù)既然能完成所有的工作,我們實際應用中為啥不用它來進行所有的數(shù)據(jù)存儲呢?那肯定是有原因呢。
數(shù)組的局限性分析:
①、插入快,對于無序數(shù)組,上面我們實現(xiàn)的數(shù)組就是無序的,即元素沒有按照從大到小或者某個特定的順序排列,只是按照插入的順序排列。無序數(shù)組增加一個元素很簡單,只需要在數(shù)組末尾添加元素即可,但是有序數(shù)組卻不一定了,它需要在指定的位置插入。
②、查找慢,當然如果根據(jù)下標來查找是很快的。但是通常我們都是根據(jù)元素值來查找,給定一個元素值,對于無序數(shù)組,我們需要從數(shù)組第一個元素開始遍歷,直到找到那個元素。有序數(shù)組通過特定的算法查找的速度會比無需數(shù)組快,后面我們會講各種排序算法。
③、刪除慢,根據(jù)元素值刪除,我們要先找到該元素所處的位置,然后將元素后面的值整體向前面移動一個位置。也需要比較多的時間。
④、數(shù)組一旦創(chuàng)建后,大小就固定了,不能動態(tài)擴展數(shù)組的元素個數(shù)。如果初始化你給一個很大的數(shù)組大小,那會白白浪費內(nèi)存空間,如果給小了,后面數(shù)據(jù)個數(shù)增加了又添加不進去了。
很顯然,數(shù)組雖然插入快,但是查找和刪除都比較慢,而且擴展性差,所以我們一般不會用數(shù)組來存儲數(shù)據(jù),那有沒有什么數(shù)據(jù)結構插入、查找、刪除都很快,而且還能動態(tài)擴展存儲個數(shù)大小呢,答案是有的,但是這是建立在很復雜的算法基礎上,后面我們也會詳細講解。
4、分析數(shù)組在內(nèi)存中的存儲
我們知道Java里當一個對象使用關鍵字“new”創(chuàng)建時,會在堆上分配內(nèi)存空間,然后返回對象的引用,這對數(shù)組來說也是一樣的,因為數(shù)組也是一個對象。
新建一個數(shù)組
int[] arr = new int[4];上面這段代碼可以看成兩部分,一部分是數(shù)組引用,即在代碼中定義的數(shù)組引用變量;另一部分是實際的數(shù)組對象,這部分是在堆內(nèi)存里運行的,通常無法直接訪問,只能通過數(shù)組引用變量來訪問。
棧內(nèi)存中存儲所定義并初始化的引用變量arr,堆內(nèi)存中存儲arr指向的4個數(shù)組arr[0],arr[1],arr[2],arr[3]
?5、擴展
1)滑動窗口練習題
有一個整型數(shù)組 arr 和一個大小為 w 的窗口從數(shù)組的最左邊滑到最右邊,窗口每次向右邊滑一個位置。 返回一個長度為n-w+1的數(shù)組res,res[i]表示每一種窗口狀態(tài)下的最大值。 以數(shù)組為[4,3,5,4,3,3,6,7],w=3為例。因為第一個窗口[4,3,5]的最大值為5,第二個窗口[3,5,4]的最大值為5,第三個窗口[5,4,3]的最大值為5。第四個窗口[4,3,3]的最大值為4。第五個窗口[3,3,6]的最大值為6。第六個窗口[3,6,7]的最大值為7。所以最終返回[5,5,5,4,6,7]。
給定整形數(shù)組arr及它的大小n,同時給定w,請返回res數(shù)組。保證w小于等于n,同時保證數(shù)組大小小于等于500。
測試樣例: [4,3,5,4,3,3,6,7],8,3 返回:[5,5,5,4,6,7] import java.util.*; public class SlideWindow {public int[] slide(int[] arr, int n, int w) {//result數(shù)組中保存每個窗口狀態(tài)下的最大值int[] result = new int[n-w+1];//記錄雙端隊列隊頭的下標 ,隊尾下標int[] qmax = new int[n];int front = 0, back = 0;//j 標記是否達到窗口大小,同時記錄result中下一個應該放入的元素的下標int j = 0;for(int i=0; i<n; i++){while(front < back && arr[qmax[back-1]] < arr[i])//back為當前要往qmax中放入的值back--;qmax[back++] = i;if(j+w-1 == i){//達到窗口長度result[j] = arr[qmax[front]];j++;}if(qmax[front]+w-1 == i){//隊頭過期front++;}} return result;} }2)數(shù)組變樹練習題
對于一個沒有重復元素的整數(shù)數(shù)組,請用其中元素構造一棵MaxTree,MaxTree定義為一棵二叉樹,其中的節(jié)點與數(shù)組元素一一對應,同時對于MaxTree的每棵子樹,它的根的元素值為子樹的最大值。現(xiàn)有一建樹方法,對于數(shù)組中的每個元素,其在樹中的父親為數(shù)組中它左邊比它大的第一個數(shù)和右邊比它大的第一個數(shù)中更小的一個。若兩邊都不存在比它大的數(shù),那么它就是樹根。請設計O(n)的算法實現(xiàn)這個方法。
給定一個無重復元素的數(shù)組A和它的大小n,請返回一個數(shù)組,其中每個元素為原數(shù)組中對應位置元素在樹中的父親節(jié)點的編號,若為根則值為-1。
測試樣例:
[3,1,4,2],4
返回:[2,0,-1,2]
思路:要求時間復雜度為O(n),題目中已經(jīng)有提示了,其實和最大最小堆有一點相似,對于大頂堆,我們常用樹的方式理解,但是實際上可以用線性表存儲(即數(shù)組),因此它需要是一棵完全二叉樹(中間不能缺節(jié)點)。本題中的MaxTree只要求是二叉樹就行了~,限制少一些,所以構建的時間復雜度也比建立大頂堆低一些。(大根堆構建初堆的時間復雜度也是O(n)) 利用棧可以快速找出左邊和右邊第一個比它大的值。
總結
以上是生活随笔為你收集整理的Java数据结构和算法(二):数组的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeadTools Android 入门
- 下一篇: DWR框架小例子