03_07:今天學(xué)習(xí)的是閉包:什么是閉包?閉包就是有函數(shù)+捕獲的變量,那什么是閉包表達(dá)式?其實(shí)定義函數(shù)有2種方式,第一就是func關(guān)鍵字,另一個(gè)就是閉包表達(dá)式,它也是一個(gè)函數(shù)。什么叫自動(dòng)閉包?那就是autoclosurce這個(gè)就是在定義函數(shù)的時(shí)候,你加上autoclosurce這個(gè)關(guān)鍵字,然后你傳參數(shù)的時(shí)候,它會(huì)自動(dòng)的把參數(shù)變成閉包表達(dá)式。閉包捕獲一個(gè)變量的時(shí)候,就會(huì)調(diào)用malloc向堆申請(qǐng)內(nèi)存,然后堆返回的內(nèi)存都是16的倍數(shù),每個(gè)變量前16個(gè)自己是固定的,前八是存儲(chǔ)類(lèi)型數(shù)據(jù),接下來(lái)的8個(gè)是引用計(jì)數(shù),捕獲一個(gè)變量申請(qǐng)一次,2個(gè)變量就申請(qǐng)2次,不是說(shuō)2個(gè)變量的話就是前面的16個(gè)字節(jié),再加上2個(gè)變量哦,不是這樣的,這2個(gè)變量的內(nèi)存是分別申請(qǐng)的,不在一起的;在堆申請(qǐng)的內(nèi)存是隨機(jī)的,而且在堆申請(qǐng)的到的內(nèi)存都是臟的,要自己初始化,如果是在閉包里使用了全局變量,那就不會(huì)向堆申請(qǐng)內(nèi)存?為什么呢,因?yàn)槿肿兞烤褪窃谡麄€(gè)運(yùn)行周期都存在的哦,捕獲臨時(shí)變量的時(shí)候向堆申請(qǐng)內(nèi)存,就是為了保住臨時(shí)變量的生命周期啊,所以全局變量的話就不會(huì)向堆申請(qǐng)內(nèi)存。全局變量在的地址一般都比較小的哦,因?yàn)樵谧钌厦?,然后堆的一般?x10xx這樣的,然后棧的話就比較大了,函數(shù)調(diào)用的時(shí)候會(huì)在棧開(kāi)辟內(nèi)存給他,存它的數(shù)據(jù)。rax寄存器一般拿來(lái)存放函數(shù)的返回值,cdi,rdi,r8,r9這種就是拿來(lái)放函數(shù)的參數(shù)的。把一個(gè)函數(shù)賦值給一個(gè)變量時(shí),這個(gè)變量存有什么數(shù)據(jù)呢?首先是有它返回的那個(gè)函數(shù)的指針,還有一個(gè)就是堆的內(nèi)存地址,調(diào)用函數(shù)里面的函數(shù)時(shí),其實(shí)是傳遞了2個(gè)參數(shù),那個(gè)捕獲的變量也當(dāng)參數(shù)傳給函數(shù)的。rip是一個(gè)變化的地址,就是它所在的那行的下面的地址,如果一個(gè)函數(shù)里有2個(gè)臨時(shí)變量,然后有2個(gè)函數(shù)都是用到了這2個(gè)臨時(shí)變量,這2變量都會(huì)在堆那里分別申請(qǐng)一塊內(nèi)存,然后2個(gè)函數(shù)共享這個(gè)2個(gè)數(shù)據(jù)。如果函數(shù)里面的臨時(shí)變量沒(méi)被函數(shù)捕獲,那它們就是在棧區(qū),跟隨函數(shù)的生命周期,函數(shù)調(diào)用結(jié)束,函數(shù)棧里的數(shù)據(jù)就會(huì)銷(xiāo)毀了哦,那2個(gè)臨時(shí)變量就沒(méi)有了,如果被函數(shù)捕獲了,放堆區(qū)了,就不會(huì)這樣銷(xiāo)毀,那就是根據(jù)它的引用計(jì)數(shù)來(lái)決定是否銷(xiāo)毀了。函數(shù),方法是不占用對(duì)象的內(nèi)存的,因?yàn)楹瘮?shù)放在代碼區(qū),函數(shù)調(diào)用的時(shí)候會(huì)傳參數(shù)和對(duì)象本身進(jìn)來(lái)去調(diào)用,如果2個(gè)對(duì)象分別調(diào)用同一個(gè)方法,那么它們使用的函數(shù)指針肯定是一樣的,因?yàn)槎际钦{(diào)用代碼區(qū)的方法,都是一份,所以指針地址是一樣的。閉包表達(dá)式會(huì)有點(diǎn)滯后性哦。lldb指令:si,register read exa,bt;寄存器有些是用來(lái)裝函數(shù)參數(shù)的,有些是用來(lái)做函數(shù)函數(shù)返回值的,如果你想找到一塊剛申請(qǐng)好的內(nèi)存,那你就去找malloc這個(gè)方法后面一行的寄存器,打印它的地址,它肯定就是剛申請(qǐng)的堆的內(nèi)存地址。
03_08:今天學(xué)了什么?屬性,下標(biāo),繼承,屬性分為存儲(chǔ)屬性,計(jì)算屬性,存儲(chǔ)屬性是占用對(duì)象的內(nèi)存的,也就是說(shuō)如果一個(gè)對(duì)象在堆申請(qǐng)了內(nèi)存,那存儲(chǔ)屬性是占用內(nèi)存空間的,計(jì)算屬性就不用占對(duì)象的內(nèi)存空間,下標(biāo)其實(shí)就是一個(gè)方法,函數(shù),不過(guò)它的聲明是叫subscript,反正本質(zhì)也是函數(shù),也有參數(shù),也有返回值,其實(shí)我也沒(méi)想明白這個(gè)東西到底有什么用,反正就拿來(lái)當(dāng)函數(shù)用吧,反正具體也沒(méi)想到什么使用場(chǎng)景。繼承的話,首先存儲(chǔ)屬性是可以繼承的,然后子類(lèi)通過(guò)父類(lèi)集成的屬性,也在子類(lèi)占用空間的哦,如果是存儲(chǔ)屬性,那可以通過(guò)繼承,然后重寫(xiě)為計(jì)算屬性,計(jì)算屬性也還是只能重寫(xiě)為計(jì)算屬性,只讀屬性可以重寫(xiě)為讀寫(xiě)屬性,讀寫(xiě)屬性不可以重寫(xiě)為只讀屬性,通過(guò)繼承然后重寫(xiě)父類(lèi)的方法,需要在方法前面加override這個(gè)關(guān)鍵字,可以在重寫(xiě)的時(shí)候給屬性添加屬性檢測(cè)器哦,類(lèi)型方法如果用class修飾的話,子類(lèi)可以繼承,如果用static修飾的話,子類(lèi)不可以繼承,因?yàn)閟tatic限定了作用于就在當(dāng)前類(lèi)。
03_09:今天學(xué)了什么呢?多態(tài),初始化,可選鏈,協(xié)議,元類(lèi)型等,今天學(xué)的東西好多啊,我都記不住了,今天都是講語(yǔ)言的,多態(tài),父類(lèi)指針指向子類(lèi)對(duì)象,其實(shí)多態(tài)的底層實(shí)現(xiàn)就跟c++的虛表一樣的,首先調(diào)用方法的時(shí)候會(huì)傳入類(lèi)對(duì)象的指針,然后其實(shí)類(lèi)對(duì)象的指針指向的是堆里面的類(lèi)對(duì)象的內(nèi)容,所以可以拿到它的地址值,然后根據(jù)內(nèi)存排布我們知道它的前8位存放了類(lèi)型信息的地址,然后我們就可以找到了存放類(lèi)型信息的內(nèi)存,然后通過(guò)看匯編我們找到了是通過(guò)類(lèi)型信息內(nèi)存的首地址+偏移量然后就找到了函數(shù)的地址,然后就可以調(diào)用函數(shù)了,函數(shù)都是依次排布的,然后函數(shù)有當(dāng)前類(lèi)的,也有從父類(lèi)繼承來(lái)的,有了地址,然后就可以調(diào)用了。初始化就要就是有兩個(gè)初始化器,一個(gè)叫指定初始化器,一個(gè)叫便利初始化器,然后它們的規(guī)則就是,便利初始化器只能調(diào)用當(dāng)前類(lèi)的其他便利初始化器,只能橫向調(diào)用,并且最終都會(huì)調(diào)用制定初始化器,然后制定初始化器再調(diào)用它的直接父類(lèi)的初始化器,然后一直往上,直到?jīng)]有父類(lèi)了為止,整個(gè)初始化過(guò)程分為2歌階段,第一個(gè)階段就是從子類(lèi)到父類(lèi),便利初始化器調(diào)用當(dāng)前類(lèi)的制定初始化器,然后再調(diào)用制定初始化器,不過(guò)要先把當(dāng)前類(lèi)的存儲(chǔ)屬性都初始化,然后再調(diào)用父類(lèi)的指定初始化器,然后再把父類(lèi)的存儲(chǔ)屬性初始化,然后再往上,直到最上的類(lèi),然后都是先完成自己的存儲(chǔ)屬性初始化然后再往上走,然后到了基類(lèi)后,這個(gè)時(shí)候所有的存儲(chǔ)屬性都初始化完了,這個(gè)時(shí)候就完成了第一階段,這個(gè)時(shí)候再?gòu)纳贤伦?,這個(gè)時(shí)候就可以重新做自己定制的內(nèi)容了,有可以使用self,也可以給其他屬性賦值了。所謂可選鏈就是可選類(lèi)型的時(shí)候,一定要用?訪問(wèn),然后一定是可選類(lèi)型有值了才可以繼續(xù)往下走,只要其中一個(gè)可選類(lèi)型是nil,那整個(gè)鏈就斷了,不會(huì)再往下走了。協(xié)議(protocol)這個(gè)就是定義了一些方法,然后枚舉,結(jié)構(gòu)體,類(lèi)都可以通過(guò)遵循這些協(xié)議,然后可以得到這些方法,實(shí)現(xiàn)這些方法,swift的規(guī)定就是你遵循了就要全部實(shí)現(xiàn),不能像oc那樣,有些可以實(shí)現(xiàn),有些不實(shí)現(xiàn)。其中其實(shí)還有很多小細(xì)節(jié)的,我記不清了,這個(gè)就到時(shí)候用到的時(shí)候再查找吧。元類(lèi)型,其實(shí)就是存儲(chǔ)在堆里類(lèi)型對(duì)象前8個(gè)字節(jié)那里存儲(chǔ)的東西,就是類(lèi)型信息,medadata就是這個(gè)東西了,跟oc的isa指針差不多意思吧反正
03_10:今天學(xué)了什么呢?好像學(xué)了錯(cuò)誤處理,字符串,數(shù)組的底層實(shí)現(xiàn);錯(cuò)誤處理其實(shí)就是error的處理,其實(shí)有2種方式處理,第一就是throw這個(gè)錯(cuò)誤,就是把錯(cuò)誤往上級(jí)函數(shù)拋,讓上級(jí)調(diào)用函數(shù)處理,然后一直拋到給系統(tǒng),其實(shí)這樣就會(huì)最后崩潰,其實(shí)我感覺(jué)這個(gè)沒(méi)啥意思,我還不如自己不處理,也是照樣崩潰的,第二就是用do catch 來(lái)處理,就是有錯(cuò)誤了,然后用catch來(lái)接這個(gè)錯(cuò)誤,然后做相應(yīng)的處理,這樣就可以了,這樣至少不會(huì)崩潰。字符串的底層存儲(chǔ)實(shí)現(xiàn)其實(shí)就是如果字符串長(zhǎng)度小于15個(gè)字節(jié)的話,字符串的數(shù)據(jù)就會(huì)直接存儲(chǔ)在字符串變量的內(nèi)存中,直接用ascii的形式存儲(chǔ),如果大于15個(gè)字節(jié)的話,就會(huì)把字符實(shí)際的內(nèi)容存儲(chǔ)在常量區(qū),字符常量,這個(gè)就是在編譯期的時(shí)候就確定的東西,然后在字符串變量里面后八個(gè)字節(jié)存儲(chǔ)這個(gè)地址,然后通過(guò)地址我們就可以找到字符串?dāng)?shù)據(jù),如果對(duì)字符串進(jìn)行拼接的話,就會(huì)開(kāi)辟新的內(nèi)存空間,也就是會(huì)開(kāi)辟堆空間來(lái)存儲(chǔ)字符數(shù)據(jù),在變量里還是存儲(chǔ)這個(gè)堆空間的地址,通過(guò)這個(gè)地址字符數(shù)據(jù);然后數(shù)組的話,它是通過(guò)結(jié)構(gòu)體實(shí)現(xiàn)的,然后是一個(gè)值類(lèi)型的,不過(guò)它的底層實(shí)現(xiàn)其實(shí)是引用類(lèi)型的,也就是說(shuō)它的數(shù)據(jù)其實(shí)也是存儲(chǔ)在堆空間的,當(dāng)給數(shù)組添加數(shù)據(jù)的時(shí)候,它也是會(huì)調(diào)用malloc方法,開(kāi)辟堆空間的內(nèi)存來(lái)存儲(chǔ)數(shù)組數(shù)據(jù),而且它的大小還會(huì)動(dòng)態(tài)改變,隨著你添加數(shù)據(jù)的個(gè)數(shù),會(huì)動(dòng)態(tài)增大,都是用16的備注增加的,然后它會(huì)在前面的空間存儲(chǔ)一些類(lèi)型信息,引用計(jì)數(shù)之類(lèi)的,后面才開(kāi)始存儲(chǔ)數(shù)據(jù)。字符綁定,什么叫字符綁定,其實(shí)就是動(dòng)態(tài)庫(kù)的,動(dòng)態(tài)庫(kù)在加載到內(nèi)存的時(shí)候是放在最下面的,內(nèi)存地址也是最大的,然后剛開(kāi)始的時(shí)候其實(shí)它的函數(shù)地址是不知道的,然后編譯器會(huì)先給他一個(gè)固定的函數(shù)地址,反正是假的函數(shù)地址,等到你真的用到它,需要加載它的時(shí)候就會(huì)出現(xiàn)了字符綁定,然后通過(guò)開(kāi)始那個(gè)假地址,通過(guò)各種各種,然后最后找到那個(gè)真實(shí)的函數(shù)地址,這個(gè)時(shí)候它就會(huì)更新存儲(chǔ)在常量區(qū)之前的那個(gè)假的地址,這個(gè)時(shí)候常量代碼區(qū)存儲(chǔ)的才是真的動(dòng)態(tài)庫(kù)函數(shù)地址,如果你這個(gè)時(shí)候再調(diào)用一次剛才那個(gè)函數(shù)的話,你就會(huì)發(fā)現(xiàn)它的地址變了,變成了真正的函數(shù)地址了,跟蹤匯編的話,你會(huì)發(fā)現(xiàn)之前是很多匯編代碼調(diào)用的,現(xiàn)在都沒(méi)有了,通過(guò)那個(gè)地址直接找到了函數(shù),因?yàn)樽址壎ê笏辛苏娴暮瘮?shù)地址,可以直接調(diào)用了哦,還有一個(gè)就是如果你需要定義一些方法的話,其實(shí)直接用struct定義比用類(lèi)定義好,為什么?因?yàn)橛媒Y(jié)構(gòu)體定義的方法,調(diào)用的時(shí)候是直接調(diào)用的,callq,你看匯編就能看得出來(lái),用類(lèi)的話,還有很多事情要做,還有通過(guò)地址找到堆空間,然后再偏移找到函數(shù)的地址,它這個(gè)函數(shù)地址實(shí)現(xiàn)差不多跟c++的虛表一樣,反正整個(gè)流程下來(lái)是比較久的,結(jié)構(gòu)體的話什么都不用,直接callq 調(diào)用,很快的哦,記?。?/p>
03_11:今天學(xué)了什么呢?可選項(xiàng)本質(zhì),運(yùn)算符重載,擴(kuò)展,訪問(wèn)控制,內(nèi)存管理;可選項(xiàng)的本質(zhì)其實(shí)就是枚舉,它就是有兩個(gè)case,一個(gè)叫.none,一個(gè)叫.some,然后這個(gè).some還是支持泛型的;運(yùn)算符重載就是你可以對(duì)現(xiàn)有的運(yùn)算符進(jìn)行自定義實(shí)現(xiàn),比如實(shí)現(xiàn)兩個(gè)類(lèi)對(duì)象的加法之類(lèi)的;擴(kuò)展,類(lèi),結(jié)構(gòu)體,枚舉都可以使用擴(kuò)展,擴(kuò)展就是擴(kuò)展方法,函數(shù),存儲(chǔ)屬性不能擴(kuò)展,因?yàn)榇鎯?chǔ)屬性是占用對(duì)象內(nèi)存的,這個(gè)在初始化的時(shí)候就已經(jīng)決定了,所以不能通過(guò)擴(kuò)展區(qū)更改;訪問(wèn)控制就是像訪問(wèn)權(quán)限之類(lèi)的,有5個(gè)等級(jí),open:當(dāng)前模塊和其他模塊都可以訪問(wèn),并且可以重載和繼承,public是當(dāng)前模塊和其他模塊可以訪問(wèn),但是不能重載和繼承;internal,就是當(dāng)前模塊可以訪問(wèn),一般類(lèi)型前面沒(méi)有寫(xiě)的默認(rèn)就是這個(gè)權(quán)限;fileprivate就是當(dāng)前定義的文件可以訪問(wèn);private這個(gè)就是定義的當(dāng)前作用域,也就是這個(gè)大括號(hào)里可以訪問(wèn);內(nèi)存管理其實(shí)跟oc的差不多,也是對(duì)堆的對(duì)象用引用計(jì)數(shù)進(jìn)行管理,還有自動(dòng)釋放池,關(guān)于循環(huán)引用就2種方案解決,一個(gè)是弱引用,這個(gè)是用在對(duì)象會(huì)有可能為nil的時(shí)候用的,這個(gè)就是對(duì)象引用計(jì)數(shù)為0后,會(huì)把它置為nil,然后它必須是var定義的,無(wú)主引用的話,是用在對(duì)象不為nil的時(shí)候,這個(gè)的話就是對(duì)象引用計(jì)數(shù)為0 了,它不會(huì)為nil,還是保存這個(gè)對(duì)象的內(nèi)存地址,所以這個(gè)時(shí)候如果還是被訪問(wèn)的話就會(huì)崩潰,提示壞地址訪問(wèn)。
03_12:學(xué)了啥?指針,字面量協(xié)議,模式匹配;指針就是地址值,不過(guò)我們?cè)趕wift中可以新建指針,也就是malloc分配內(nèi)存,然后返回地址,也就是指針,然后通過(guò)指針進(jìn)行操作,指針有2種類(lèi)型,也有可更改的,或者只讀的,通過(guò)指針去訪問(wèn)地址內(nèi)容,這個(gè)還是比較方便的呢;字面量協(xié)議就是只要你遵守了字面量的協(xié)議,你就可以通過(guò)字面量去初始化一個(gè)變量;模式匹配,這個(gè)東西好多的,反正就是到時(shí)候用到就知道了,反正基本也都能看懂吧反正,就這樣了。
03_13:今天學(xué)什么了?從oc到swift,從swift到oc;oc調(diào)用swift的話,其實(shí)xcode已經(jīng)默認(rèn)生成了一個(gè)targetname-swift橋接文件,不過(guò)如果是swift調(diào)用oc的話,需要自己新建一個(gè)targetname-bridge-header.h這樣的一個(gè)頭文件,然后oc把想給swift調(diào)用的文件的.h文件import進(jìn)去;如果swift的想給oc調(diào)用的話,首先需要繼承自nsobject?為什么一定要繼承自nsobject呢?首先是因?yàn)槿绻脒M(jìn)行方法調(diào)用的話,肯定要走runtime那套流程,要走這個(gè)流程最重要的東西是要有isa指針,要這個(gè)指針的話肯定是要繼承自nsobject的了。然后想暴露屬性,成員或者方法給oc調(diào)用的話,需要加上@objc,如果一個(gè)類(lèi)都想暴露出去的話,就在class前面加@objcmembers這個(gè)關(guān)鍵字。還有一點(diǎn)就是oc的方法調(diào)用就是走msg_send這個(gè)流程,swift方法調(diào)用就是走虛表這個(gè)流程,反正具體走那個(gè)流程就看他在那個(gè)環(huán)境里了。
03_23:今天學(xué)了什么呢?在flutter中,萬(wàn)物都是widget,首先入口函數(shù)main,runapp,然后在這里就要傳入一個(gè)widget,在flutter中首先會(huì)有一個(gè)materialapp這個(gè)是一個(gè)設(shè)計(jì)樣式,然后接下來(lái)就是一個(gè)scaffod,這個(gè)scaffod就相當(dāng)于iOS開(kāi)發(fā)中的viewcontroller,然后它可以有一個(gè)導(dǎo)航欄,一個(gè)body,還可以有一個(gè)底部導(dǎo)航欄,然后在widget的里面可以添加child的widget,這個(gè)就是一個(gè),然后多個(gè)的話用children,反正就是會(huì)形成一個(gè)樹(shù)狀的結(jié)構(gòu),然后widget有stateless和stateful2種的,stateless就是無(wú)狀態(tài)的,這個(gè)就是用在靜態(tài)界面的時(shí)候,就是說(shuō)里面的內(nèi)容都是靜態(tài)的,不會(huì)改變的,stateful用于有狀態(tài)改變的,然后它的狀態(tài)由一個(gè)stata子類(lèi)來(lái)管理的,stateless的它的生命周期就比較簡(jiǎn)單了,就是constructor和build方法兩個(gè),不過(guò)在stateful的就比較復(fù)雜了,首先是widget的constructor,然后再到它的createstate,然后再到state的constructor,initconstruct,然后在到build,在到dispose。在布局方面,其實(shí)也是flex布局的思想,縱軸的話就是colum,橫軸就是row,然后其實(shí)就是跟小程序那里的差不多啊,這個(gè)的元素是widget,小程序的是view,布局思想基本差不多的。必選參數(shù),位置可選參數(shù),命名可選參數(shù),初始化列表,在這里可以判斷或者給參數(shù)一個(gè)初始值或者參數(shù)的值的有無(wú)的判斷。在state子類(lèi)有變量改變需要更新頁(yè)面的時(shí)候,就是用setstate方法,這個(gè)底層就是會(huì)讓它最終調(diào)用build方法,其實(shí)就是有一個(gè)dirt的一個(gè)狀態(tài)管理,它有一個(gè)bool值來(lái)管理,需要更新渲染的時(shí)候是yes,就是用來(lái)標(biāo)記是否要調(diào)用build方法,如果你修改了變量,就能檢測(cè)到的哦。widget例如,text,button,listview,container,center,richtext,textspan,icon,image等等
03_24:今天學(xué)了什么呢?dio網(wǎng)絡(luò)請(qǐng)求的三方庫(kù),async,await異步請(qǐng)求,listview,gridview,wrap,flex布局,父widget像子widget傳遞數(shù)據(jù),第一步新建一個(gè)共享的數(shù)據(jù),然后把widget都包裹在它里面,然后發(fā)送通知,第三步就是放監(jiān)聽(tīng)者,swiper,輪播圖,
03_28:今天學(xué)了什么呢?
===如果在面試的時(shí)候遇到難題,我們有3種方法分析解決復(fù)雜的問(wèn)題:畫(huà)圖能使抽象問(wèn)題形象化,舉例使抽象問(wèn)題具體化,分解使復(fù)雜問(wèn)題簡(jiǎn)單化
===面試官除了希望應(yīng)聘者的代碼能夠完成基本的功能之外,還會(huì)關(guān)注應(yīng)聘者是否考慮了邊界問(wèn)題,特殊輸入*(比如null指針,空字符串等)以及錯(cuò)誤處理
===在介紹項(xiàng)目經(jīng)驗(yàn)的時(shí)候,應(yīng)聘者不必詳述項(xiàng)目的背景,而要突出介紹自己完成的工作以及取得的成績(jī)
===值類(lèi)型的實(shí)例在棧上分配內(nèi)存,而引用類(lèi)型的實(shí)例在堆上分配內(nèi)存
===其實(shí)查找的本質(zhì)就是找到一個(gè)切入點(diǎn),然后不斷的縮小查找的范圍,從而找到答案
===合并兩個(gè)數(shù)組或者字符串的時(shí)候,如果從前往后復(fù)制每個(gè)數(shù)字或者字符需要重復(fù)移動(dòng)數(shù)字或者字符多次,那么我們可以考慮從后往前復(fù)制,這樣就能減少移動(dòng)的次數(shù),從而提高效率
===二叉樹(shù)有很多特例,二叉搜索樹(shù)就是其中一個(gè),在二叉搜索樹(shù)中,左子節(jié)點(diǎn)總是小于或者等于根節(jié)點(diǎn),而右子節(jié)點(diǎn)總是大于或者等于根節(jié)點(diǎn)。我們可以平均在O(logn)的時(shí)間內(nèi)根據(jù)數(shù)值在二叉搜索樹(shù)中找到一個(gè)節(jié)點(diǎn);二叉樹(shù)的另外2個(gè)特例就是堆和紅黑樹(shù)。堆可以分為最大堆和最小堆,在最大堆中根節(jié)點(diǎn)的值最大,在最小堆中根節(jié)點(diǎn)的值最小,有很多需要快速查找到最大值或者最小值的問(wèn)題都可以用堆來(lái)解決。紅黑樹(shù)是把樹(shù)中的節(jié)點(diǎn)定義為紅黑兩種顏色,并通過(guò)規(guī)則確保從根節(jié)點(diǎn)到葉節(jié)點(diǎn)的最長(zhǎng)路徑的長(zhǎng)度不超過(guò)最短路徑的2倍。
===用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列;用兩個(gè)隊(duì)列實(shí)現(xiàn)一個(gè)棧
===有很多算法都可以用遞歸和循環(huán)兩種不同的方式實(shí)現(xiàn)。通?;谶f歸的實(shí)現(xiàn)方法代碼會(huì)比較簡(jiǎn)潔,但性能不如基于循環(huán)的實(shí)現(xiàn)方法。因?yàn)檫f歸的時(shí)候,如果數(shù)據(jù)長(zhǎng)度比較長(zhǎng)的話,會(huì)導(dǎo)致調(diào)用棧的層級(jí)很深有可能導(dǎo)致棧溢出的風(fēng)險(xiǎn)。
===位運(yùn)算可以看成是一類(lèi)特殊的算法,它是把數(shù)字表示成二進(jìn)制之后對(duì)0和1的操作。由于位運(yùn)算的對(duì)象為二進(jìn)制數(shù)字,所以不是很直觀,但是掌握它也不難,因?yàn)榭偣仓挥信c,或,異或,左移,右移5種位運(yùn)算
03_29:今天學(xué)了什么呢?
===如果面試題是要求在排序的數(shù)組(或者部分排序的數(shù)組,排序數(shù)組的旋轉(zhuǎn))中查找一個(gè)數(shù)字或者統(tǒng)計(jì)某個(gè)數(shù)字出現(xiàn)的次數(shù),我們都可以嘗試用二分查找算法
===實(shí)現(xiàn)快速排序算法的關(guān)鍵在于先在數(shù)組中選擇一個(gè)數(shù)字,接下來(lái)把數(shù)組中的數(shù)字分為2部分,比選擇的數(shù)字小的數(shù)字移動(dòng)到數(shù)組的左邊,比選擇的數(shù)字大的移動(dòng)到數(shù)組的右邊
===如果我們需要重復(fù)地多次計(jì)算相同的問(wèn)題,通??梢赃x擇用遞歸或者循環(huán)兩種不同的方法。遞歸是在一個(gè)函數(shù)內(nèi)部調(diào)用這個(gè)函數(shù)自身。而循環(huán)則是通過(guò)設(shè)置計(jì)算的初始值以及終止條件,在一個(gè)范圍內(nèi)重復(fù)運(yùn)算。
===遞歸是函數(shù)調(diào)用自身,而函數(shù)調(diào)用是有時(shí)間和空間的消耗的:每一次函數(shù)調(diào)用,都需要在內(nèi)存棧中分配空間以保存參數(shù),返回地址以及臨時(shí)變量,而往棧里壓入數(shù)據(jù)和彈出數(shù)據(jù)都需要時(shí)間,另外遞歸中有可能有很多計(jì)算都是重復(fù)的,從而很大的影響性能,最重要的遞歸還會(huì)有更嚴(yán)重的問(wèn)題:調(diào)用棧溢出,因?yàn)槊總€(gè)進(jìn)程的棧的容量都是有限的,當(dāng)遞歸的調(diào)用層級(jí)太多時(shí),就會(huì)超出棧的容量,從而導(dǎo)致調(diào)用棧溢出。
===斐波拉契數(shù)列的本質(zhì)就是后面的答案都是要依賴前面的答案的:f(n) = f(n-1)+f(n-2)
===所謂位運(yùn)算的異或:就是相同為0,不同為1
===把一個(gè)整數(shù)減去1之后再和原來(lái)的整數(shù)做位與運(yùn)算,得到的結(jié)果相當(dāng)于是把整數(shù)的二進(jìn)制表示中的最右邊一個(gè)1變成0,很多二進(jìn)制的問(wèn)題都可以用這個(gè)思路解決
===由于計(jì)算機(jī)表示小數(shù)(包括float和double類(lèi)型小數(shù))都有誤差,我們不能直接用==判斷兩個(gè)小數(shù)是否相等,如果兩個(gè)小數(shù)的差的絕對(duì)值很小,我們就認(rèn)為它們相等
===我們可以使用字符串或者數(shù)組來(lái)表示大數(shù)
===如果面試題是關(guān)于n位整數(shù)并且沒(méi)有限定n的取值范圍,或者是輸入任意大小的整數(shù),那么題目很有可能是需要考慮大數(shù)問(wèn)題的,字符串是一個(gè)簡(jiǎn)單,有效的表示大數(shù)的方法
===當(dāng)我們想刪除一個(gè)鏈表的節(jié)點(diǎn)的時(shí),并不一定要?jiǎng)h除這個(gè)節(jié)點(diǎn)本身,可以先把下一個(gè)節(jié)點(diǎn)的內(nèi)容復(fù)制出來(lái)覆蓋被刪除節(jié)點(diǎn)的內(nèi)容,然后把下一個(gè)節(jié)點(diǎn)刪除。其實(shí)關(guān)于鏈表的問(wèn)題一定要考慮的幾個(gè)問(wèn)題:1,頭指針為null 2,要操作的節(jié)點(diǎn)在開(kāi)頭 3,要操作的節(jié)點(diǎn)在結(jié)尾 4,整個(gè)鏈表就只有一個(gè)節(jié)點(diǎn),這幾個(gè)情況都是需要考慮的
===當(dāng)我們用一個(gè)指針遍歷不能解決問(wèn)題的時(shí)候,可以嘗試用兩個(gè)指針來(lái)遍歷鏈表,可以讓其中一個(gè)指針遍歷的速度快一些(比如一次在鏈表上走2步),或者讓它先在鏈表上走若干步
===代碼完整性:完成基本功能,考慮邊界條件,做好錯(cuò)誤處理
===代碼魯棒性:采取防御式編程,處理無(wú)效的輸入
03_30:今天學(xué)了什么呢?
===不管是廣度優(yōu)先遍歷一個(gè)有向圖還是一棵樹(shù),都要用到隊(duì)列。第一步我們把起始節(jié)點(diǎn)(對(duì)樹(shù)而言是跟節(jié)點(diǎn))放入隊(duì)列中,接下來(lái)每次從隊(duì)列的頭部取出一個(gè)節(jié)點(diǎn),遍歷這個(gè)節(jié)點(diǎn)之后把從它能達(dá)到的節(jié)點(diǎn)(對(duì)樹(shù)而言是子節(jié)點(diǎn))都依次放入隊(duì)列。我們重復(fù)這個(gè)遍歷過(guò)程,直到隊(duì)列中的節(jié)點(diǎn)全部被遍歷為止
===如果面試題是要求處理一顆二叉樹(shù)的遍歷序列,我們可以先找到二叉樹(shù)的根節(jié)點(diǎn),再基于根節(jié)點(diǎn)把整顆樹(shù)的遍歷序列拆分為左子樹(shù)對(duì)應(yīng)的子序列和右子樹(shù)對(duì)應(yīng)的子序列,接下來(lái)再遞歸地處理這兩個(gè)子序列
===如果面試題是按照一定要求擺放若干個(gè)數(shù)字,我們可以先求出這些數(shù)字的所有排列,然后再一一判斷每個(gè)排列是不是滿足題目給定的要求
===如果需要判斷多個(gè)字符是不是在某個(gè)字符串里出現(xiàn)過(guò)或者統(tǒng)計(jì)多個(gè)字符在某個(gè)字符串中出現(xiàn)的次數(shù),我們可以考慮基于數(shù)組創(chuàng)建一個(gè)簡(jiǎn)單的哈希表,這樣可以用很小的空間消耗換來(lái)時(shí)間效率的提升
===單向鏈表和棧:從鏈表的頭部開(kāi)始遍歷,然后把它的值壓棧,然后遍歷到最后的時(shí)候,其實(shí)在棧頂?shù)臄?shù)字就是鏈表的尾部的值;單向鏈表和棧的配合使用,我們可以找到鏈表的尾部
03_31:今天學(xué)了什么呢?
===異或運(yùn)算的性質(zhì):任何一個(gè)數(shù)字異或它自己都等于0。也就是說(shuō)我們從頭到尾一次異或數(shù)組中的每一個(gè)數(shù)字,最終的結(jié)果剛好是那個(gè)只出現(xiàn)一次的數(shù)字,因?yàn)槟切┏蓪?duì)出現(xiàn)的數(shù)字全部在異或中抵消了
===抽象建模的第一步是選擇合理的數(shù)據(jù)結(jié)構(gòu)來(lái)表述問(wèn)題,畢竟數(shù)據(jù)結(jié)構(gòu)只有有限的幾種而已;第二步就是分析模型中的內(nèi)在規(guī)律,并用編程語(yǔ)言表述這種規(guī)律。
===不使用新的變量,交換兩個(gè)變量的值;1,加減法 ?2,異或