數組是應用最普遍的數據存儲結構。它被植入到大部分編程語言中。大部分數據結構都有最基本的四個操做:插入、刪除、查找、修改。對於這四種操做每一種數據結構都有相應的算法。算法和數據結構所以就是很是緊密的相聯繫的。算法
數組的每個元素必須是連續,也就是中間沒有洞。就是說第一個元素和第三個元素有值,但不容許第二個元素是空的。編程
1 package chapter2; 2 3 public class Array { 4 public static void main(String[] args){ 5 //建立一個數組 6 long[] arr; 7 arr = new long[100]; 8 int nElems = 0; 9 int j; 10 long searchKey; 11 arr[0] = 99; 12 arr[1] = 11; 13 arr[2] = 22; 14 arr[3] = 33; 15 arr[4] = 55; 16 arr[5] = 66; 17 arr[6] = 44; 18 arr[7] = 77; 19 arr[8] = 23; 20 arr[9] = 88; 21 nElems = 10; 22 //輸出前10位數組元素 23 for(j = 0; j < nElems; j++){ 24 System.out.print(arr[j] + " "); 25 } 26 System.out.println(); 27 //查找數組的一個元素值爲55的元素 28 searchKey = 55; 29 for(j = 0; j < arr.length; j++){ 30 if(arr[j] == searchKey){ 31 break; 32 } 33 } 34 if(j == nElems){ 35 System.out.println("Can't find the element"); 36 }else{ 37 System.out.println("find the element " + searchKey + " at index " + (j + 1) ); 38 } 39 //刪除指定元素 40 searchKey = 66; 41 for(j = 0; j < arr.length; j++){ 42 if(arr[j] == searchKey){ 43 break; 44 } 45 } 46 for(int k = j; k < nElems; k++){ 47 arr[k] = arr[k+1]; 48 } 49 nElems--; 50 //輸出刪除後的數組 51 for(j = 0; j < nElems; j++){ 52 System.out.print(arr[j] + " "); 53 } 54 } 55 } 56 57 輸出結果: 58 99 11 22 33 55 66 44 77 23 88 59 find the element 55 at index 5 60 99 11 22 33 55 44 77 23 88
上面的程序包含了一個很大的方法,經過將程序劃分紅類之後,而且將其中的方法模塊化,這樣程序看起來更加有條理。數組
1 package chapter2; 2 class ArrayMethod{ 3 private long[] arr; 4 private int nElems; 5 public ArrayMethod(int max){ 6 arr = new long[max]; 7 nElems = 0; 8 } 9 public void insert(long value){ 10 arr[nElems] = value; 11 nElems++; 12 } 13 public boolean find(long searchKey){ 14 //查找數組的一個元素值爲searchKey的元素 15 int j; 16 for(j = 0; j < arr.length; j++){ 17 if(arr[j] == searchKey){ 18 break; 19 } 20 } 21 if(j == nElems){ 22 return false; 23 }else{ 24 return true; 25 } 26 } 27 //刪除指定元素 28 public boolean delete(long searchKey){ 29 int j; 30 for(j = 0; j < arr.length; j++){ 31 if(arr[j] == searchKey){ 32 break; 33 } 34 } 35 for(int k = j; k < nElems; k++){ 36 arr[k] = arr[k+1]; 37 } 38 nElems--; 39 return true; 40 } 41 //輸出數組 42 public void display(){ 43 //輸出前10位數組元素 44 for(int j = 0; j < nElems; j++){ 45 System.out.print(arr[j] + " "); 46 } 47 System.out.println(); 48 } 49 } 50 class HighArray { 51 52 public static void main(String[] args){ 53 int maxSize = 100; 54 ArrayMethod arr = new ArrayMethod(maxSize); 55 arr.insert(99); 56 arr.insert(11); 57 arr.insert(22); 58 arr.insert(33); 59 arr.insert(55); 60 arr.insert(66); 61 arr.insert(44); 62 arr.insert(77); 63 arr.insert(23); 64 arr.insert(88); 65 //輸出數組 66 arr.display(); 67 //查找值爲55的元素 68 long searchKey = 55; 69 if(arr.find(searchKey)){ 70 System.out.println("find the element "); 71 }else{ 72 System.out.println("Can't find the element"); 73 } 74 //刪除指定元素 75 searchKey = 22; 76 if(arr.delete(searchKey)){ 77 System.out.println("Delete the element successfully "); 78 }else{ 79 System.out.println("Can't find the element"); 80 } 81 //輸出刪除元素後的數組 82 arr.display(); 83 } 84 }
假設一個數組,其中的數據項按關鍵字升序排列,即最小值在下標爲0的單元上,每個單元都比前一個單元的值大。這種數組被稱爲有序數組。數據結構
當向這種數組中插入數據項時,須要爲插入操做找到正確的位置:恰好在稍小值的後面,稍大值的前面。而後將全部比待茶數據項的值向後移以便騰出空間。
將數據按順序排列的好處之一就是能夠經過二分法查找顯著地提升查找速度。但缺點是下降了插入操做的速度。編程語言
默認狀況下是線性查找,線性查找和未經排序的數組的查找操做類似。模塊化
當使用二分查找時就體現有序數組的好處,這種查找比線性查找快不少,尤爲是對大數組來講更爲顯著。
二分查找首先從要查找的範圍肯定一箇中位數,而後比較要找的數和中位數的大小關係,肯定更小的範圍,依次遞歸,知道找到那個數。spa
將數目進行放大,能夠發現二分查找是極爲優秀的3d
二分查找是將數組數據項範圍不斷對半分隔來查找特定的數據項。方法以下所示:code
1 package chapter2; 2 class ArrayMethod{ 3 private long[] arr; 4 private int nElems; 5 public ArrayMethod(int max){ 6 arr = new long[max]; 7 nElems = 0; 8 } 9 public void insert(long value){ 10 arr[nElems] = value; 11 nElems++; 12 } 13 //二分查找法 14 public int halfFind (long searchKey){ 15 int lowerBound = 0; 16 int upperBound = nElems - 1; 17 int curIn; 18 while(true){ 19 curIn = (lowerBound + upperBound)/2; 20 if(arr[curIn] == searchKey){ 21 return curIn; 22 }else if(lowerBound >= upperBound){ 23 return nElems; 24 }else{ 25 if(arr[curIn] > searchKey){ 26 upperBound = curIn - 1; 27 }else{ 28 lowerBound = curIn + 1; 29 } 30 } 31 } 32 } 33 public int size(){ 34 return nElems; 35 } 36 public boolean find(long searchKey){ 37 //查找數組的一個元素值爲searchKey的元素 38 int j; 39 for(j = 0; j < arr.length; j++){ 40 if(arr[j] == searchKey){ 41 break; 42 } 43 } 44 if(j == nElems){ 45 return false; 46 }else{ 47 return true; 48 } 49 } 50 //刪除指定元素 51 public boolean delete(long searchKey){ 52 int j; 53 for(j = 0; j < arr.length; j++){ 54 if(arr[j] == searchKey){ 55 break; 56 } 57 } 58 for(int k = j; k < nElems; k++){ 59 arr[k] = arr[k+1]; 60 } 61 nElems--; 62 return true; 63 } 64 //輸出數組 65 public void display(){ 66 //輸出前10位數組元素 67 for(int j = 0; j < nElems; j++){ 68 System.out.print(arr[j] + " "); 69 } 70 System.out.println(); 71 } 72 } 73 class HighArray { 74 75 public static void main(String[] args){ 76 int maxSize = 100; 77 ArrayMethod arr = new ArrayMethod(maxSize); 78 arr.insert(99); 79 arr.insert(11); 80 arr.insert(22); 81 arr.insert(33); 82 arr.insert(55); 83 arr.insert(66); 84 arr.insert(44); 85 arr.insert(77); 86 arr.insert(23); 87 arr.insert(88); 88 //輸出數組 89 arr.display(); 90 //查找值爲55的元素 91 long searchKey = 35; 92 // if(arr.find(searchKey)){ 93 // System.out.println("find the element "); 94 // }else{ 95 // System.out.println("Can't find the element"); 96 // } 97 //二分法查找 98 if(arr.halfFind(searchKey) != arr.size()){ 99 System.out.println("Find it"); 100 }else{ 101 System.out.println("Can't find it"); 102 } 103 // //刪除指定元素 104 // searchKey = 22; 105 // if(arr.delete(searchKey)){ 106 // System.out.println("Delete the element successfully "); 107 // }else{ 108 // System.out.println("Can't find the element"); 109 // } 110 //輸出刪除元素後的數組 111 arr.display(); 112 } 113 }
使用有序數組最主要的好處是查找的速度比無序數組快多了。很差的方面是在插入操做中因爲全部靠後的數據都須要移動以騰開空間,因此速度較慢。有序數組和無序數組中的刪除操做都很慢,這是由於數據項必須向前移動來填補已刪除數據項的洞。有序數組在查找頻繁的狀況下十分有用,但如果插入和刪除較爲頻繁時,則沒法高效工做。blog
計算機科學中評價算法效率的方法稱爲大O表示法。比較算法時候一般會說"算法A比算法B快2倍",這種說法意義不大。由於數據項的變化會對排序形成必定很大影響。有可能數據項增長50%,算法A就比B快了3倍,或者可能只有一半的數據項,A和B的速度是相同的。
無序數組的插入是咱們到如今爲止所見過的算法中惟一一個與數組項個數無關的算法。新數據項總被放在下一個有空的地方,a[nElems],而後nElems增長。不管數組中的數據項個數N有多大,一次插入老是用相同的時間。咱們能夠說向一個無序數組中插入一個數據項的時間T是一個常數K
T=K
在現實狀況中,插入所需的實際時間與如下這些因素有關:微處理器,編譯程序生成程序代碼的效率,等等。
在數組數據項的線性查找中,咱們已經發現尋找特定數據項所需的比較平均次數爲數據項總數的一半。所以設N爲數據項總數,搜索時間T與N的一半成正比:
T=K*N/2
同插入同樣,若要獲得方程中K的值,首先須要對某個N值的查找進行計時,而後用T來計算K。當獲得K後,即可以對任意N的值來計算T。將2併入K能夠獲得更方便的公式,新K值等於原先的K除以2即
T=K*N
這個方程說明平均線性查找時間與數組的大小成正比。即若是一個數組增大兩倍,則所花費的查找時間也會相應地增加兩倍。
一樣,咱們能夠爲二分查找指定出一個與T和N有關的公式:T=K*log2(N)
實際上,因爲全部的對數和其餘對數成比例(好比從底數2轉換到底數爲10需乘以3.322),也能夠將這個爲常數的底數也併入K。由此沒必要指定底數:
T=K*log(N)
不要常數
大O表示法同上面的公式比較相似,但它省去了常數K。當比較算法時,並不在意具體的微處理器或編譯器;真正須要比較的是對應不一樣的N值,T是如何變化的,而不是具體的數字,所以不須要常數。
大O表示法使用大寫字母O,能夠認爲其含義是order of(大約是)。咱們可使用大O表示法來描述線性查找使用了O(N)級時間,二分查找使用了O(logN)級時間。向一個無序數組中的插入使用了O(1),或常數級時間。
下表總結的是討論過的算法的運行時間
大O表示法的實質並非對運行時間給出實際值,而是表達了運行時間是如何受數據項個數所影響的。除了實際安裝後真正去測量一次算法的運行時間以外,這多是對算法進行比較的最有意義的方法了。
僅使用數組彷佛就能夠完成全部工做,爲何不用它們來進行全部數據存儲呢?咱們已經見到了許多關於數組的缺點。在一個無序數組中能夠很快進行插入(O(1)),可是查找卻要花費較慢的O(N)時間。在一個有序數組中能夠查找得很快,用O(logN)的時間,但插入卻花費了O(N)時間。對於這兩種數組而言,因爲平均半數的數據項爲了填補"空洞"必須移動,因此刪除操做平均須要O(N)時間。 若是有一種數據結構進行任何如插入、刪除和查找的操做都很快(理想的O(1)或是O(logN)時間),那就行了。後面咱們會介紹相似的數組,但這是以程序的複雜度爲代價的。 另外數組被建立後,佔用內存長度是肯定的,沒法進行修改,這樣的話,若是建立的長度過大,可是實際須要的不多就會形成內存浪費,若是建立的長度太小,就會形成溢出,沒法彈性使用。