摘要:實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶圖的鄰接表等等。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。
算法的復(fù)雜度:
1.算法在編寫成可執(zhí)行程序后,運(yùn)行 時(shí)需要耗費(fèi)時(shí)間資源和空間(內(nèi)存)資源 。因此衡量一個(gè)算法的好壞,一般是從時(shí)間和空間兩個(gè)維度來衡量的,即時(shí)間復(fù)雜度和空間復(fù)雜度。
2.時(shí)間復(fù)雜度主要衡量一個(gè)算法的運(yùn)行快慢,而空間復(fù)雜度主要衡量一個(gè)算法運(yùn)行所需要的額外空間。在計(jì)算機(jī)發(fā)展的早期,計(jì)算機(jī)的存儲(chǔ)容量很小。所以對(duì)空間復(fù)雜度很是在乎。但是經(jīng)過計(jì)算機(jī)行業(yè)的迅速發(fā)展,計(jì)算機(jī)的存儲(chǔ)容量已經(jīng)達(dá)到了很高的程度。所以我們?nèi)缃褚呀?jīng)不需要再特別關(guān)注一個(gè)算法的空間復(fù)雜度。
時(shí)間復(fù)雜度的定義:
在計(jì)算機(jī)科學(xué)中,算法的時(shí)間復(fù)雜度是一個(gè)函數(shù),它定量描述了該算法的運(yùn)行時(shí)間。一個(gè)算法執(zhí)行所耗費(fèi)的時(shí)間,從理論上說,是不能算出來的,只有你把你的程序放在機(jī)器上跑起來,才能知道。但是我們需要每個(gè)算法都上機(jī)測(cè)試嗎?是可以都上機(jī)測(cè)試,但是這很麻煩,所以才有了時(shí)間復(fù)雜度這個(gè)分析方式。一個(gè)算法所花費(fèi)的時(shí)間與其中語句的執(zhí)行次數(shù)成正比例,算法中的基本操作的執(zhí)行次數(shù),為算法的時(shí)間復(fù)雜度。
實(shí)際中我們計(jì)算時(shí)間復(fù)雜度時(shí),我們其實(shí)并不一定要計(jì)算精確的執(zhí)行次數(shù),而只需要大概執(zhí)行次數(shù),那么這里我們使用大O的漸進(jìn)表示法。
大O符號(hào)(Big O notation):是用于描述函數(shù)漸進(jìn)行為的數(shù)學(xué)符號(hào)。 推導(dǎo)大O階方法:
1、用常數(shù)1取代運(yùn)行時(shí)間中的所有加法常數(shù)。
2、在修改后的運(yùn)行次數(shù)函數(shù)中,只保留最高階項(xiàng)。
3、如果最高階項(xiàng)存在且不是1,則去除與這個(gè)項(xiàng)目相乘的常數(shù)。得到的結(jié)果就是大O階。
在實(shí)際中一般情況關(guān)注的是算法的最壞運(yùn)行情況,所以數(shù)組中搜索數(shù)據(jù)時(shí)間復(fù)雜度為O(N)。
void Func1(int N){ int count = 0; for (int k = 0; k < 2 * N; ++k) { ++count; } int M = 10; while (M--) { ++count; } printf("%d/n", count);}
基本操作執(zhí)行了2N+10次,通過推導(dǎo)大O階方法知道,時(shí)間復(fù)雜度為 O(N)
void Func2(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);}
基本操作執(zhí)行了M+N次,有兩個(gè)未知數(shù)M和N,時(shí)間復(fù)雜度為 O(N+M)
void Func4(int N){ int count = 0; for (int k = 0; k < 100; ++ k) { ++count; } printf("%d/n", count);}
基本操作執(zhí)行了10次,通過推導(dǎo)大O階方法,時(shí)間復(fù)雜度為 O(1)
// 計(jì)算strchr的時(shí)間復(fù)雜度?const char * strchr ( const char * str, int character );
基本操作執(zhí)行最好1次,最壞N次,時(shí)間復(fù)雜度一般看最壞,時(shí)間復(fù)雜度為 O(N)
// 計(jì)算BubbleSort的時(shí)間復(fù)雜度?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; }}
基本操作執(zhí)行最好N次,最壞執(zhí)行了(N*(N+1)/2次,通過推導(dǎo)大O階方法+時(shí)間復(fù)雜度一般看最
壞,時(shí)間復(fù)雜度為 O(N^2)
// 計(jì)算BinarySearch的時(shí)間復(fù)雜度?int BinarySearch(int* a, int n, int x){ assert(a); int begin = 0; int end = n - 1; while (begin < end) { int mid = begin + ((end - begin) >> 1); if (a[mid] < x) begin = mid + 1; else if (a[mid] > x) end = mid; else return mid; } return -1;}
操作執(zhí)行最好1次,最壞O(logN)次,時(shí)間復(fù)雜度為 O(logN) ps:logN在算法分析中表示是底數(shù)為2,對(duì)數(shù)為N。有些地方會(huì)寫成lgN。(折紙查找計(jì)算出來)
// 計(jì)算階乘遞歸Fac的時(shí)間復(fù)雜度?long long Fac(size_t N){ if(0 == N) return 1; return Fac(N-1)*N;}
基本操作遞歸了N次,時(shí)間復(fù)雜度為O(N)。
8
// 計(jì)算斐波那契遞歸Fib的時(shí)間復(fù)雜度?long long Fib(size_t N){ if(N < 3) return 1; return Fib(N-1) + Fib(N-2);}
基本操作遞歸了2N次,時(shí)間復(fù)雜度為O(2N)。
空間復(fù)雜度也是一個(gè)數(shù)學(xué)表達(dá)式,是對(duì)一個(gè)算法在運(yùn)行過程中臨時(shí)占用存儲(chǔ)空間大小的量度 。
空間復(fù)雜度不是程序占用了多少bytes的空間,因?yàn)檫@個(gè)也沒太大意義,所以空間復(fù)雜度算的是變量的個(gè)數(shù)。空間復(fù)雜度計(jì)算規(guī)則基本跟實(shí)踐復(fù)雜度類似,也使用大O漸進(jìn)表示法。
注意:函數(shù)運(yùn)行時(shí)所需要的棧空間(存儲(chǔ)參數(shù)、局部變量、一些寄存器信息等)在編譯期間已經(jīng)確定好了,因此空間復(fù)雜度主要通過函數(shù)在運(yùn)行時(shí)候顯式申請(qǐng)的額外空間來確定。
// 計(jì)算BubbleSort的空間復(fù)雜度?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; }}
使用了常數(shù)個(gè)額外空間,所以空間復(fù)雜度為 O(1)
// 計(jì)算Fibonacci的空間復(fù)雜度?// 返回斐波那契數(shù)列的前n項(xiàng)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;}
動(dòng)態(tài)開辟了N個(gè)空間,空間復(fù)雜度為 O(N)
// 計(jì)算階乘遞歸Fac的空間復(fù)雜度?long long Fac(size_t N){ if (N == 0) return 1; return Fac(N - 1)*N;}
遞歸調(diào)用了N次,開辟了N個(gè)棧幀,每個(gè)棧幀使用了常數(shù)個(gè)空間。空間復(fù)雜度為O(N)
線性表(linear list)是n個(gè)具有相同特性的數(shù)據(jù)元素的有限序列。 線性表是一種在實(shí)際中廣泛使用的數(shù)據(jù)結(jié)
構(gòu),常見的線性表:順序表、鏈表、棧、隊(duì)列、字符串…線性表在邏輯上是線性結(jié)構(gòu),也就說是連續(xù)的一條直線。但是在物理結(jié)構(gòu)上并不一定是連續(xù)的,線性表在物理上存儲(chǔ)時(shí),通常以數(shù)組和鏈?zhǔn)浇Y(jié)構(gòu)的形式存儲(chǔ)。
順序表:
鏈表:
順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu),一般情況下采用數(shù)組存儲(chǔ)。在數(shù)組
上完成數(shù)據(jù)的增刪查改。
靜態(tài)順序表:
#define N 10typedef int SLDataType;//方便更改存儲(chǔ)類型typedef struct SeqList{ SLDataType arr[N];//定長(zhǎng)數(shù)組 int size;//有效數(shù)據(jù)個(gè)數(shù)}SeqList;
動(dòng)態(tài)順序表:
typedef int SLDataType;//方便更改存儲(chǔ)類型typedef struct SeqList{ SLDataType* arr;//指向動(dòng)態(tài)開辟的數(shù)字 int size;//有效數(shù)據(jù)個(gè)數(shù) int capicity;// 容量空間大小}SeqList;
靜態(tài)順序表只適用于確定知道需要存多少數(shù)據(jù)的場(chǎng)景。靜態(tài)順序表的定長(zhǎng)數(shù)組導(dǎo)致N定大了,空間開多了浪
費(fèi),開少了不夠用。所以現(xiàn)實(shí)中基本都是使用動(dòng)態(tài)順序表,根據(jù)需要?jiǎng)討B(tài)的分配空間大小,所以下面我們實(shí)
現(xiàn)動(dòng)態(tài)順序表。
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈
接次序?qū)崿F(xiàn)的 。
注意:
1.鏈表的存儲(chǔ)在邏輯上連續(xù),在物理上不一定連續(xù)
2.結(jié)點(diǎn)一般從堆上申請(qǐng)
1.單項(xiàng)或雙向鏈表
2.帶頭或不帶頭鏈表
3.循環(huán)或非循環(huán)鏈表
雖然有這么多的鏈表的結(jié)構(gòu),但是我們實(shí)際中最常用還是兩種結(jié)構(gòu)
1.無頭單向不循環(huán)鏈表(OJ題基本都是)
2.帶頭雙項(xiàng)循環(huán)鏈表
無頭單向非循環(huán)鏈表:結(jié)構(gòu)簡(jiǎn)單,一般不會(huì)多帶帶用來存數(shù)據(jù)。實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等。另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多。
帶頭雙向循環(huán)鏈表:結(jié)構(gòu)最復(fù)雜,一般用在多帶帶存儲(chǔ)數(shù)據(jù)。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。另外這個(gè)結(jié)構(gòu)雖然結(jié)構(gòu)復(fù)雜,但是使用代碼實(shí)現(xiàn)以后會(huì)發(fā)現(xiàn)結(jié)構(gòu)會(huì)帶來很多優(yōu)勢(shì),實(shí)現(xiàn)反而 簡(jiǎn)單了。
不同點(diǎn)
1.存儲(chǔ)空間上:
順序表:物理上一定連續(xù)
鏈表:邏輯上連續(xù),物理上不一定連續(xù)2.隨機(jī)訪問:
順序表:支持 O(1)
鏈表:不支持O(N)
3.任意位置的插入或刪除:
順序表:可能需要搬移元素,效率低 O(N)
鏈表:只需要修改指針
4.插入:
順序表:動(dòng)態(tài)表可以擴(kuò)容
鏈表:需要的時(shí)候在棧上申請(qǐng)
5.應(yīng)用場(chǎng)景:
順序表:元素高效存儲(chǔ)和頻繁訪問
鏈表:任意位置插入刪除頻繁
6.緩存利用率
順序表:高
鏈表:低
附上鏈表高頻面試題詳情解析,方便大家理解與使用鏈表
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/125661.html
摘要:我們可以使用鏈表這種數(shù)據(jù)結(jié)構(gòu),來刪除元素的時(shí)候而不必讓后面的元素向前移動(dòng)。一個(gè)節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)稱為它的前驅(qū),下一個(gè)節(jié)點(diǎn)即指向的節(jié)點(diǎn)稱為它的后繼節(jié)點(diǎn),在簡(jiǎn)單的單向鏈表中,第一個(gè)節(jié)點(diǎn)稱為頭節(jié)點(diǎn)它沒有前驅(qū)節(jié)點(diǎn),最后一個(gè)節(jié)點(diǎn)沒有后繼節(jié)點(diǎn)為。 之前我們用數(shù)組的方式來實(shí)現(xiàn)了隊(duì)列,是否還記得在出隊(duì)列后有這樣一段代碼: for (i = 0; i < this.length - 1; i++) { ...
摘要:概述集合類主要有大分支,及。不能保證元素的排列順序,順序有可能發(fā)生變化不是同步的集合元素可以是但只能放入一個(gè)是接口的唯一實(shí)現(xiàn)類,可以確保集合元素處于排序狀態(tài)。如果這兩個(gè)的通過比較返回,新添加的將覆蓋集合中原有的,但不會(huì)覆蓋。 概述 Java集合類主要有2大分支,Collection及Map。Collection體系如下: https://upload-images.jianshu......
摘要:時(shí)間年月日星期日說明本文部分內(nèi)容均來自慕課網(wǎng)。當(dāng)對(duì)應(yīng)的鏈表存在時(shí),從左側(cè)插入數(shù)據(jù)。從右側(cè)插入數(shù)據(jù)。當(dāng)系統(tǒng)在定時(shí)持久化之前出現(xiàn)宕機(jī),還未來得及往硬盤寫入數(shù)據(jù),那數(shù)據(jù)就丟失了。當(dāng)數(shù)據(jù)集過大時(shí),可能會(huì)導(dǎo)致服務(wù)器停止幾百毫秒甚至是秒鐘。 時(shí)間:2017年05月21日星期日說明:本文部分內(nèi)容均來自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)示例源碼:無個(gè)人學(xué)習(xí)源碼:https:...
閱讀 3735·2023-01-11 11:02
閱讀 4244·2023-01-11 11:02
閱讀 3050·2023-01-11 11:02
閱讀 5181·2023-01-11 11:02
閱讀 4737·2023-01-11 11:02
閱讀 5534·2023-01-11 11:02
閱讀 5313·2023-01-11 11:02
閱讀 3990·2023-01-11 11:02