插入排序 逆序對本質
資料結構概述
1. 資料結構 = 個體的儲存 + 個體的關係儲存
把現實中大量而複雜的問題以特定的資料型別和特定的儲存結構儲存到主儲存器(記憶體)中,以及在此基礎上實現某個功能(比如查詢,刪除某個元素,對所有 元素進行排序)而執行的相應操作,這個相應的操作也叫演算法。
2. 演算法 = 對儲存資料的操作( 解題的方法和步驟 )
衡量演算法的標準:
- 時間複雜度:大概程式要執行的次數,而非執行的時間;
- 空間複雜度:演算法執行過程中大概所佔用的最大記憶體;
- 難易程度; 4.健壯性;
-
邏輯結構
1)線性結構:
- 陣列
- 連結串列
棧和佇列是一種特殊的線性結構
2)非線性結構:
- 樹
- 圖
-
物理結構
模組一 線性結構
【把所有的結點用一根直線穿起來】
1. 連續儲存【陣列】
優點:存取速度快
缺點:插入刪除元素慢;事先必須知道陣列的長度;空間通常是有限制的,需要大塊連續的記憶體塊
2. 離散儲存【連結串列】
優點:插入刪除元素快;空間沒有限制
缺點:存取速度慢
2.1 定義
n 個節點離散分配,彼此通過指標相連;每個節點只有一個前驅節點和一個後續節點;首節點沒有前驅節點,尾節點沒有後續節點
-
首節點:第一個有效節點
-
尾節點:最後一個有效節點,指標域為空
-
頭結點:第一個有效節點之前的那個節點
- 頭結點並不存放有效資料
- 可以方便對連結串列的操作
-
頭指標:指向頭結點的指標變數,存放的頭結點地址
-
尾指標:指向尾結點的指標變數,存放的尾結點地址
若通過一個函式對連結串列進行處理,確定一個連結串列,只需要一個引數:頭指標,即可推算出連結串列的其他所有資訊。
2.2 分類
-
單鏈表:每個節點的指標域只能指向後繼節點
-
雙鏈表:每一個節點有兩個指標域
-
迴圈連結串列:能通過任何一個節點找到其他所有的節點
-
非迴圈連結串列
演算法:遍歷、查詢、清空、銷燬、求長度、排序、刪除、插入
3. 線性結構的兩種常見應用之一 棧
3.1 定義
一種可以實現 “ 先進後出 ” 的儲存結構,類似於彈夾
3.2 分類
-
靜態棧
-
動態棧
3.3 演算法
-
出棧
-
壓棧
3.4 應用
函式呼叫、中斷、表示式求值(利用兩個棧可以編寫一個計算器)、記憶體分配緩衝處理、迷宮
4. 線性結構的兩種常見應用之二 佇列
4.1 定義
一種可以實現 “ 先進先出 ” 的儲存結構
4.2 分類
-
鏈式佇列 —— 用連結串列實現
-
靜態佇列 —— 用陣列實現
4.2.1 靜態佇列
靜態佇列通常都必須是迴圈佇列 !
迴圈佇列:
- 靜態佇列為什麼必須是迴圈佇列
避免記憶體洩漏
- 迴圈佇列需要幾個引數來確定
2個引數:
front
、rear
- 迴圈佇列各個引數的含義
不同場合有不同含義:
1)佇列初始化:
front
和rear
的值都是零2)佇列非空:
front
代表的是佇列的第一個元素
rear
代表的是佇列的最後一個有效元素的下一個元素3)佇列空
front
和rear
的值相等,但不一定是零
- 迴圈佇列入隊偽演算法講解
1)將值存入
r
所代表的位置2)正確的寫法:
rear = ( rear + 1 ) % 陣列的長度
錯誤的寫法:
rear = rear + 1
- 迴圈隊列出隊位演算法講解
front = ( front + 1 ) % 陣列的長度
- 如何判斷迴圈佇列是否為空
如果
front
與rear
的值相等則該佇列就一定為空
- 如何判斷迴圈佇列是否已滿
front
的值可能比 rear
大,也可能比 rear
小,也有可能兩者相等
兩種方式:
1)多增加一個標識引數
2)少用一個元素 (通常使用第二種方式)
如果 front 和 rear 的值緊挨著,則佇列已滿
if ( ( rear + 1 ) % 陣列長度 == front )
已滿
else
不滿
-
演算法:
入隊
出隊
-
佇列的具體應用:所有和時間有關的操作都有佇列的影子
5. 專題 —— 遞迴
5.1 定義
一個函式自己直接或間接呼叫自己
5.2 滿足的三個條件
-
遞迴必須得有一個明確的終止條件;
-
該函式所處理的資料規模必須在遞減;
-
這個轉化必須是可解的。
5.3 舉例
-
求階乘
-
1+2+3+…+100
-
漢諾塔
-
走迷宮
5.4 迴圈和遞迴
-
迴圈:不易理解,速度快,儲存空間小
-
遞迴:易於理解,速度慢,儲存空間大
5.5 應用
-
樹和森林就是以遞迴的方式定義的
-
數和圖的很多演算法都是以遞迴來實現的
-
很多數學公式就是以遞迴的方式定義的
6. 函式的呼叫
-
當在一個函式的執行期間呼叫另一個函式時,在執行被呼叫函式之前,系統需要完成的三件事:
1)將所有的實際引數,返回地址(下一條語句)等資訊傳遞給被調函式儲存;
2)為被調函式的區域性變數(也包括形參)分配儲存空間;
3)將控制轉移到被調函式的入口。
-
從被調函式返回主調函式之前,系統也要完成的三件事:
1)儲存被調函式的返回結果;
2)釋放被調函式所佔的儲存空間;
3)依照被調函式儲存的返回地址將控制轉移到呼叫函式。
-
當有多個函式相互呼叫時,按照 “ 後呼叫先返回 ” 的原則,上述函式之間資訊傳遞和控制轉移必須藉助 “ 棧 ” 來實現,即系統將整個程式執行時所需的資料空間安排在一個棧中,每當呼叫一個函式時,就在棧頂分配一個儲存區,進行壓棧操作,每當一個函式退出時,就釋放它的儲存區,就進行出棧操作,當前執行的函式永遠都在棧頂位置。
模組二 非線性結構
1. 樹
1.1 定義
- 專業定義:
-
有且只有一個根節點;
-
有若干個互不相交的子樹,這些子樹本身也是一棵樹
- 通俗定義:
-
樹是由節點和邊組成;
-
每個節點只有一個父節點,但可以有多個子節點;
-
但有一個節點例外,該節點沒有父節點,此節點稱為根節點
1.2 專業術語
- 節點 父節點,子節點
- 子孫 堂兄弟
- 深度:從根節點(第一層)到最底層節點的層數稱之為深度
- 葉子節點:沒有子節點的節點
- 非終端節點:實際就是非葉子節點,即有子節點的節點
- 度:子節點的個數稱為度
1.3 分類
1.3.1 一般樹
任意一個節點的子節點個數都不受限制
1.3.2 二叉樹
任意一個節點的子節點個數最多為兩個(可以一個,可以兩個),且子節點的位置不可更改(左子樹,右子樹)
1)一般二叉樹
2)滿二叉樹:在不增加樹的層數的前提下,無法再多新增一個節點的二叉樹就是滿二叉樹
3)完全二叉樹:如果只是刪除了滿二叉樹最底層最右邊的連續若干個節點,這樣形成的二叉樹就是完全二叉樹。(特例:滿二叉樹)
性質:
知道節點個數,可以知道樹有多少層;
知道任意一個點,可以知道它的父節點,有沒有子節點,子節點是誰。
1.3.3 森林
n 個互不相交的樹的集合
1.4 儲存
1.4.1 二叉樹的儲存
-
連續儲存【完全二叉樹】
- 優點:查詢某個節點的父節點和子節點速度很快(也包括判斷有沒有子節點)
- 缺點:耗用記憶體空間過大
-
鏈式儲存
-
一般樹的儲存:把一個普通樹轉化成二叉樹來儲存。一個普通樹轉化成的 二叉樹一定沒有右子樹!
1)雙親表示法:求父節點方便
2)孩子表示法:求子節點方便
3)雙親孩子表示法:求父節點和子節點都很方便
4)二叉樹表示法:具體轉化方法:設法保證任意一個節點的 左指標域指向它的第一個孩子,右指標域指向它的下一個兄弟。
1.4.2 森林的儲存
具體轉化方法:先把森林轉化成二叉樹,再儲存二叉樹。左指標域指向它的第一個孩子,右指標域指向另一棵樹。
1.5 二叉樹操作
1.5.1 遍歷:遞迴!
-
先序遍歷( 根——左——右 ):先訪問根節點,再先序訪問左子樹,最後先序訪問右子樹
-
中序遍歷( 左——根——右 ):先中序遍歷左子樹,再訪問根節點,最後中序遍歷右子樹
-
後序遍歷( 左——右——根 ):先後序遍歷左子樹,再後序遍歷右子樹,最後訪問根節點
- 已知兩種遍歷序列來求原始二叉樹:通過 先序和中序 或 中序和後序 ,可以還原出原始的二叉樹。但是,通過 先序和後序 無法還原出原始的二叉樹!
應用:
-
樹是資料庫中資料組織的一種重要形式
-
作業系統子父程序的關係本身就是一棵樹
-
面嚮物件語言中類的繼承關係本身就是一棵樹