二叉樹、排序算法與數據庫
二叉樹
二叉樹的性質
- 節點數與深度的關系:深度為 k k k的二叉樹,最多有 2 k ? 1 2^k - 1 2k?1個節點。例如,深度為 3 3 3的二叉樹,最多有 2 3 ? 1 = 7 2^3 - 1 = 7 23?1=7個節點。
- 葉子節點與度為2節點的關系:對于任意一棵二叉樹,如果其葉子節點數為 n 0 n_0 n0?,度為 2 2 2的節點數為 n 2 n_2 n2?,則 n 0 = n 2 + 1 n_0 = n_2 + 1 n0?=n2?+1
根節點/ \左子樹 右子樹/ \ / \
節點 節點 節點 節點
二叉樹的存儲結構
- 順序存儲結構:對于完全二叉樹,可以使用數組來存儲節點。將根節點存儲在數組的第一個位置,然后按照從上到下、從左到右的順序依次存儲其他節點。這樣可以通過節點的下標來快速訪問其左右子節點。例如,對于節點 i i i,其左子節點的下標為 2 i + 1 2i + 1 2i+1,右子節點的下標為 2 i + 2 2i + 2 2i+2。
- 鏈式存儲結構:通常使用鏈表來表示二叉樹的節點。每個節點包含三個部分:數據域、左指針域和右指針域。左指針域指向該節點的左子節點,右指針域指向該節點的右子節點。這種存儲結構可以方便地進行節點的插入和刪除操作。
二叉樹的遍歷方式
- 前序遍歷:先訪問根節點,然后遞歸地遍歷左子樹,最后遞歸地遍歷右子樹。例如,對于二叉樹 A ( B ( D , E ) , C ( F ) ) A(B(D,E),C(F)) A(B(D,E),C(F))
前序遍歷的結果為 A B D E C F ABDECF ABDECF - 中序遍歷:先遞歸地遍歷左子樹,然后訪問根節點,最后遞歸地遍歷右子樹。對于上述二叉樹,中序遍歷的結果為 D B E A C F DBEACF DBEACF
- 后序遍歷:先遞歸地遍歷左子樹,然后遞歸地遍歷右子樹,最后訪問根節點。對于上述二叉樹,后序遍歷的結果為 D E B F C A DEBFCA DEBFCA
- 層序遍歷:按照從上到下、從左到右的順序依次訪問二叉樹的每一層節點。對于上述二叉樹,層序遍歷的結果為 A B C D E F ABCDEF ABCDEF
題目 \text{題目} 題目
設二叉樹的中序序列為 B C D A BCDA BCDA,后序序列為 D C B A DCBA DCBA,則前序序列為: ( ) \textbf{(\qquad)} ()
解析 \text{解析} 解析
首先,根據后序可以得到根節點:A
其次,根據中序可以得到左子樹和右子樹:BCD
空
然后再根據后序確定左子樹的結構:根節點為:B
,D
在左,C
在右或者D
在上,C
在下。
再看,中序發現C
跑中間去了,所以是第二種情況
因此,答案為: A B C D ABCD ABCD
排序算法
排序算法是計算機科學中用于將一組數據按照特定順序(如升序或降序)排列的算法。
- 冒泡排序(Bubble Sort):
- 基本思想:重復地走訪要排序的數列,一次比較兩個數據元素,如果順序不對則進行交換,并一直重復這樣的走訪操作,直到沒有要交換的數據元素為止。
- 示例代碼(Python):
def bubble_sort(arr):n = len(arr)for i in range(n):for j in range(0, n - i - 1):if arr[j] > arr[j + 1]:arr[j], arr[j + 1] = arr[j + 1], arr[j]return arr
- 時間復雜度:平均和最壞情況下為 O ( n 2 ) O(n^2) O(n2),最好情況(數組已經有序)下為 O ( n ) O(n) O(n)。
- 空間復雜度: O ( 1 ) O(1) O(1),因為只需要幾個額外的變量來進行交換操作,不隨數據規模增長。
- 穩定性:穩定,相同元素的相對順序在排序前后不會改變。
- 選擇排序(Selection Sort):
- 基本思想:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
- 示例代碼(Python):
def selection_sort(arr):n = len(arr)for i in range(n):min_index = ifor j in range(i + 1, n):if arr[j] < arr[min_index]:min_index = jarr[i], arr[min_index] = arr[min_index], arr[i]return arr
- 時間復雜度:平均和最壞情況下為 O ( n 2 ) O(n^2) O(n2),最好情況也是 O ( n 2 ) O(n^2) O(n2),因為每次都要遍歷剩余未排序元素找最小(大)值。
- 空間復雜度: O ( 1 ) O(1) O(1)。
- 穩定性:不穩定,例如序列
[5, 5, 3]
,在排序過程中,兩個5
的相對順序可能會改變。
- 插入排序(Insertion Sort):
- 基本思想:將一個數據插入到已經排好序的數組中的適當位置,從而得到一個新的、個數加一的有序數組。初始時把第一個元素看作已排序序列,然后依次將后面的元素插入到合適位置。
- 示例代碼(Python):
def insertion_sort(arr):for i in range(1, len(arr)):key = arr[i]j = i - 1while j >= 0 and arr[j] > key:arr[j + 1] = arr[j]j = j - 1arr[j + 1] = keyreturn arr
- 時間復雜度:平均和最壞情況下為 O ( n 2 ) O(n^2) O(n2),最好情況(數組已經有序)下為 O ( n ) O(n) O(n)。
- 空間復雜度: O ( 1 ) O(1) O(1)。
- 穩定性:穩定,在插入過程中,相同元素的相對順序不會改變。
- 快速排序(Quick Sort):
- 基本思想:通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
- 示例代碼(Python):
def quick_sort(arr):if len(arr) <= 1:return arrpivot = arr[len(arr) // 2]left = [x for x in arr if x < pivot]middle = [x for x in arr if x == pivot]right = [x for x in arr if x > pivot]return quick_sort(left) + middle + quick_sort(right)
- 時間復雜度:平均情況下為 O ( n log ? n ) O(n \log n) O(nlogn),最壞情況(如數組已經有序且每次選擇的基準值都不合適)下為 O ( n 2 ) O(n^2) O(n2)。
- 空間復雜度:平均情況下為 O ( log ? n ) O(\log n) O(logn),最壞情況(遞歸深度達到 n n n 層)下為 O ( n ) O(n) O(n)。
- 穩定性:不穩定,因為在劃分過程中,相同元素的相對順序可能會改變。
- 歸并排序(Merge Sort):
- 基本思想:采用分治法,將一個序列分成兩個長度相等的子序列,分別對這兩個子序列進行排序,然后將排好序的子序列合并成一個最終的有序序列。
- 示例代碼(Python):
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = merge_sort(arr[:mid])right_half = merge_sort(arr[mid:])return merge(left_half, right_half)def merge(left, right):result = []i = j = 0while i < len(left) and j < len(right):if left[i] < right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1result.extend(left[i:])result.extend(right[j:])return result
- 時間復雜度:平均、最壞和最好情況下均為 O ( n log ? n ) O(n \log n) O(nlogn),因為每次都是將序列分成兩部分,共分 log ? n \log n logn 層,每層合并操作的時間復雜度為 O ( n ) O(n) O(n)。
- 空間復雜度: O ( n ) O(n) O(n),因為在合并過程中需要額外的空間來存儲臨時序列。
- 穩定性:穩定,在合并過程中,相同元素的相對順序不會改變。
- 堆排序(Heap Sort):
- 基本思想:利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子節點的鍵值或索引總是小于(或者大于)它的父節點。先將待排序序列構建成一個大頂堆(或小頂堆),此時,整個序列的最大值(或最小值)就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值(或最小值)。然后將剩余 n ? 1 n - 1 n?1 個元素重新構造成一個堆,這樣會得到 n n n個元素的次大值(或次小值)。如此反復執行,便能得到一個有序序列。
- 示例代碼(Python):
def heapify(arr, n, i):largest = il = 2 * i + 1r = 2 * i + 2if l < n and arr[i] < arr[l]:largest = lif r < n and arr[largest] < arr[r]:largest = rif largest != i:arr[i], arr[largest] = arr[largest], arr[i]heapify(arr, n, largest)def heap_sort(arr):n = len(arr)for i in range(n // 2 - 1, -1, -1):heapify(arr, n, i)for i in range(n - 1, 0, -1):arr[i], arr[0] = arr[0], arr[i]heapify(arr, i, 0)return arr
- 時間復雜度:平均和最壞情況下為 O ( n log ? n ) O(n \log n) O(nlogn),因為構建堆的時間復雜度為 O ( n ) O(n) O(n),調整堆的時間復雜度為 O ( log ? n ) O(\log n) O(logn),共進行 n ? 1 n - 1 n?1 次調整。
- 空間復雜度: O ( 1 ) O(1) O(1),只需要一些常數級別的額外空間。
- 穩定性:不穩定,在調整堆的過程中,相同元素的相對順序可能會改變。
排序算法 | 平均時間復雜度 | 最壞時間復雜度 | 最好時間復雜度 | 空間復雜度 | 穩定性 | 基本思想 |
---|---|---|---|---|---|---|
冒泡排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 穩定 | 重復走訪數列,比較相鄰元素,若順序不對則交換,直到沒有元素需要交換 |
選擇排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( 1 ) O(1) O(1) | 不穩定 | 從未排序序列中找最小(大)元素,放到已排序序列末尾,重復此過程 |
插入排序 | O ( n 2 ) O(n^2) O(n2) | O ( n 2 ) O(n^2) O(n2) | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 穩定 | 將數據插入到已排序數組的合適位置,初始把第一個元素看作已排序序列 |
快速排序 | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n 2 ) O(n^2) O(n2) | O ( n log ? n ) O(n \log n) O(nlogn) | 平均 O ( log ? n ) O(\log n) O(logn),最壞 O ( n ) O(n) O(n) | 不穩定 | 選擇基準值,將序列分成兩部分,分別對兩部分遞歸排序 |
歸并排序 | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n ) O(n) O(n) | 穩定 | 采用分治法,將序列分成子序列排序后合并成最終有序序列 |
堆排序 | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n log ? n ) O(n \log n) O(nlogn) | O ( n log ? n ) O(n \log n) O(nlogn) | O ( 1 ) O(1) O(1) | 不穩定 | 先構建堆,將堆頂元素與末尾元素交換,調整堆,重復此過程 |
以下是幾種常見排序算法對長度為(n)的數組排序最多需要的步驟公式:
排序算法 | 最多步驟公式 |
---|---|
冒泡排序 | n ( n ? 1 ) 2 \frac{n(n - 1)}{2} 2n(n?1)? |
選擇排序 | n ( n ? 1 ) 2 + n ? 1 \frac{n(n - 1)}{2}+n - 1 2n(n?1)?+n?1 |
插入排序 | n ( n ? 1 ) n(n - 1) n(n?1) |
快速排序 | n ( n ? 1 ) ÷ 2 n(n - 1)\div2 n(n?1)÷2(最壞情況) |
歸并排序 | n log ? 2 n n\log_2n nlog2?n |
堆排序 | n log ? 2 n + n n\log_2n + n nlog2?n+n |
以下幾個結論:
- 最壞情況下希爾排序和堆排序的時間復雜度與其它的不同。
- 最壞情況下:有序表的對分查找<循環鏈表最大項<順序查找<堆排序
結構圖
結構圖是描述系統、組織或結構層次關系的可視化工具,其深度和相關概念在不同領域(如計算機科學、管理學、系統工程等)有不同的定義和應用。以下是關于結構圖深度及其相關概念的詳細說明:
一、結構圖的深度(Depth)
定義:
結構圖的深度指從最高層(根節點)到最低層(葉子節點)的層級數量。它反映了系統或組織的縱向復雜度。
示例:
- 在公司組織結構圖中,若層級為“董事會→CEO→部門經理→團隊主管→員工”,則深度為5層。
- 在軟件架構中,若類繼承層次為“基類→子類A→子類B→子類C”,則深度為4層。
意義:
- 深度過深(如多層嵌套)可能導致:
- 信息傳遞效率降低(如決策延遲)。
- 維護成本增加(如代碼修改需逐層調整)。
- 深度過淺(如扁平化結構)可能導致:
- 管理寬度過大(如一個管理者直接領導過多下屬)。
- 功能劃分不清晰(如模塊職責不明確)。
二、相關概念
1. 寬度(Width)
- 定義:某一層級中節點的橫向數量(如同一層級的子模塊或部門數量)。
- 示例:部門經理下有5個團隊主管,寬度為5。
- 意義:寬度過大會增加管理難度(需平衡管理跨度)。
2. 復雜度(Complexity)
- 定義:結構中節點間連接關系的數量和類型(如依賴、繼承、調用關系)。
- 示例:模塊間頻繁調用或循環依賴會增加復雜度。
- 意義:高復雜度可能導致系統難以理解、擴展或維護。
3. 模塊化(Modularity)
- 定義:將系統分解為獨立、可替換的模塊,每個模塊完成特定功能。
- 示例:軟件中的“用戶模塊”“支付模塊”。
- 意義:提高復用性、降低耦合度(模塊間低依賴)。
4. 耦合(Coupling)與內聚(Cohesion)
- 耦合:模塊間的依賴程度(低耦合更優)。
- 內聚:模塊內部功能的相關性(高內聚更優)。
- 示例:若模塊A直接修改模塊B的數據,耦合度高;若模塊僅處理自身業務邏輯,內聚度高。
5. 扇入(Fan-In)與扇出(Fan-Out)
- 扇入:某模塊被其他模塊調用的次數。
- 扇出:某模塊調用其他模塊的次數。
- 意義:高扇入說明模塊復用性強;高扇出可能增加維護難度。
三、應用場景
- 軟件架構設計:
- 控制繼承層次深度(避免“類爆炸”)。
- 優化模塊間依賴關系(降低耦合)。
- 組織管理:
- 設計層級結構(如扁平化或科層制)。
- 平衡管理寬度(如管理跨度理論)。
- 系統工程:
- 分解復雜系統為子系統(如航空航天系統)。
- 確保各層級職責清晰(如硬件層、驅動層、應用層)。
四、設計原則
- 適度深度:根據領域需求平衡層級(如管理類系統通常較扁平,技術類系統可能更復雜)。
- 高內聚、低耦合:模塊獨立且功能集中。
- 單一職責原則:每個模塊/層級只負責一類功能。
- 開閉原則:允許擴展(增加層級)而不修改現有結構。
隊列
front=rear
:隊列要么空要么滿