C 語言數據結構(一):時/空間復制度

目錄

一、前言

1. 什么是數據結構

2. 什么是算法

二、時 / 空間復雜度

1. 算法效率

2. 時間復雜度

2.1 時間復雜度的概念

2.2 大 O 的漸進表示法

2.3 常見的計算時間復雜度的例子

2.3.1 實例 1

2.3.2 實例 2

2.3.3 實例 3

2.3.4 實例 4

2.3.5 實例 5 :冒泡排序

2.3.6 實例 6 :二分查找

2.3.7 實例 7 :階乘遞歸

2.3.8 實例 8 :斐波那契遞歸

3. 空間復雜度

3.1?空間復雜度的概念

3.2 顯式申請的額外空間

3.3?常見的計算空間復雜度的例子

3.3.1 實例 1 :冒泡排序

3.3.2 實例 2 :返回斐波那契數列的前n項

3.3.3 實例 3 :階乘遞歸

4.

4.1 常見復雜度總結

4.2?判斷復雜度的方法

4.2.1?判斷時間復雜度的方法

4.2.2?判斷空間復雜度的方法

5. 練習

5.1 消失的數字

5.2 輪轉數組

💬 :如果你在閱讀過程中有任何疑問或想要進一步探討的內容,歡迎在評論區暢所欲言!我們一起學習、共同成長~!

👍 :如果你覺得這篇文章還不錯,不妨順手、加入收藏,并分享給更多的朋友噢~!


一、前言

1. 什么是數據結構

數據結構(Data Structure)是計算機存儲、組織數據的方式,指相互之間存在一種或多種特定關系的數據元素的集合。

2. 什么是算法

算法(Algorithm)是明確的計算流程,它接收一個或一組輸入值,經過一系列計算步驟后,輸出一個或一組結果,其作用就是把輸入數據轉變為輸出結果。


二、時 / 空間復雜度

1. 算法效率

算法在編寫成可執行程序后,運行時需要耗費時間資源和空間(內存)資源 。

因此衡量一個算法的好壞,一般從時間和空間兩個維度來衡量,即時間復雜度和空間復雜度。

  • 時間復雜度主要衡量一個算法的運行快慢。
  • 空間復雜度主要衡量一個算法運行所需要的額外空間。

計算機發展早期,由于存儲容量很小,所以很在乎空間復雜度。但隨著計算機行業迅速發展,如今計算機的存儲容量已達到很高程度,所以我們現在不需特別關注一個算法的空間復雜度。

2. 時間復雜度

2.1 時間復雜度的概念

時間復雜度是定量描述算法運行時間隨問題規模 N 增長的變化趨勢的函數。

理論上,算法執行耗時無法直接算出,需運行程序才知曉,但沒必要對每個算法都上機測試,于是有了時間復雜度分析。

算法耗時與語句執行次數成正比,基本操作的執行次數就是算法的時間復雜度。即,算出某條基本語句和問題規模 N 的數學表達式,就得到了該算法的時間復雜度。

// 計算一下Func1中++count語句共執行了多少次?
void Func1(int N)
{int count = 0;for (int i = 0; i < N ; ++ i){for (int j = 0; j < N ; ++ j){++count;}}for (int k = 0; k < 2 * N ; ++ k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

這里 Func1 執行的基本操作次數 :

  • 當 N = 10 時,F (N) = 130
  • 當 N = 100 時,F (N) = 10210
  • 當 N = 1000 時,F (N) = 1002010

實際中計算時間復雜度時,不一定要計算精確的執行次數,只需獲知大概執行次數,此時使用大 O 的漸進表示法。

2.2 大 O 的漸進表示法

大 O 符號(Big O notation):用于描述函數漸進行為的數學符號。

推導大 O 階:

  1. 用常數 1 取代運行時間中的所有加法常數。
  2. 在修改后的運行次數函數中,只保留最高階項 ?。
  3. 若最高階項存在且不是 1,則去掉該項系數 y 。得到的結果就是大 O 階 ?。

對于前面的?

  • 步驟 1:用常數 1 取代加法常數 10,得??。
  • 步驟 2:只保留最高階項?
  • 步驟 3:最高階項??的系數為 1,無需去除,得到大 O 階 ?。

因此使用大 O 的漸進表示法后,前面 Func1 的時間復雜度為??。

另外,有些算法的時間復雜度存在最好、平均和最壞運行情況:

  • 最壞情況:任意輸入規模的最大運行次數 (上界)
  • 平均情況:任意輸入規模的期望運行次數
  • 最好情況:任意輸入規模的最小運行次數 (下界)

例如:在一個長度為 N 的數組中搜索一個數據 x

  • 最好情況:1 次找到
  • 最壞情況:N 次找到
  • 平均情況:N/2 次找到

實際中一般關注算法的最壞運行情況,所以數組中搜索數據的時間復雜度為 O (N) 。

2.3 常見的計算時間復雜度的例子

2.3.1 實例 1
void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N ; ++ k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

基本操作執行次數??,所以 Func2 時間復雜度為 O(N) 。

2.3.2 實例 2
void Func3(int N, int M)
{int count = 0;for (int k = 0; k < M; ++ k){++count;}for (int k = 0; k < N ; ++ k){++count;}printf("%d\n", count);
}

基本操作執行次數??,有兩個未知數 M 和 N,所以 Func3 時間復雜度為 O(N + M) 。

2.3.3 實例 3
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++ k){++count;}printf("%d\n", count);
}

基本操作執行次數 100,所以 Func4 時間復雜度為 O(1) 。

2.3.4 實例 4
// 計算strchr的時間復雜度?
const char *strchr ( const char *str, int character );

基本操作執行最好 1 次,最壞 N 次,時間復雜度一般看最壞,所以時間復雜度為 O(N) 。

2.3.5 實例 5 :冒泡排序
// 冒泡排序
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i-1] > a[i]){Swap(&a[i-1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

基本操作執行最好? 次(數組已有序,只需進行一輪遍歷),最壞執行了? 次(? ),時間復雜度一般看最壞,所以時間復雜度為?

2.3.6 實例 6 :二分查找
// 二分查找
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n-1;// [begin, end]:begin和end是左閉右閉區間,因此有=號while (begin <= end){int mid = begin + ((end-begin)>>1);if (a[mid] < x)begin = mid+1;else if (a[mid] > x)end = mid-1;elsereturn mid;}return -1;
}

基本操作執行最好 1 次(第一次比較就找到目標值),最壞? 次(第 k 次比較后,搜索區間長度變為??,?得??。又因為大 O 表示法中,通常忽略對數的底數 ),所以時間復雜度為? 。有些地方寫成??。

2.3.7 實例 7 :階乘遞歸
// 階乘遞歸
long long Fac(size_t N)
{if(0 == N)return 1;return Fac(N-1)*N;
}

基本操作執行了 N 次,所以時間復雜度為 O(N) 。

2.3.8 實例 8 :斐波那契遞歸
// 斐波那契遞歸
long long Fib(size_t N)
{if(N < 3)return 1;return Fib(N-1) + Fib(N-2);
}

以 Fib(5)?為例,構建它的遞歸調用樹:

  • 當 N < 3 時,F(N) = 1?;
  • 當?N >=?3?時,F(N) = F(N - 1) + F(N - 2) +1 (+1是當前Fib(N)的這次調用)。此遞推關系的解的增長趨勢與? 同階。

又因為大 O 表示法忽略常數和低階項,所以該算法的時間復雜度為?

3. 空間復雜度

3.1?空間復雜度的概念

空間復雜度是用于衡量算法運行時臨時占用存儲空間大小的數學表達式。

其計算規則與時間復雜度類似,采用大O漸進表示法。

需注意,函數運行所需的棧空間(如參數、局部變量、寄存器信息等占用空間)在編譯時已確定,故空間復雜度主要看函數運行時顯式申請的額外空間

3.2 顯式申請的額外空間

  • 動態內存分配函數申請的空間。

  • 部分庫函數內部會申請額外空間,C 語言標準庫中常見的如?realloc 、strdup 等。

  • 創建某些數據結構時需專門寫代碼向系統申請內存,那么申請到的這部分內存空間就屬于額外空間。如,創建鏈表節點時得用?malloc?或?new?這些操作去申請內存。

3.3?常見的計算空間復雜度的例子

3.3.1 實例 1 :冒泡排序
// 計算BubbleSort的空間復雜度
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i-1] > a[i]){Swap(&a[i-1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

BubbleSort函數未通過動態內存分配函數、特定庫函數或創建復雜數據結構來顯式申請額外空間,僅使用了幾個固定的局部變量,所以它使用了常數個額外空間,其空間復雜度為?O(1)。

3.3.2 實例 2 :返回斐波那契數列的前n項
// 計算Fibonacci的空間復雜度
// 返回斐波那契數列的前n項
long long* Fibonacci(size_t n)
{if(n==0)return NULL;long long * fibArray = (long long *)malloc((n+1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n ; ++i){fibArray[i] = fibArray[i - 1] + fibArray [i - 2];}return fibArray;
}

動態開辟了 N+1 個空間,空間復雜度為 O(N) 。

3.3.3 實例 3 :階乘遞歸
// 計算階乘遞歸Fac的空間復雜度
long long Fac(size_t N)
{if(N == 0)return 1;return Fac(N-1)*N;
}

Fac函數雖然未顯式地使用動態內存分配函數來申請額外空間,但遞歸調用會隱式地占用與遞歸深度成正比的棧空間,因此在計算空間復雜度時需要考慮這部分額外空間

這里遞歸調用了 N+1 次,所以空間復雜度為 O(N) 。

4.

4.1 常見復雜度總結

基本操作執行次數/額外空間量復雜度
常數常數階
線性階
平方階
對數階
NlogN階
立方階
指數階

4.2?判斷復雜度的方法

4.2.1?判斷時間復雜度的方法
  1. 確定基本操作:如簡單賦值、算術和比較運算等,其時間復雜度為?O(1)。
  2. 分析循環
    • 單層循環:循環體為基本操作且次數與輸入規模?n?相關,復雜度為?O(n)。
    • 嵌套循環:多層嵌套時,復雜度是各層循環次數乘積,如雙重嵌套為?O(n^2),三層為?O(n^3)
    • 次數不固定循環:像二分查找,復雜度為?O(logn)。
  3. 分析遞歸函數
    • 確定深度:如階乘遞歸調用?N?次,復雜度為?O(N)。
    • 考慮操作數:每次遞歸操作數固定,復雜度是深度與單次操作數乘積;若操作數與規模有關,如斐波那契遞歸,復雜度為?O(2^n)
  4. 處理條件語句if-else?本身?O(1),若分支操作復雜度不同,取最壞情況,如一支?O(1)?一支?O(n),整體為?O(n)。
  5. 確定整體復雜度:取復雜度最高部分作為整體復雜度。
4.2.2?判斷空間復雜度的方法
  1. 找基本變量:如簡單數據類型(intfloatchar?等)變量占用固定空間,復雜度為?O(1)。
  2. 數據結構
    • 一維數組:數組的空間復雜度取決于數組的大小。若一維數組大小為?n,復雜度?O(n)。
    • 二維數組:int arr[m][n],復雜度?O(mn)。
    • 其他:鏈表、樹、圖等,依節點數和單節點空間定,如鏈表節點數為?n?時復雜度?O(n)。
  3. 遞歸函數
    • 調用棧:遞歸深度為?n?且每次遞歸占固定空間,復雜度?O(n)。
    • 數據結構:若遞歸函數中創建了數據結構,需結合結構空間和遞歸深度考量。
  4. 函數調用
    • 參數:函數參數若為大型數據結構或數組等,需考慮其占用空間對整體空間復雜度的影響。若傳遞的是指針,則通常只占固定空間,空間復雜度為 O(1)。
    • 返回值:若函數返回大型數據結構,返回值占用的空間也應計入空間復雜度。
  5. 整體復雜度:取代碼各部分中復雜度最高的作為整體復雜度。

5. 練習

5.1 消失的數字

一個數組包含從0到n的所有整數,但其中缺了一個。請編寫代碼在O(n)時間內找出缺失的整數。

分析:“O(n)時間”意味著不能使用嵌套循環等。

示例:求和法

#include <stdio.h>int missingNumber(int* nums, int numsSize)
{int i = 0;int sum1 = 0;int sum2 = 0;for (i = 0; i < numsSize; i++){sum1 += nums[i];}sum2 = (numsSize * (numsSize + 1)) / 2;return sum2 - sum1;
}int main()
{int nums[] = { 9,6,4,2,3,5,7,0,1 };int size = sizeof(nums) / sizeof(nums[0]);int missnum = missingNumber(nums, size);printf("missnum=%d\n", missnum);return 0;
}

5.2 輪轉數組

給定一個整數數組,將數組中的元素向右輪轉?k?個位置。

示例:環狀替換

?
#include <stdio.h>void swap(int *a, int *b) 
{int temp = *a;*a = *b;*b = temp;
}// 向右輪轉數組函數
void rotate(int* nums, int numsSize, int k) 
{k = k % numsSize;// 處理 k > 數組長度的情況。避免不必要的重復輪轉int count = 0;for (int start = 0; count < numsSize; start++) {int current = start;// current 用于標記當前正在處理的元素的索引int prev = nums[start];do    {int next = (current + k) % numsSize;// 計算當前元素要移動到的新位置的索引// 取模確保索引在數組范圍內int temp = nums[next];nums[next] = prev;prev = temp;      // 更新 prev 為新位置的原來值current = next;   // 更新 current 為新位置的索引count++;} while (start != current);   // 回到起始位置時,說明當前的環狀替換完成,退出內層循環}
}int main() 
{int nums[] = {1, 2, 3, 4, 5, 6, 7};int numsSize = sizeof(nums) / sizeof(nums[0]);int k = 3;rotate(nums, numsSize, k);for (int i = 0; i < numsSize; i++) {printf("%d ", nums[i]);}printf("\n");return 0;
}?
  • 索引 :0→3→6→2→5→1→4→0
  • 元素 :1→4→7→3→6→2→5→1

  • 時間復雜度:O(n)(?n?是數組長度)
  • 空間復雜度:O(1)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/897006.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/897006.shtml
英文地址,請注明出處:http://en.pswp.cn/news/897006.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一文讀懂Redis分布式鎖

引言 在當今互聯網時代&#xff0c;分布式系統已成為大規模應用的主流架構。然而&#xff0c;這種架構中多個服務同時對共享資源的操作可能導致并發問題&#xff0c;如數據不一致和資源爭用。有效管理這些并發訪問&#xff0c;確保共享資源的安全性顯得尤為重要。 分布式鎖作…

23種設計模式一覽【設計模式】

文章目錄 前言一、創建型模式&#xff08;Creational Patterns&#xff09;二、結構型模式&#xff08;Structural Patterns&#xff09;三、行為型模式&#xff08;Behavioral Patterns&#xff09; 前言 設計模式是軟件工程中用來解決特定問題的一組解決方案。它們是經過驗證…

極狐GitLab 17.9 正式發布,40+ DevSecOps 重點功能解讀【三】

GitLab 是一個全球知名的一體化 DevOps 平臺&#xff0c;很多人都通過私有化部署 GitLab 來進行源代碼托管。極狐GitLab 是 GitLab 在中國的發行版&#xff0c;專門為中國程序員服務。可以一鍵式部署極狐GitLab。 學習極狐GitLab 的相關資料&#xff1a; 極狐GitLab 官網極狐…

elk的相關的基礎

以下是關于ELK&#xff08;Elasticsearch, Logstash, Kibana&#xff09;的200個基礎問題及其答案&#xff0c;涵蓋了ELK的核心概念、組件、配置、使用場景、優化等方面。 ?Elasticsearch 基礎 ?**什么是Elasticsearch&#xff1f;**? 答&#xff1a;Elasticsearch是一個分…

Beyond Compare for mac v5.0.6.30713 文件對比利器 支持M、Intel芯片

Mac毒搜集到的Beyond Compare是一套超級的文件及文件夾(目錄)的比較工具&#xff0c;不僅可以快速比較出兩個目錄的不同&#xff0c;還可以比較每個文件的內容&#xff0c;而且可以任意顯示比較結果。 應用介紹 程序內建了文件瀏覽器&#xff0c;方便您對文件、文件夾、壓縮包…

ProfibusDP主站轉ModbusTCP網關如何進行數據互換

ProfibusDP主站轉ModbusTCP網關如何進行數據互換 在現代工業自動化領域&#xff0c;通信協議的多樣性和復雜性不斷增加。Profibus DP作為一種經典的現場總線標準&#xff0c;廣泛應用于工業控制網絡中&#xff1b;而Modbus TCP作為基于以太網的通信協議&#xff0c;因其簡單易…

python代碼注釋方式

在 Python 中&#xff0c;注釋是用于解釋代碼、提高代碼可讀性和可維護性的重要工具。Python 支持兩種主要的注釋方式&#xff1a;單行注釋和多行注釋。此外&#xff0c;Python 還支持文檔字符串&#xff08;docstrings&#xff09;&#xff0c;用于為模塊、函數、類和方法提供…

【雜談】信創電腦華為w515(統信系統)登錄鎖定及忘記密碼處理

華為w515麒麟芯片版&#xff0c;還有非麒麟芯片版本&#xff0c;是一款信創電腦&#xff0c;一般安裝的UOS系統。 準備一個空U盤&#xff0c;先下載鏡像文件及啟動盤制作工具&#xff0c;連接如下&#xff1a; 百度網盤 請輸入提取碼 http://livecd.uostools.com/img/apps/l…

數據結構秘籍(四) 堆 (詳細包含用途、分類、存儲、操作等)

1 引言 什么是堆&#xff1f; 堆是一種滿足以下條件的樹&#xff1a;&#xff08;樹這一篇可以參考我的文章數據結構秘籍&#xff08;三&#xff09;樹 &#xff08;含二叉樹的分類、存儲和定義&#xff09;-CSDN博客&#xff09; 堆中的每一個結點值都大于等于&#xff08…

#define GBB_DEPRECATED_MSG(msg) __declspec(deprecated(msg))

這個宏 #define GBB_DEPRECATED_MSG(msg) __declspec(deprecated(msg)) 是用來在 C++ 中標記某些函數、變量或者代碼元素為已棄用(deprecated)的,并附帶一個自定義的棄用消息。 具體解釋: __declspec(deprecated(msg)): __declspec 是 Microsoft Visual C++ (MSVC) 的擴展…

服務器數據恢復—raid5陣列中硬盤掉線導致上層應用不可用的數據恢復案例

服務器數據恢復環境&故障&#xff1a; 某公司一臺服務器&#xff0c;服務器上有一組由8塊硬盤組建的raid5磁盤陣列。 磁盤陣列中2塊硬盤的指示燈顯示異常&#xff0c;其他硬盤指示燈顯示正常。上層應用不可用。 服務器數據恢復過程&#xff1a; 1、將服務器中所有硬盤編號…

全網獨家:zabbixV7版本容器服務器無法訪問Postgres V17數據庫的問題解決

近期將zabbix平臺從V6.2.6升級到7.2.4&#xff0c;遇到問題“PostgresoL server is not available. Waiting 5seconds”&#xff0c;容器無法訪問Postgres V17數據庫&#xff0c;本文記錄問題解決過程。 一、系統環境 1、數據庫版本 數據庫版本&#xff1a;postgres-17.4-tim…

進程控制 ─── linux第15課

目錄 進程控制 1.進程創建 (fork前面講過了) 寫時拷貝 進程終止 進程退出場景 退出碼 進程終止方法 進程控制 1.進程創建 (fork前面講過了) 在linux中fork函數時非常重要的函數&#xff0c;它從已存在進程中創建一個新進程。新進程為子進程&#xff0c;而原進程為父…

常見的網絡協議介紹

一、什么是網絡協議 指的是通信雙方的數據發送和接收順序&#xff0c;數據的封裝規則。 通俗解釋&#xff1a;描述雙方發送和接收的每個字節是按照什么規則。 二、TCP/IP體系的常用協議 (一)應用層 HTTP&#xff1a;超文本協議&#xff1b;指的是用來傳輸文本網頁的協議&#…

Hive-07之企業級調優

????????hive的企業級調優 1、Fetch抓取 Fetch抓取是指&#xff0c;Hive中對某些情況的查詢可以不必使用MapReduce計算 例如&#xff1a;select * from score;在這種情況下&#xff0c;Hive可以簡單地讀取employee對應的存儲目錄下的文件&#xff0c;然后輸出查詢結果…

華為云 | 快速搭建DeepSeek推理系統

DeepSeek&#xff08;深度求索&#xff09;作為一款國產AI大模型&#xff0c;憑借其高性能、低成本和多模態融合能力&#xff0c;在人工智能領域崛起&#xff0c;并在多個行業中展現出廣泛的應用潛力。 如上所示&#xff0c;在華為云解決方案實踐中&#xff0c;華為云提供的快速…

Spring Boot 3 整合 MinIO 實現分布式文件存儲

引言 文件存儲已成為一個做任何應用都不可回避的需求。傳統的單機文件存儲方案在面對大規模數據和高并發訪問時往往力不從心&#xff0c;而分布式文件存儲系統則提供了更好的解決方案。本篇文章我將基于Spring Boot 3 為大家講解如何基于MinIO來實現分布式文件存儲。 分布式存…

3月5日作業

代碼作業&#xff1a; #!/bin/bash# 清空目錄函數 safe_clear_dir() {local dir"$1"local name"$2"if [ -d "$dir" ]; thenwhile true; doread -p "檢測到 $name 目錄已存在&#xff0c;請選擇操作&#xff1a; 1) 清空目錄內容 2) 保留目…

達夢數據庫關于參數PK_WITH_CLUSTER的改動分析

目錄 1、PK_WITH_CLUSTER取值為0 2、PK_WITH_CLUSTER取值為1 達夢數據庫的參數PK_WITH_CLUSTER在最近使用過程中發現與前期使用的版本存在差異&#xff0c;特此測試分析一下。具體哪個版本改動的暫未得知。 PK_WITH_CLUSTER&#xff0c;默認值為0&#xff0c;動態會話級參數。…

android11使用gpio口控制led狀態燈

目錄 一、簡介 二、解決方法 A、底層驅動 B、上層調用 C、驗證 一、簡介 1、需求&#xff1a;這里是用2個gpio口來控制LED燈&#xff0c;開機時默認亮藍燈&#xff0c;按開機鍵&#xff0c;休眠亮紅燈&#xff0c;喚醒亮藍燈。 原理圖&#xff1a; 這里由于主板上電阻R63…