什麼是數據結構?html
指數據元素之間的關係。這些關係能夠分爲:算法
集合微信
線性結構數據結構
樹形結構函數
網狀結構。spa
邏輯結構分爲: 線性結構 和 非線性結構。設計
集合:除了同屬一個對象外不存在相互關係。如:汽車上的人除了同輛車彼此間無其餘關係。指針
線性結構:元素間爲嚴格的一對一關係,即一個元素有且只有一個前驅。如:成績表中一個學生一個成績code
樹形結構:元素之間爲嚴格的一對多關係,即一個元素有且只有一個前驅,但能夠多個後繼。如家譜,行政組織htm
網狀結構:元素之間存在多對多的關係,比較複雜。如:微信朋友圈
如何描述數據結構?
使用二元組來描述數據結構
Data_structure = (D,R)
D 是元素的有限集
R 是D上全部元素的關係有限集
下面舉一些例子
線性結構能夠用二元組表示爲:
linear = (D,R) D = {A,B,C,D,E,F,G,H} R = {r} r = {<A,B>,<B,C>,<C,D>,<D,E>,<E,F>,<F,G>,<G,H>}
樹形結構能夠用二元組表示爲:
tree = (D,R) D = {A,B,C,D,E,F,G,H,I,J} R = {r} r = {<A,B>,<A,C>,<A,D>,<B,E>,<B,F>,<C,G>,<C,H>,<C,I>,<D,G>}
網狀結構能夠用二元組表示爲:
nets = (D,R) D = {1,2,3,4,5} R = {r} r = {(1,2),(1,3),(1,4),(2,3),(2,4),(2,5),(3,4),(3,5),(4,5)}
尖括號表示有向線,圓括號表示無向線。
將上面的尖括號或圓括號的元素用線連一下即是對應的形狀了。
數據結構如何存儲?
順序存儲結構:數據元素依次存儲在一組連續存儲單元當中,數據邏輯關係由存儲單元的位置直接體現。
鏈式存儲結構:數據元素存儲在任意存儲單元當中,而附加的指針域表示元素之間的邏輯關係
程序 = 算法 + 數據結構
數據結構離不開算法,下面說說算法
什麼是算法?
算法具備五個特性:
有窮性:一個算法必須在有窮的執行步驟後結束,每步均可以在有窮的時間內完成。
肯定性:算法中每條指令必須明確,任何狀況下對於相同輸入都有相同的輸出。
可行性:算法中描述的操做都可以用基本運算的有限執行次數來實現。
輸入:一個算法有0個或者多個輸入,這些輸入是某個特定對象的合集
輸出:一個算法至少有一個輸出,與輸入有着某些特定關係的量。
如何設計算法?
正確性:除了語法正確,邏輯上也正確,能達到預期的目,最好能夠驗證結果是否正確。
可讀性:方便閱讀,交流,改進
健壯性:輸入非法參數,算法能及時給出錯誤答案,並輸出錯誤提示,同時終止程序。
效率與存儲:執行的時間越短越好,使用的空間越少越好,雖然時間和空間總算矛盾的
算法的時間複雜度
過後統計:將不一樣的算法編製成程序,而後比較這些程序執行的時間。
事前估算:用數學方法對算法效率直接分析,經常使用此方法來判斷時間複雜度。
事前估算須要考慮的因數:
1.問題的規模,排序10個數字比排序100個數字時間短
2.編寫的語言,越高級的語言執行的效率越低。
3.機器的速度,80386和i7的速度沒得比
4.算法自己的策略。
因爲2,3因數須要考慮到硬件,軟件(編譯器)等因數,所以用絕對的機器運行時間來衡量算法
是不科學的,因此須要拋開這些因數,主要研究時間效率與算法所處理的問題規模n的函數關係
當隨着問題規模n增大時,算法執行時間的增加率與f(n)的增加率相同,稱爲算法的漸進時間複雜度。記做:
T(n) = O(f(n))
執行時間 == 執行次數(假設執行一條語句所用的時間相同),另外下面的時間複雜度都以最壞的狀況考慮
下面是常見的時間複雜度:
常數階 O(1)
對數階 O(logn)
線性階 O(n)
平方階 O(n^2)
立方階 O(n^3)
指數階 O(2^n)
階數階 O(n!)
爆炸階 O(n^n)
下面兩段代碼是累加求和
1 int sum1(){ 2 int i,sum=0,n=100; #執行了1次 3 for(i=0;i<n;i++) #執行了n+1次 4 sum += i; #執行了n次 5 }
高斯算法
1 int sum2(){ 2 int n,sum=0; #執行了1次 3 sum = n(n+1)/2; #執行了1次 4 }
上面兩段代碼中 n 是問題的規模。
第一個算法執行次數爲:1 + n+1 + n = 2n+2
隨着問題規模n的增長,執行次數以線性增長,因此時間複雜度爲O(n)
第二個算法執行次數爲:1 + 1 = 2
隨着問題規模n的增長,執行次數始終爲2,因此時間複雜度爲O(1)
因此時間複雜度計算規則爲:
1.若是執行的次數是常數,與規模n無關,則爲O(1)
2.若是執行的次數是屢次多項式,取最高項,如:n^3 + n^2 則爲O(n^3)
3.若是執行的次數是含有係數和常數,能夠忽略係數和常數,如: 2n^2 + 3 則爲O(n^2)
有了上面的法則,來分析下下面這些算法的時間複雜度,n均表明問題規模。
1 #define n 10
2 int multiMatrix(int a[n][n],b[n][n],c[n][n]){ 3 for(i=0;i<=n;i++) #執行了n+1次 4 for(j=0;j<n;j++){ #執行了(n+1)*n次 5 c[i][j] = 0; #執行了n^2
6 for(k=0;k<n;k++) #執行了n^2*(n+1)次 7 c[i][j] = c[i][j] +a[i][k] * b[k][i]; #執行了n^3
8
9 } 10 }
T(n) = 2n^3 + 3n^2 + 2n + 1 因此時間複雜度爲O(n^3)
1 int fun(){ 2 i=1;n=100; 3 while(i<n){ 4 i = i * 2; 5 } 6 }
假設上面的算法執行了x次後中止。則有 2^x >= n,因此 x = log(2)n,因此時間複雜度爲O(logn)
1 void prime(n){ 2 i=2; 3 while( (n%i)!=0 && i*1.0<sqrt(n) ) #若是不能被整除,且小於根號n,則繼續執行 4 i++; 5
6 if(i*1.0>sqrt(n)) 7 printf("%d是個素數\n",n); 8 else
9 printf("%d不是素數\n",n); 10
11 }
函數做用判斷n是否爲素數,原理就是遍歷開區間(1,n)間的數,而後看看遍歷的數可否被整除。
事實上若是 n^1/2 (根號n)前不存在一個數能被n整除,那麼n^1/2(根號n)以後的數也不會被n整除
這是由於對稱性。所以只須要判斷 n^1/2(根號n)以前的數字是否存在一個能被n整除的數便可。
因此隨着問題規模n的增長,執行的次數增加率與 f(n)=n^1/2 的漸進增加率相同,因此T(n)=O(n^1/2)
後面還有幾道具備挑戰的題目,這裏先講講空間複雜度:
算法的空間複雜度
指算法對運算所須要的輔助工做單元和存儲爲實現計算所需信息輔助性的空間大小,記做:
S(n) = O(f(n));
這個大小不包括輸入數據佔用的空間,大小是由具體的實際問題來決定的。
下面一個例子:判斷三個數字中,哪一個數最大
1 max(int a,b,c){ 2 if(a>b){ 3 if(a>c)return a; 4 else return c; 5 } 6 else{ 7 if(b>c)return b; 8 else return c; 9 } 10 }
隨着問題規模增大,執行的次數也增大,時間複雜度爲O(n)
1 max(int a[3]){ 2 c=a[0]; 3 for(i=0;i<3;i++) 4 if(a[i]>c)c=a[i]; 5
6 return c; 7 }
隨着問題規模增大,執行的次數也增大,時間複雜度也爲O(n)
上面兩個例子時間複雜度相同都爲O(n),但第一個算法無需額外的空間,而第二個算法須要兩個變量輔助。
因此第一個算法空間複雜度優於第二個算法,可是當問題規模不是3 而是100的時候,相信沒人會用第一個
算法,由於會寫到手殘。
歡迎各位轉載,請指明出處:http://www.cnblogs.com/demonxian3/p/7075441.html
最後來幾個練習題,評論附上答案
選擇題
1 int fun(int n){ 2 i=1,s=1; 3 while(s<n) 4 s += ++i; 5
6 return i; 7 }
A. O(n/2) B. O(logn) C. O(n) D.O(n^1/2)
for(int i=0;i<m;i++) for(int j=0;j<n;i++) a[i][j] = i*j;
A. O(m^3) B. O(n^2) C. O(m*n) D.O(m+n)
計算題,設n爲整數求下面的時間複雜度
(1)
1 i=1,j=0; 2 while(i+j<n) 3 if(i>j) j=j+1; 4 else i=i+1;
(2)
1 x=91,y=100; 2 while(y>0) 3 if(x>100){ 4 x=x-10; 5 y=y-1; 6 } 7 else
8 x=x+1;
(3)
1 x=n 2 while(x>=(y+1)*(y+1)) 3 y=y+1;