<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
如何衡量一個演演算法的好壞呢?比如對於以下斐波那契數列:
long long Fib(int N) { if (N < 3) return 1; return Fib(N - 1) + Fib(N - 2); }
斐波那契數列的遞迴實現方式非常簡潔,但簡潔一定好嗎?那該如何衡量其好與壞呢?
演演算法在編寫成可執行程式後,執行時需要耗費時間資源和空間(記憶體)資源 。因此衡量一個演演算法的好壞,一般是從時間和空間兩個維度來衡量的,即時間複雜度和空間複雜度。
時間複雜度主要衡量一個演演算法的執行快慢,而空間複雜度主要衡量一個演演算法執行所需要的額外空間。在計算機發展的早期,計算機的儲存容量很小。所以對空間複雜度很是在乎。但是經過計算機行業的迅速發展,計算機的儲存容量已經達到了很高的程度。所以我們如今已經不需要再特別關注一個演演算法的空間複雜度。
時間複雜度的定義:在電腦科學中,演演算法的時間複雜度是一個函數,它定量描述了該演演算法的執行時間。一個演演算法執行所耗費的時間,從理論上說,是不能算出來的,只有你把你的程式放在機器上跑起來,才能知道。但是我們需要每個演演算法都上機測試嗎?是可以都上機測試,但是這很麻煩,所以才有了時間複雜度這個分析方式。一個演演算法所花費的時間與其中語句的執行次數成正比例,演演算法中的基本操作的執行次數,為演演算法的時間複雜度。
即:找到某條基本語句與問題規模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("%dn", count); }
Func1 執行的基本操作次數 : F(N) = N^2 + 2*N + 10
代入數位計算一下:
N = 10 F(N) = 130
N = 100 F(N) = 10210
N = 1000 F(N) = 1002010
可以發現當N越來越大的時候,數位的大小主要取決於N^2了。
實際中我們計算時間複雜度時,我們其實並不一定要計算精確的執行次數,而只需要大概執行次數,那麼這裡我們使用大O的漸進表示法。
大O符號(Big O notation):是用於描述函數漸進行為的數學符號。
推導大O階方法:
使用大O的漸進表示法以後,Func1的時間複雜度為: O(N^2)
N = 10 F(N) = 100
N = 100 F(N) = 10000
N = 1000 F(N) = 1000000
通過上面我們會發現大O的漸進表示法去掉了那些對結果影響不大的項,簡潔明瞭的表示出了執行次數。
另外有些演演算法的時間複雜度存在最好、平均和最壞情況:
例如:在一個長度為N陣列中搜尋一個資料x
在實際中一般情況關注的是演演算法的最壞執行情況,所以陣列中搜尋資料時間複雜度為O(N)
範例1
計算Func2的時間複雜度:
void Func2(int N) { int count = 0; for (int k = 0; k < 2 * N; ++k) { ++count; } int M = 10; while (M--) { ++count; } printf("%dn", count); }
基本操作執行了2*N + 10次,而通過推導大O階方法
用常數1取代加法常數,得到2*N + 1
只保留最高階項,得到2*N
將最高階項的係數變為1,得到N
所以最後的時間複雜度是O(N)
範例2:計算Func3的時間複雜度
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("%dn", count); }
時間複雜度為O(N+M)
範例3:計算Func4的時間複雜度
void Func4(int N) { int count = 0; for (int k = 0; k < 100; ++k) { ++count; } printf("%dn", count); }
用常數1替代100,時間複雜度是O(1)
範例4:計算strchr的時間複雜度
const char* strchr(const char* str, int character) { while (*str != character) { str++; } return str; }
最快執行了1次,最慢執行了N次,所以時間複雜度是O(N)
範例5:計算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; } }
第一趟氣泡排序了N - 1次,第二趟氣泡排序了N - 2次,依次類推,排序這個基本操作在最壞的情況下一共執行了(N*(N+1)/2次,而最好的情況下是陣列已經排好了,此時只需要執行N次,時間複雜度取最壞的情況,所以是O(N^2)
範例6:計算BinarySearch的時間複雜度
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; else return mid; } return -1; }
假如有序陣列有N個數,那麼查詢一次就會將陣列的範圍縮小一半,直到最後只剩下一個數
可以這麼用數位表示:
N / 2 / 2 / 2 / 2 / 2 / 2 ...... / 2 / 2 = 1
假設查詢了x次,也就是每次將陣列縮小一半(除以2)這個基本操作執行了x次,那麼這個x與N之間的關係是2^x = N
那麼x = logN,這裡預設底數為2
所以時間複雜度是O(logN)
範例7:計算階乘遞迴Fac的時間複雜度
long long Fac(size_t N) { if (0 == N) return 1; return Fac(N - 1) * N; }
基本操作遞迴了N次,所以時間複雜度為O(N)
範例8:計算斐波那契遞迴Fib的時間複雜度
long long Fib(size_t N) { if (N < 3) return 1; return Fib(N - 1) + Fib(N - 2); }
基本操作遞迴了約為2^N次,根據推到大O階的方法,所以最後的時間複雜度為O(N)
空間複雜度也是一個數學表示式,是對一個演演算法在執行過程中臨時佔用儲存空間大小的量度
空間複雜度不是程式佔用了多少bytes的空間,因為這個也沒太大意義,所以空間複雜度算的是變數的個數。空間複雜度計算規則基本跟實踐複雜度類似,也使用大O漸進表示法。
注意:函數執行時所需要的棧空間(儲存引數、區域性變數、一些暫存器資訊等)在編譯期間已經確定好了,因此空間複雜度主要通過函數在執行時候顯式申請的額外空間來確定。
範例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; } }
可見,紅框標註的地方,是在函數的內部額外建立了4個變數,也就是開闢了常數個額外空間,所以空間複雜度為O(1)
範例2:計算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個sizeof(long long)大小的空間,所以空間複雜度為O(N)
範例3:計算階乘遞迴Fac的空間複雜度
long long Fac(size_t N) { if (N == 0) return 1; return Fac(N - 1) * N; }
遞迴呼叫了N次,開闢了N個棧幀,每個棧幀使用了常數個空間,所以空間複雜度為O(N)
範例4:計算Fibonacci的空間複雜度
long long Fib(size_t N) { if (N < 3) return 1; return Fib(N - 1) + Fib(N - 2); }
每一次遞迴呼叫時,每兩個子函數用的函數棧幀空間都是同一個,所以只額外開闢了N個棧幀,空間複雜度為O(N)
連結://img.jbzj.com/file_images/article/202207/202207081035315
題目描述:陣列nums包含從0到n的所有整數,但其中缺了一個。請編寫程式碼找出那個缺失的整數。你有辦法在O(n)時間內完成嗎?
範例 1:
輸入:[3,0,1]
輸出:2
範例 2:
輸入:[9,6,4,2,3,5,7,0,1]
輸出:8
這裡給出時間複雜度都為O(N)的思路
連結://img.jbzj.com/file_images/article/202207/202207081035316
題目描述:給你一個陣列,將陣列中的元素向右輪轉 k 個位置,其中 k 是非負數。
範例 1:
輸入: nums = [1,2,3,4,5,6,7], k = 3
輸出: [5,6,7,1,2,3,4]
解釋:
向右輪轉 1 步: [7,1,2,3,4,5,6]
向右輪轉 2 步: [6,7,1,2,3,4,5]
向右輪轉 3 步: [5,6,7,1,2,3,4]
範例 2:
輸入:nums = [-1,-100,3,99], k = 2
輸出:[3,99,-1,-100]
解釋:
向右輪轉 1 步: [99,-1,-100,3]
向右輪轉 2 步: [3,99,-1,-100]
這裡給出3種思路:
void reverse(int* nums, int left, int right) { while (left < right) { int tmp = nums[left]; nums[left] = nums[right]; nums[right] = tmp; ++left; --right; } } void rotate(int* nums, int numsSize, int k){ k %= numsSize; reverse(nums, 0, numsSize - 1 - k); reverse(nums, numsSize - k, numsSize - 1); reverse(nums, 0, numsSize - 1); }
到此這篇關於C語言演演算法的時間複雜度和空間複雜度的文章就介紹到這了,更多相關C語言時間複雜度內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45