写给你的数据结构教程(第一天)
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
引言
我想通過這個教程,使得讀者不需要寫太多代碼,便能理解數(shù)據(jù)結(jié)構(gòu)的概念和重要性。
很多算法是運(yùn)行在特殊的數(shù)據(jù)結(jié)構(gòu)之上的,可以說數(shù)據(jù)結(jié)構(gòu)和算法是相輔相成的,有句出自某書的非常經(jīng)典的話是這樣說的:
Algorithms Plus Data Structures Equals Programs
即算法加數(shù)據(jù)結(jié)構(gòu)等于程序。注意這里是程序,不是軟件。軟件還有工程性在其中。
但是我依然希望能夠不講太多的算法,因?yàn)椤八惴ā碧?#xff0c;通過學(xué)習(xí)大量算法來學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的方式有些舍本逐末。
我會盡量從構(gòu)造和設(shè)計的角度來闡述數(shù)據(jù)結(jié)構(gòu)。
構(gòu)造即我們手上有什么,怎么利用這些去制造我們需要的結(jié)構(gòu)。
設(shè)計即我們的目的是什么,為了達(dá)成這個目的我們需要什么結(jié)構(gòu)。
在內(nèi)存里存儲數(shù)據(jù)
在C語言中,我們要處理的數(shù)據(jù),都存儲在內(nèi)存中,即便是硬盤中某個文件里存放的小說,要對其內(nèi)容進(jìn)行處理,我們依然需要將其加載到內(nèi)存中,再接著進(jìn)行后續(xù)的處理。
當(dāng)我們在C語言里定義一個變量,諸如:
int?o;在這句代碼之后,我們便可以操作這個變量。
我們可以往里面存放一個整數(shù),那這個整數(shù)有多大呢?使用
printf("%d",sizeof(int));這句代碼,可以輸出int型變量在其運(yùn)行設(shè)備上的大小。如果你在你的筆記本電腦上編譯運(yùn)行,這個大小應(yīng)該是4,即4bytes,換算成二進(jìn)制位(bit)即4*8=32bits。
我們可以利用sizeof測出所有基本類型的大小。這樣就有很多不同的盒子去放數(shù)據(jù)了。
內(nèi)存里的數(shù)據(jù)看起來怎么樣
上面我們創(chuàng)建了一個int型變量o。在內(nèi)存里看起來是這樣的:
當(dāng)然現(xiàn)在什么都沒有,因?yàn)樵诙x的時候并未初始化,我們也沒放任何東西進(jìn)去?,F(xiàn)在存點(diǎn)東西進(jìn)去:
o?=?11;這下變成這樣了:
最后我們顯示出這個數(shù)據(jù)存儲單元在內(nèi)存中的位置(變量地址):
printf("%d",&o);現(xiàn)在我們什么都有了:
移動數(shù)據(jù)
先看看這樣一個程序:
#include?<stdio.h> void?showcopies(int?copies) {printf("copies?%d\n",&copies); } int?main(void) {int?o;o?=?11;printf("original?%d\n",&o);showcopies(o);return?0; }在這個程序里我們定義了一個函數(shù),并且利用參數(shù)傳遞,將變量o“移動”到了showcopies這個函數(shù)中,并且“改名”叫做copies。
主函數(shù)main里輸出了o這個變量的地址,showcopies里輸出了移動后的o的地址。
最后的結(jié)果是什么樣呢?可以試著運(yùn)行看看。
我去泡杯咖啡喝……
如果你在程序里加入了輸出變量o和變量copies的值的語句,你會更加肯定,被“移動”的只有變量o的值而已。
也就是說,當(dāng)我們進(jìn)行參數(shù)傳遞的時候,其實(shí)是在新的一塊內(nèi)存區(qū)域里開辟一個同樣大小的空間,然后把作為參數(shù)傳遞的變量的內(nèi)存空間里的數(shù)據(jù)復(fù)制到新的空間中。
一組數(shù)據(jù)
數(shù)組(Array)這個名字其實(shí)有些呆,因?yàn)楣饪粗形拿o人的感覺就是一組數(shù),諸如(1,2,3)這樣的,但事實(shí)上還可以是('a','b','c')。我傾向于把Array理解為一列數(shù)據(jù)或者一組數(shù)據(jù)。
運(yùn)行上面的程序,就可以看到(圖畫的我累,所以這里不畫了=。=)這樣的結(jié)果:
2686736
2686740
2686744
這三個地址便是數(shù)組m里三個依次相鄰元素的地址。因?yàn)槊總€int變量占據(jù)4bytes,所以這三個int變量的地址是依次相差4(bytes)。數(shù)據(jù)里的數(shù)據(jù),在內(nèi)存里是連續(xù)存放的。
這樣的好處在于訪問會特別快,我們只需要知道第一個變量的地址,就能通過向后移動一定的位置來讀取另一個變量。
正如我們在程序里使用的訪問方式一樣,[]括號里加入索引號,便能訪問,在實(shí)際的程序運(yùn)行中,這種直接訪問的速度也非??臁?/p>
移動一組數(shù)據(jù)或自定義數(shù)據(jù)類型
當(dāng)我們嘗試在函數(shù)調(diào)用的時候傳遞數(shù)組(Array)或者自定義的數(shù)據(jù)類型(結(jié)構(gòu)體)時,會出現(xiàn)一種例外。
因?yàn)檫@兩類數(shù)據(jù)在內(nèi)存里占用的空間可能會很大,所以全部復(fù)制到一個新空間里實(shí)在不是很劃算的一件事。甚至多數(shù)時候我們不需要一個新的備份,只需要在原來的數(shù)據(jù)上修改就行。
所以對于這兩類數(shù)據(jù),C語言里使用指針(Pointer)來操作。
int?m[3]; int?*first_element_address; first_element_address?=?&m[0]; printf("%d\n",*(first_element_address+1)==m[1]);當(dāng)調(diào)用函數(shù)時,會生成傳遞參數(shù)的指針變量,再將這個指針變量傳遞過去。
至于驗(yàn)證方法,可以利用移動數(shù)據(jù)一段中的程序來檢驗(yàn)。作業(yè)1。
/*?Cheatsheet?for?using?pointer?in?C?*/ /*?生成指針?*/ pointer?=?&variable;? /*?用指針讀取指向變量的值,再與指向變量的值進(jìn)行相等判斷?*/ *pointer?==?variable;? /*?當(dāng)pointer指向一塊連續(xù)數(shù)據(jù)區(qū)域時,例如數(shù)組?*/ *(pointer+index)?==?array[index];來,我們來實(shí)現(xiàn)一個可變數(shù)組
先看看C語言的數(shù)組,有兩個明細(xì)的缺陷:
如果訪問數(shù)組的時候下標(biāo)(索引值)越界,會發(fā)生錯誤。
數(shù)組的大小是固定的。
因此我們要設(shè)計一個用起來更好的,比如擁有如下特性:
可以檢查是否越界
可變大小
數(shù)組的數(shù)據(jù)在內(nèi)存中皆是連續(xù)存放的,因此我們需要一個用于存放連續(xù)數(shù)據(jù)的區(qū)域。
作業(yè)2
參考
http://en.wikipedia.org/wiki/Algorithms_%2B_Data_Structures_%3D_Programs
《Linux C編程一站式學(xué)習(xí)》?宋勁杉
轉(zhuǎn)載于:https://my.oschina.net/sooshian/blog/221101
總結(jié)
以上是生活随笔為你收集整理的写给你的数据结构教程(第一天)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cocos2d-x之读取plist文件
- 下一篇: Office 365系列之十二:Acti