標籤:已耗用時間 擴容 for display find 為什麼 綜述 特殊 index
目錄
- 數組綜述
- Java中的數組
- 建立數組
- 訪問數組
- 初始化數組
- 有序數組
- 線性尋找
- 二分尋找
- 有序數組的優缺點
- 大O標記法(order of)
- 為什麼不用資料解決一切
- 小結數組綜述
數組是最廣泛的資料存放區結構,其中還有一種特殊數組(有序數組),下面講解下插入、查詢、刪除相關的注意事項
- 插入: 新資料總是插在數組第一個空位上,由於已有資料項目個數已知,所以空位的具體位置很容易得知,然而查詢和刪除卻沒有那麼快。當不允許插入重複值的模式下,還需要將演算法計算是否存在資料項目。
- 尋找: 若是無重複項模式,必須平均搜尋一半的資料項目來尋找特定資料項目。找數組頭部的資料項目快,找數組尾部的資料項目慢。若重複模式,必須從頭尋找到尾。
- 刪除: 只有找到特定資料項目後才能進行刪除操作(要麼該資料項目被替換,要麼變為空白)。刪除演算法中假設不允許有洞(一個或多個空單元後面存在非空資料項目),如果刪除演算法允許有洞,則其他演算法變得很複雜,因為要判斷非空降低效率。因此非空資料項目必須連續,不能有洞。在無重複項模式下尋找平均N/2個資料項目+移動剩下的N/2個資料項目來補洞。總共是N步。
- 重複值問題:
- 重複項模式下尋找演算法: 重複項使尋找演算法複雜,匹配上一個後,還得考慮是否繼續尋找可能的匹配,直到最後一個資料項目(所有藍眼睛的人和第一個藍眼睛的人的區別)。通常操作需要N步。
- 重複項模式下插入演算法: 和不重複項模式一樣,只需要一步。
- 重複項模式下刪除演算法:若是刪除第一個特定的資料項目,操作步驟平均是N/2次尋找+N/2移動,若是刪除所有特定的資料項目,需要檢查N個資料項目+移動多餘N/2個資料項目,操作的平均時間取決於重複項的個數和分布。
表格區別重複與無重複模式的區別
| 操作 |
不允許重複 |
允許重複 |
| 尋找 |
N/2次比較 |
N次比較 |
| 插入 |
無比較、一次移動 |
一次移動 |
| 刪除 |
N/2次比較、N/2次移動 |
N次比較、多於N/2次移動 |
Java中的數組建立數組
int[] intArray = new int[]{};
int[] intArray = new int[2];
訪問數組
數組資料項目通過下標訪問,下標從0開始,止於長度減1。若下標不在這個範圍訪問數組,則拋出Array Index Out of Bounds的執行階段錯誤。
初始化數組
int[] intArray = {1,2,3,4,5};
int[] intArray = new int[]{1,2,3,4,5};
程式碼範例
github 地址
public class HightArray { /** 聲明一個數組 */ private long[] a; /** 聲明變數記錄資料項目個數 */ private int nElemts; /** 構造方法用於初始化數組和資料項目總數 */ public HightArray(int max){ a = new long[max]; nElemts = 0; } public void insert(long value){ a[nElemts] = value; nElemts ++ ; } public boolean find(long searchKey){ int j; for (j=0;j<nElemts;j++){ if(a[j]==searchKey){ break; } } return j == nElemts; } public boolean delete(long value){ int j; for (j=0; j<nElemts; j++){ if(a[j]==value){ break; } } if(j == nElemts) { return false; } else { for (int k=j; k<nElemts; k++){ a[k] = a[k+1]; } nElemts--; return true; } } public void display(){ System.out.println(Arrays.stream(a).mapToObj(String::valueOf).collect(Collectors.joining(" "))); }}
測試類別>>>
public class HightArrayTest { @Test public void test(){ int maxSize = 100; HightArray hightArray = new HightArray(maxSize){{ insert(77); insert(99); insert(44); insert(55); insert(66); insert(22); insert(88); insert(11); insert(00); insert(33); }}; hightArray.display(); int searchKey = 35; System.out.println(hightArray.find(searchKey)?"Found ":"Can‘t find " + searchKey); hightArray.delete(00); hightArray.delete(55); hightArray.delete(99); hightArray.display(); }}
有序數組
數組中的資料項目按照關鍵字升序排列。當向此數組中插入資料項目時,需要為插入操作找到正確位置,在稍小位置和稍大位置之間,然後將稍大位置至末尾的資料項目往後移動一位騰出位置。
這中順序排列的好處就是可以通過二分尋找顯著提高查詢速度,但是降低了插入的速度,畢竟要騰出坑位。
線性尋找
線性尋找就是依次向後,尋找匹配。而在有序數組中當匹配到一個合適的資料項目就退出尋找。
二分尋找
二分尋找就是一半一半的找,這種查詢比線性查詢快很多,尤其對大數組來說。
有序數組程式碼範例
github 地址
public class OrderArray { private long[] a; private int nElems; public OrderArray(int maxSize){ a = new long[maxSize]; nElems = 0; } public int size(){ return nElems; } public void insert(long value){ int j ; for (j = 0; j < nElems; j++) { if(a[j] > value) { break; } } for(int k=nElems; k>j; k--){ a[k] = a[k-1]; } a[j] = value; nElems++; } public int find(long searchKey){ int lowerBound = 0, upperBound = nElems -1, curIndex; while (true){ if(lowerBound > upperBound){ return nElems; } curIndex = (lowerBound + upperBound)/2; if(a[curIndex] == searchKey) { return curIndex; } else if (a[curIndex] < searchKey){ lowerBound = curIndex + 1; } else { upperBound = curIndex - 1; } } } public boolean delete(long value){ int j = find(value); if(j == nElems) { return false; } for (int k = j; k < nElems; k++){ a[k] = a[k+1]; } nElems--; return true; } public void display(){ System.out.println(Arrays.stream(a).mapToObj(String::valueOf).collect(Collectors.joining(" "))); }}
測試類別>>>
public class OrderArrayTest { @Test public void test(){ int maxSize = 100; OrderArray orderArray = new OrderArray(maxSize){{ insert(77); insert(99); insert(44); insert(55); insert(22); insert(88); insert(11); insert(00); insert(66); insert(33); }}; int searchKey = 55; System.out.println(orderArray.find(searchKey)!=orderArray.size()?"Found":"Can‘t find" + searchKey); orderArray.display(); orderArray.delete(00); orderArray.delete(55); orderArray.delete(99); orderArray.display(); }}
有序數組的優缺點
- 優點: 二分尋找比無序資料快。
- 缺點: 插入需要尋找,尋找後需要將靠後的資料項目整體往後移動。刪除操作都需要補洞。
大O標記法(order of)
| 演算法 |
已耗用時間 |
| 線性尋找(從頭到尾) |
O(N) |
| 二分尋找(一半一半) |
O(LogN) |
| 無序數組插入(僅一次) |
O(1) |
| 有序數組插入(查詢+移動) |
O(N) |
| 無序數組刪除(查詢+補洞) |
O(N) |
| 有序數組刪除(查詢+補洞) |
O(N) |
為什麼不用資料解決一切
一個無序數組插入(O(1)時間),查詢卻花費(O(N)時間)。一個有序數組查詢花費(O(LogN)時間),但是插入缺花費(O(N)時間)。而刪除都需要花費(O(N)時間)。所以需要一種資料結構插入、查詢、刪除都很快,理論上(O(1)或者O(LogN)時間),而且數組一旦建立,其大小被固定,擴容不便,若空間大了又會浪費。
小結
- 無序數組可以快速插入,但查詢和刪除比較慢。
- 有序數組可以使用二分法尋找。
- 線性尋找需要的時間和資料項目個數成正比。
- 二分法尋找需要的時間和資料項目個數的對數成正比。
- O(1)演算法最好、O(LogN)次之、O(N)一般、O(N*N)最差。
Java資料結構和演算法( 二 ) ## 數組