數據結構與算法概念

數據結構小白入門

數據結構指一組相互之間存在一種或多種特定關係的數據元素的集合,
當咱們須要在計算機中存儲這些數據時,還涉及到數據的,組織方式,在計算機中的存儲方式,以及定義在該數據上的一組操做;算法

  • 一組數據相互之間有某種關係
  • 組織方式
  • 存儲方式
  • 以及可對其進行的一組操做
理解:

咱們學習的最終目的是要在計算機中存儲一組數據,可是不得不先考慮數據的組織方式,在計算機中的存儲方式,以及能夠對這些數據進行的一組操做,固然了既然是一組數據必然代表了這寫數據之間是存在想換的關聯關係的;關係可能還會有多種;數據結構

例如:

一組數據:12345函數

組織方式:從小到大學習

存儲方式:可以使用線性存儲結構設計

操做:要取出最大的一個指針

數據結構研究方向

問題:code

  • 機外處理
  • 處理要求

建模:索引

  • 邏輯結構
  • 基本運算

實現:內存

  • 存儲結構
  • 算法

基本術語

數據(Data):

​ 全部能被計算機處理的符號的集合element

數據元素(DataElement):

​ 是數據集合中的一個 個體,即數據的基本單位

數據項(DataItem):

​ 數據元素經常可分爲若干個數據項,數據項是數據具備意義的最小單位

組織數據的三個層次:

數據(表)->數據元素(行)->數據項(字段)

實際問題中的數據成爲原始數據

邏輯結構(LogicalStructure)

​ 數據元素之間的結構關係,如從小到大/一對一/一對多

物理結構(PhysicalStructure)

​ 也會叫作存儲結構,指數據在計算機內的表示,邏輯結構在計算機中的具體實現

邏輯結構

常見的邏輯結構以下:

集合:

數據元素屬於同一個集合,表示爲R{}; 數據之間不存在特定關係

組織結構鬆散,任意兩節點之間都沒有鄰接關係

線性:

除了起始節點d1和終端階段dn外,每一個節點都有一個前驅和一個後繼,表示爲R={d1,d2...dn},數據之間存在先後順序關係

各節點按邏輯關係排列,造成一條'鏈'

樹狀:

每一個元素最多有一個前驅,能夠有多個後繼,表示爲(D,{R}),就像一個樹幹長了多個樹枝

具有分支,層次特性,上層節點能夠和下層度哦哦哦個節點相鄰接,可是下層節點只能和一個上層節點鄰接

圖狀:

任何兩個元素之間均可以相鄰接,表示爲(D,{R})

注意:

邏輯結構

  • 與元素自己的形式,內容,無關

  • 元素的相對位置,無關

  • 與包含的節點個數,無關

存儲結構

存儲結構由 存儲節點(每一個存儲節點存放一個數據元素)節點之間的邏輯關係共同組成

反過來講,一個完整的存儲結構必須能夠存儲數據元素,以及元素之間的邏輯關係

存儲結構分類 (缺圖)

  • 順序存儲

    使用索引(相對起始位置)來表示數據的邏輯結構,數據被存儲在一組連續的存儲單元中

    特色:

    • 需預先分配長度,
    • 插入和刪除慢,須要移動其餘元素
    • 存取數據快捷,屬於隨機存儲結構(可經過索引直接訪問任意位置數據)
  • 鏈式存儲

    藉助元素地址指針表示數據的邏輯結構,每一個元素都會包含指向下一個元素的指針

    這種結構須要在節點上附加一個指針項,指出後繼節點的位置,即每一個節點存儲單元包含兩個部分:[數據項,指針項]

    特色:

    • 動態分配內容,不須要預先分配內存
    • 插入刪除快捷,不須要移動其餘元素
    • 非隨機存取結構(獲取數據必須遍歷前面的全部節點)
  • 索引存儲(Map是否屬於索引結構 很疑惑?)

    藉助索引表來指示數據元素的存儲位置

    索引表中包含了全部數據元素的地址,查詢索引表可以快速的定位到須要的數據

    特色:

    • 索引是一份獨立於實際存放數據,的數據結構(就像書的目錄都在正文前面)
    • 索引須要佔用額外的存儲空間
    • 當實際數據發生改變時須要重建索引
    • 查詢數據快
    • 插入修改,刪除慢
  • 散列存儲(哈希表)

    經過散列函數計算得出元素的位置

    特色:

    • 在散列函數不變時,相同數據會得出相同的位置
    • 存入順序和取出順序一般不一致
    • 沒法完成隨機存取(指定獲取某個元素)

順序和鏈式是最基本的也是最經常使用的存儲結構,須要重點掌握,包括各自的優缺點,使用場景等

鏈式存儲結構可實現樹結構(邏輯結構)

運算

運算指的是某種邏輯結構上能夠進行的操做;

運算分爲兩類:

  • 加工型運算

    會改變原邏輯結構的內容,順序,個數等的操做

  • 引用型運行

    與加工型運算相反

常見運算:

創建,查找,讀取,插入,刪除

加工型:創建,插入,刪除

引用型:讀取,查找

算法

算法字面意思,計算方法;

算法規定了求解給定類型問題所需的全部處理步驟以及執行順序,使得問題能在優先時間內機械的求解,一個算法就是對特定問題求解步驟的一種描述,再具體一點,算法是一段有窮的指令序列;算法必須能使用某種語言描述;

例如:

計算1到5的和 ,這個需求,如何來實現,第一步作什麼,第二步作什麼,整個計算步驟和執行順序統稱爲算法,若是最終可以在有限的步驟下求出正確的和,那這就是一個合格的算法;

算法的特色:

  • 有窮性

    算法必須在執行有窮步後結束

  • 肯定性

    算法的每個步驟都必須是明肯定義的,

  • 可行性

    算法中的每一步都是能夠經過已實現的操做來完成的

  • 輸入

    一個算法具有0或多個輸入

  • 輸出

    一個算法有一個或多個輸出,它們與輸入有着特定的關係

算法與程序的區別,算法只是一種描述,可使用任何語言,可是一般不能直接被計算機運行,而程序則是算法的具體實現,使用某種計算機語言;

算法設計應知足的要求

  • 正確性:對於合法的輸入產生符合要求的輸出

  • 易讀性:算法應該儘量易讀,便於交流,這也是保證正確性的前提(註釋可提升易讀性)

  • 健壯性:當輸入非法數據時,算法可做出適當反應而不至於崩潰(例如輸出錯誤緣由);

  • 時空性:指的是算法的時間複雜度和空間複雜度,算法分析主要也是分析算法的時間複雜度和空間複雜的,其目的是提升算法的效率;

算法分析

解決同一問題的算法可能有多種,咱們但願從中選出最優的算法,效率高的,佔用空間小的,爲此咱們就須要對算法進行評估和分析;

一般評估算法根據兩個度量

  • 時間複雜度:算法運行完成所需的總步數(標準操做),一般是問題規模的函數

  • 空間複雜度:算法執行時所佔用的存儲空間,一般是問題規模的函數

肯定算法的計算量

  • 合理選擇一種或幾種操做做爲'標準操做',無特殊說明默認以賦值操做做爲標準操做;
  • 肯定算法共執行多少次標準操做,並將這次數規定爲算法的計算量
  • 以算法在全部時輸入下的計算量最大值做爲算法的最壞狀況時間複雜度
  • 以算法在全部時輸入下的計算量最小值做爲算法的最好狀況時間複雜度
  • 以算法在全部時輸入下的計算量平均值做爲算法的平均狀況時間複雜度
  • 最壞/平均狀況時間複雜度均可做爲時間複雜度,一般以最壞狀況衡量;

注意:時間複雜度一般以量級來衡量,也就是說不須要精確的計算到底執行了幾步,而是得出其計算量的數量級便可,並忽略常數,由於當數量級足夠大時,常數對於計算量的影響能夠忽略不計;

如: (n-1)(n-2) 數量級爲 n^2

時間複雜度使用大O表示,如O(1)

案例:

1.
void aFunction(){
    int c = 10 + 20;
    int d = c * c;
        printf(d);
}

上列算法若以賦值運算做爲標準操做,則該算法的計算量爲2,其時間複雜度記爲O(1),爲何是O(1)呢,是由於2是一個常數,常數對於函數的增加影響並不大,因此計算量爲常數時表示爲O(1),按照這種方式,即便計算量爲2000,一樣記爲O(1),稱爲常數

2.
void bFunction(int n){
  for(int i = 0;i < n;i++){ // n
    int c = 2 * i;// 1
    int d = 3 * i;// 2
  }
}

此時函數的循環次數由未知數n來決定,循環體內計算量爲2,當n是一個天然數時,函數的計算量等於(n)(2),此時時間複雜度爲O(n),不管用常數2對n進行加減乘除對於n的指數都沒有影響,當n足夠大時,內部的2次計算量能夠忽略,因此記爲O(n),稱爲線性階

更粗陋的度量方法是函數體包含一層循環時記爲O(n)

3.
void bFunction(int n){
  for(int i = 0;i < n;i++){
      for(int j = 0;j < i;j++){
      }
  }
}

外層循環次數爲n,內層循環次數隨着n的增加而增加且最大值爲n-1次,那麼整個函數的計算量爲(n)(n-1),常數能夠忽略,因此時間複雜度記爲O(n^2) ,稱爲平方階

粗陋的方法是有兩層嵌套循環,且循環次數都隨着n的增加而增加,因此是O(n^2),以此類推,可是要注意下面這種狀況

4.
void bFunction(int n){
  for(int i = 0;i < n;i++){
      for(int j = 0;j < 3;j++){
      }
  }
}

此時內層循環的循環次數是固定(常數)因此不會影響計算量的數量級,時間複雜度記爲O(n)

5.
void bFunction(int n){
  for(int i = 3;i < n;){
      i *= 3;
  }
}

此時函循環次數會隨着3循環體中的常數3的的變化而變化,咱們能夠用對數來表示,

假設循環次數爲s,循環條件可表示爲 s = 3^s < n;(即i自己爲3,其次冪會不斷的增加,但結果要小於n)

固然這裏有個條件是i的初值必須和每次乘等的值相同,造成次冪的增加;

用對數表示爲s = log3n,時間複雜度記爲O(log3n),常數能夠忽略,因此最後是O(logn)稱之爲對數階

對數階的數量級小於線性階,由於若n的值相同,對數階計算量必然小於線性階

6.
void bFunction(int n){
 for(int i = 0;i < n;i++){
     for(int j = 0;j < n;j++){
         for(int k = 0;k < n;k++){
       }
     }   
 }
}

上述算法時間複雜度爲O(n^3),3爲常數,對應着循環的嵌套層數;也能夠用O(n^C)表示,稱爲多項式階

7.
void bFunction(int n){
  int num = 2;
  for(int i = 0;i < n;){ //O(n)
        num *= 2;
  }
  for (int j = 0;j<num;j++){ //O(n)
  }
}

上述函數輸入的參數n將做爲循環次數的指數,假設循環次數爲s, s = 2^n,那麼時間複雜度爲O(2^n),

稱之爲指數階,可記爲O(C^n)

8.
void bFunction(int n)
{
    for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
          }
    }   
  
      for(int i=0;i<n;i++){
      }             
}

對於順序運行的算法,總時間複雜度等於算法中最大時間複雜度,即O(n^2)

9.
void bFunction(int n)
{
  if(n % 2 ==0){
    for(int i=0;i<n;i++){
          for(int j=0;j<n;j++){
          }
    }   
  }else{
      for(int i=0;i<n;i++){
      }             
  } 
}

對於具有分支結構的算法,總時間複雜度等於算法中時間複雜度最大路徑的複雜度,即O(n^2)

時間複雜度大小順序

常數 < 對數階 < 線性階 < 平方階 < 多項式階 < 指數階

O(1) < O(logn) < O(n) < O(n^2) < O(n^C) < O(C^n)

我的觀點,如有錯誤敬請指出,謝謝!

相關文章
相關標籤/搜索