1. 程式人生 > >資料結構與演算法筆記(三)陣列

資料結構與演算法筆記(三)陣列

3.陣列

陣列(Array)是一種線性表資料結構。它是一組連續的記憶體空間,來儲存一組具有相同型別的資料。

3.1 特性

  • 線性表

    資料排成像一條線的結構,如陣列、連結串列、佇列、棧等。

    與之相對立的是非線性,如二叉樹、堆、圖等,其資料之間並不是簡單的前後關係。

  • 連續的記憶體空間和相同型別的資料

    • 可以實現隨機訪問

      陣列支援隨即訪問,通過定址公式,計算該元素儲存的記憶體地址

      a[i]_address = base_address + i * data_type_size

      根據下標隨即訪問的時間複雜度為 O

      ( 1 ) O(1)

    • 低效的插入和刪除

      為保證陣列的連續性,就要做大量的資料搬移工作。

      插入
      若在第k個位置插入資料,則k-n位置的資料需往後移。

      • 最好情況時間複雜度: O
        ( 1 ) O(1)
        (末尾插入)
      • 最壞情況時間複雜度: O
        ( n ) O(n)
      • 和平均情況時間複雜度: O ( n ) O(n)

      改進:如果陣列中資料是無序的,也就是無規律的情況下,在插入元素時,可以直接將第k位的資料搬移到陣列元素的最後,把新的元素直接放入第k個位置。這樣時間複雜度就會降為 O ( 1 ) O(1)

      刪除
      和插入類似。

      • 最好情況時間複雜度: O ( 1 ) O(1) (刪除末尾資料)
      • 最壞情況時間複雜度: O ( n ) O(n)
      • 平均情況時間複雜度: O ( n ) O(n)

      改進:在某些特殊場景下,不追求陣列中資料的連續性時,可以將多次刪除操作集中在一起執行,減少資料搬移次數。先記錄下已經刪除的資料,但不進行資料遷移,僅僅是記錄,當陣列沒有更多空間儲存資料時,才觸發執行一次真正的刪除。這也是JVM標記清除垃圾回收演算法的核心思想。

3.2 陣列訪問越界問題

要警惕陣列的訪問越界問題,例如在C語言中的陣列越界是一種未決行文,迴圈越界訪問會導致死迴圈bug。

3.3 用陣列還是容器

陣列需要預先指定了空間大小,容器如ArrayList可以將很多陣列操作的細節封裝起來,支援動態擴容。

  • 希望儲存基本型別資料,可以用陣列
  • 事先已知資料大小,並且對資料操作簡單,用不到ArrayList提供的大部分方法,可以用陣列
  • 表示多維陣列時,可以用陣列
  • 業務開發,使用容器足夠;開發框架,追求效能,首先陣列

3.4 為什麼陣列要從0開始編號

從陣列儲存的記憶體模型,下標最確切的定義是偏移,如果a來表示陣列的首地址,a[0]就是偏移為0的位置,a[k]表示偏移k個type_size的位置,所以a[k]的記憶體地址計算如下:
a[i]_address = base_address + i * data_type_size
如果陣列是從 1 開始計數,那麼就會變成:
a[i]_address = base_address + (i-1)* data_type_size

對於CPU來說,多了一次減法的指令。
當然,還有一定的歷史原因。