數據結構與算法之美學習筆記:第五講

1、數組的基本概念

數組是一種線性數據結構,它用連續的內訓空間,來存儲一組局具備相同類型的數據java

一、線性表

第一線性表:顧名思義,線性表就是數據排成像一條線同樣的結構,每一個線性上的數據最多隻有先後兩個方向,其實除了數組、鏈表、隊列、棧等也是線性結構程序員

而與它相對立的概念是非線性表,好比二叉樹、堆、圖等,之因此叫非線性,是由於,在非線性表中、數據之間並非簡單的先後關係算法

二、連續的內存空間和相同類型的數據

第二個是連續的內存空間和相同類型的數據,正是由於這兩個限制,它纔有了一個堪稱「殺手鐗」的特性「隨機訪問」。但有利有弊,這兩個限制數組

也讓數組的不少操做變得很是低效,好比要在數組中刪除、插入一個數據,爲了保證連續性,就須要作大量的數據搬移工做。性能優化

2、如何實現隨機訪問

一、如何隨機訪問數組中的元素

說到數據的訪問,那你知道數組是如何實現根據下標隨機訪問數組元素的嗎?bash

 

咱們知道,計算機會給每一個內存單元分配一個地址,計算機經過地址來訪問內訓中的數據,但計算機須要隨機訪問數組中的某個元素時
它會首先經過下面的尋址公式,計算出該元素存儲的內存地址:網絡

a[i]_address = base_address + i * data_type_size


data_type_size表示數組中每一個元素的大小,咱們舉的這個例子裏,數組中存儲的是int類型數據,因此data_type_size就爲4個字節。數據結構

二、數組和鏈表的區別

不少人回答說:鏈表適合插入、刪除、時間複雜度O(1);數組適合查找,查找時間複雜度爲 O(1)」框架

實際上這種表述是不許確的,數組是適合查找操做,可是查找的時間複雜度並不爲O(1),即使是排好序的數組,你用二分查找,時間複雜度也是O(logn)。性能

因此,正確的表述應該是,數組支持隨機訪問,根據下標隨機訪問的時間複雜度爲 O(1)。

3、低效的插入和刪除

一、究竟爲何會致使低效?

插入:最好O(1) 最壞O(n) 平均O(n)
刪除:最好O(1) 最壞O(n) 平均O(n)

二、又有哪些改進方法呢?

數組若無序,插入新的元素時,能夠將第K個位置元素移動到數組末尾,把新的元素,插入到第k個位置,此處複雜度爲O(1)

           

實際上,在某些場景下,咱們並不必定非得追求數組中數據的連續性,若是咱們將屢次刪除操做集中在一塊兒執行,刪除的效率是否是是提升不少的呢?
屢次刪除集中在一塊兒,提升刪除效率

記錄下已經被刪除的數據,每次的刪除操做並非搬移數據,只是記錄數據已經被刪除,當數組沒有更多的存儲空間時,

再觸發一次真正的刪除操做。即JVM標記清除垃圾回收算法。

四. 警戒數組的訪問越界問題

int main(int argc, char* argv[]){
    int i = 0;
    int arr[3] = {0};
    for(; i<=3; i++){
        arr[i] = 0;
        printf("hello world\n");
    }
    return 0;
}

用C語言循環越界訪問的例子說明訪問越界的bug。此例在《C陷阱與缺陷》出現過,很慚愧,看過可是如今也只有一丟丟印象。翻了下書,替做者加上一句話:

若是用來編譯這段程序的編譯器按照內存地址遞減的方式給變量分配內存,那麼內存中的i將會被置爲0,則爲死循環永遠出不去。

五. 容器可否徹底替代數組

一、ArrayList相對於數組有什麼優點

相比於數字,java中的ArrayList封裝了數組的不少操做,並支持動態擴容。一旦超過存儲容量,擴容時比較耗內存,由於涉及到內存申請和數據搬移。

因此、若是事先能肯定須要存儲的 數據大小,最好在建立ArrayList 的時候事先指定數據大小

好比咱們要從數據中去吃醋10000條數據放入ArrayList。咱們看下面幾行代碼,你會發現,相比之下,

實現指定數據大小能夠省掉不少次內存申請和數據搬移操做

ArrayList<User> users = new ArrayList(10000);
for (int i = 0; i < 10000; ++i) {
  users.add(xxx);
}

二、數組適合的場景:

1) Java ArrayList 的使用涉及裝箱拆箱,有必定的性能損耗,若是特別管柱性能,能夠考慮數組
2) 若數據大小事先已知,而且涉及的數據操做很是簡單,可使用數組
3) 表示多維數組時,數組每每更加直觀。
4) 業務開發容器便可,底層開發,如網絡框架,性能優化。選擇數組。

六. 解答開篇問題

一、從性能方面

1) 從偏移角度理解a[0] 0爲偏移量,若是從1計數,會多出K-1。增長cpu負擔。爲何循環要寫成

for(int i = 0;i<3;i++) 

而不是

for(int i = 0 ;i<=2;i++)

第一個直接就能夠算出3-0 = 3 有三個數據,然後者 2-0+1個數據,多出1個加法運算,很惱火。

2) 也有必定的歷史緣由

一、C語言設計者用哪一個0開始計數數組下表,以後java、JavaScript 等高級語言都效仿了 C 語言

二、或者說,爲了在必定程度上減小 C 語言程序員學習 Java 的學習成本,所以繼續沿用了0開始計數的 習慣

三、實際上,不少語言中數組並非從0開始計數的,好比 Matlab。甚至還有一些語言支持負數下標,好比Python

7、課後思考題

前面我基於數組的原理引出 JVM 的標記清除垃圾回收算法的核心理念我不知道你是否使用 Java 語言,理解JVM
若是你熟悉,能夠在評論區回顧下你理解的標記清除垃圾回收算法。

前面咱們講到一維數組的內存尋址公式,那你能夠思考一下類比一下,二維數組的內存尋址公式是怎樣的呢?

相關文章
相關標籤/搜索