JavaScript資料結構和演算法簡述——陣列

發表於2015-09-15

為什麼先講陣列


資料結構可以簡單的被分為線性結構和非線性結構。

線性結構大致包括:

  1. 陣列(連續儲存);
  2. 連結串列(離散儲存);
  3. 棧(線性結構常見應用,由連結串列或陣列增刪和改進功能實現);
  4. 佇列(線性結構常見應用,由連結串列或陣列增刪和改進功能實現);

非線性結構大致包括:

  1. 樹;
  2. 圖;

其中,陣列是應用最廣泛的資料儲存結構。它被植入到大部分程式語言中。由於陣列十分容易懂,所以它被用來作為介紹資料結構的起點非常合適。

JavaScript陣列基礎知識


在ECMAScript中陣列是非常常用的引用型別了。ECMAScript所定義的陣列和其他語言中的陣列有著很大的區別。那麼首先要說的就是陣列在js中是一種特殊的物件。

特點:

  1. 陣列是一組資料的線性集合;
  2. js陣列更加類似java中的容器。長度可變,元素型別也可以不同;
  3. 陣列的長度可以隨時修改(length屬性);

常用操作方法:

  • push、pop
  • shift、unshift
  • splice、slice
  • concat、join、sort、reverse等

JavaScript陣列操作


一、 陣列方法:

1、 陣列的建立

注意:雖然第三種方法建立陣列指定了長度,但實際上所有情況下陣列都是變長的,也就是說即使指定了長度為5,仍然可以將元素儲存在規定長度以外的,並且這時長度會隨之改變。

2、 陣列元素的訪問

3、 陣列元素的新增

4、 陣列元素的刪除

5、 陣列的合併

6、 陣列的拷貝

7、 陣列元素的排序

8、 陣列元素的字串化

簡單介紹了下陣列各個方法的使用,也算是對js陣列學習的一個review和總結,利用這些方法可以實現陣列更復雜些的操作,具體大家可以自己去實踐。可見,js陣列的功能很強大。

二、 陣列屬性

1、 length屬性

length屬性表示陣列的長度,即其中元素的個數。因為陣列的索引總是由0開始,所以一個陣列的上下限分別是:0和length-1。和其他大多數語言不同的是,JavaScript陣列的length屬性是可變的,這一點需要特別注意。當length屬性被設定得更大時,整個陣列的狀態事實上不會發生變化,僅僅是length屬性變大;當length屬性被設定得比原來小時,則原先陣列中索引大於或等於length的元素的值全部被丟失。下面是演示改變length屬性的例子:

由上面的程式碼我們可以清楚的看到length屬性的性質。但length物件不僅可以顯式的設定,它也有可能被隱式修改。JavaScript中可以使用一個未宣告過的變數,同樣,也可以使用一個未定義的陣列元素(指索引超過或等於length的元素),這時,length屬性的值將被設定為所使用元素索引的值加1。例如下面的程式碼:

程式碼中同樣是先定義了一個包含10個數字的陣列,可以看出其長度為10。隨後使用了索引為15的元素,將其賦值為15,即 arr[15]=34,這時再輸出陣列的長度,得到的是16。無論如何,對於習慣於強型別程式設計的開發人員來說,這是一個很令人驚訝的特性。事實上,使用new Array()形式建立的陣列,其初始長度就是為0,正是對其中未定義元素的操作,才使陣列的長度發生變化。

綜上,利用length屬性可以方便的增加或者減少陣列的容量。

2、 prototype屬性

返回物件型別原型的引用。prototype 屬性是 object 共有的。

objectName.prototype

objectName 引數是object物件的名稱。

對於陣列物件,以下例子說明 prototype 屬性的用途。

給陣列物件新增返回陣列中最大元素值的方法。要完成這一點,宣告一個函式,將它加入 Array.prototype, 並使用它。

3、 constructor屬性

表示建立物件的函式。

object.constructor // object是物件或函式的名稱。

說明:constructor 屬性是所有具有 prototype 的物件的成員。constructor 屬性儲存了對構造特定物件例項的函式的引用。

JavaScript陣列演算法的C語言實現


使用沒有指標的語言,個人覺得無法將資料結構和演算法的精髓講的出來,而且js底層已將陣列相關演算法封裝好,所以這裡不使用原生的js或者java等,而是使用c語言來實現。為了照顧沒有學過指標的同學,我會盡可能的簡單實現,並寫好註釋,畫好圖解,大家可以體會一下。

執行結果:

程式圖解:

衡量演算法的標準


需要詳細瞭解的同學請閱讀相關書籍。這裡我簡單介紹一下。

1、 時間複雜度

程式大概要執行的次數,而非執行的時間

通常使用大O表示法(含義:”order of”大約是)來表示。比如無序陣列的插入,無論陣列中有多少資料項,都只需要在下一個有空的地方進行一步插入操作,那麼可以說向一個無序陣列中插入一個資料項的時間T是一個常數K: T=K;又比如線性查詢,查詢特定資料項所需的比較次數平均為資料項總數的一半,因此可以說:T=KN/2,為了得到更加簡潔的公式,可以將2併入K,可以得到:T=KN。大O表示法同上面的公式比較類似,但是它省略了常數K。當比較演算法時,並不在乎具體的處理器或者編譯器,真正需要比較的是對應不同的N值T是怎樣變化的,而不是具體的數字。

用大O表示法表示陣列相關演算法執行時間:

演算法 大O表示法
線性查詢 O(N)
二分查詢 O(logN)
無序陣列的插入 O(1)
有序陣列的插入 O(N)
無序陣列的刪除 O(N)
有序陣列的刪除 O(N)

注:O(1)是優秀;O(logN)是良好;O(N)還可以;O(N2)就差一些了。

2、 空間複雜度

演算法執行過程中大概所佔用的最大記憶體

3、 難易程度

寫出來的演算法不能只讓自己看得懂,或者自己寫完以後自己也看不懂了。。。

4、 健壯性

不能一用就崩潰。。。

為什麼不用陣列表示一切


僅用陣列看似可以完成所有的工作,那麼為什麼不用它來進行所有的資料儲存呢?

在一個無序陣列中可以很快進行插入(O(1)),但是查詢卻要花費較多的時間O(N)。在一個有序陣列中可以查詢的很快(O(logN)),但是插入卻要O(N)。對於有序和無序陣列,由於平均半數的資料項需要移動,所以刪除操作平均需要花費O(N)。

如果有一種資料結構進行任何插入、刪除和查詢操作都很快(O(1)或者O(logN)),那就太爽了哈。後面我們會向這一目標靠近。

相關文章