数据结构与算法--代码鲁棒性案例分析
代碼魯棒性
- 魯棒是robust的音譯,就是健壯性。指程序能夠判斷輸入是否符合規(guī)范,對(duì)不合要求的輸入能夠給出合理的結(jié)果。
- 容錯(cuò)性是魯棒的一個(gè)重要體現(xiàn)。不魯棒的代碼發(fā)生異常的時(shí)候,會(huì)出現(xiàn)不可預(yù)測(cè)的異常,或者程序奔潰。
- 由于魯棒性非常重要,因此我們?cè)趯?xiě)代碼的時(shí)候,必須進(jìn)行防御性編程,這個(gè)必須成為我們編程的一種習(xí)慣,編碼過(guò)程中應(yīng)該能夠預(yù)見(jiàn)可能出現(xiàn)的問(wèn)題,并適當(dāng)處理。
最容易出錯(cuò)的雙指針操作
- 鏈表中倒數(shù)第K個(gè)節(jié)點(diǎn)
- 題目:輸入一個(gè)鏈表,輸出鏈表中倒數(shù)第k個(gè)結(jié)點(diǎn)。例如,鏈表依次是1,2,3,4,5,6,倒數(shù)第三個(gè)就是4。
- 我們依然用之前我們?cè)谥v解鏈表實(shí)現(xiàn)時(shí)候的鏈表對(duì)象定義
分析
- 此處題目要求的應(yīng)該是單向鏈表,因?yàn)殡p向鏈表沒(méi)有復(fù)雜度可言。為了得到倒數(shù)第K個(gè)節(jié)點(diǎn),并且不能從尾部遍歷,同樣的案例從尾部到頭打印單向鏈表,我們?cè)谥暗奈恼?#xff08;數(shù)據(jù)結(jié)構(gòu)與算法–鏈表實(shí)現(xiàn)以及應(yīng)用)中已經(jīng)有詳細(xì)的分析,此處同樣也可以利用這種方法。
- 以上方法中借助其他數(shù)據(jù)接口,棧實(shí)現(xiàn),時(shí)間復(fù)雜度是O(N+k),空間復(fù)雜度O(2n)。
- 應(yīng)該還有更加高效率的算法,我們還是從頭遍歷鏈表,假設(shè)有n個(gè)節(jié)點(diǎn),那么倒數(shù)第k個(gè)節(jié)點(diǎn)從頭開(kāi)始數(shù)是第n-k+1 個(gè)節(jié)點(diǎn)
- 如果我們能得到節(jié)點(diǎn)個(gè)數(shù)n大小,那么直接從頭遍歷n-k+1個(gè)就得到倒數(shù)第k個(gè)了 。
- 我們用雙指針實(shí)現(xiàn),利用兩個(gè)指針中間步數(shù)的差值來(lái)得到倒數(shù)第k個(gè),比如,我們需要n-k+1的差距,也就是n-(k-1)
- 當(dāng)指針A 走到末尾的時(shí)候,指針B需要走k-1 的距離,那么此時(shí)B指針指向的就是倒數(shù)第k個(gè)
- 例如倒數(shù)第2 個(gè),那么B就比A 少走了2-1 = 1 步驟,就是指向倒數(shù)第二個(gè)了。
- 如下圖:
- 如上圖一中P1走兩步,P2 則指向頭結(jié)點(diǎn)
- 接著繼續(xù)兩個(gè)指針P1, P2,一起向前走,
- 當(dāng)P1走到鏈表尾,則,P2,正好走到倒數(shù)第三位置。
- 如下diam實(shí)現(xiàn):
魯棒性分析
- 雙指針?lè)椒ㄖ行枰⒁獾狞c(diǎn)還是挺多的:
- 當(dāng)我們輸入head 結(jié)點(diǎn)為null時(shí)候回,由于代碼會(huì)范問(wèn)空指針內(nèi)存,次數(shù)程序會(huì)奔潰
- 輸入head為頭肩底的鏈表節(jié)點(diǎn)總數(shù)少于k個(gè),此時(shí),我們需要先走k-1 步驟,如果正好k-1個(gè)節(jié)點(diǎn),那么之后的步驟會(huì)拋出NPL,如果少于k-1個(gè)節(jié)點(diǎn),則此時(shí)就已經(jīng)npl
- 輸入?yún)?shù)k為0 的時(shí)候,此時(shí) k-1= -1,非法數(shù)值,-1 二進(jìn)制位符號(hào)位1 此時(shí)會(huì)讀成數(shù)據(jù)位,此時(shí)數(shù)據(jù)變?yōu)?294967295,此時(shí)for循環(huán)將會(huì)超過(guò)執(zhí)行次數(shù)。
相關(guān)問(wèn)題
- 問(wèn)題一:求單向鏈表中介節(jié)點(diǎn),奇數(shù)個(gè)返回中間節(jié)點(diǎn),偶數(shù)個(gè)返回中間兩個(gè)任意一個(gè),同樣雙指針A,B, A每個(gè)循環(huán)走一步,B每個(gè)循環(huán)走兩步,B到末尾,則A就在中間節(jié)點(diǎn)
- 問(wèn)題二:求解單向鏈表是否環(huán)形鏈表,同樣雙指針A,B,A走一步,B走兩步,如果到最后B追上了A 則環(huán)形,如果B到最后null,則不是環(huán)形
最容易死循環(huán)的鏈表問(wèn)題
- 題目:定義一個(gè)函數(shù),輸入鏈表頭結(jié)點(diǎn),反轉(zhuǎn)并輸出反轉(zhuǎn)后的鏈表頭結(jié)點(diǎn)。
分析
- 我們依然用 之前鏈表的實(shí)現(xiàn)文章中定義的鏈表節(jié)點(diǎn)如下:
- 鏈表反轉(zhuǎn)涉及到每個(gè)節(jié)點(diǎn)的指針操作,非常容易出現(xiàn)死循環(huán)問(wèn)題,為了正確理解整個(gè)過(guò)程,我們應(yīng)該首先借助圖形來(lái)直觀的分析。如下:
- 我一開(kāi)始還是想到雙指正方法,第一步驟分別指向錢(qián)兩個(gè)節(jié)點(diǎn),并且改變本節(jié)點(diǎn)的指針指向,我們此時(shí)需要知道的是,本節(jié)點(diǎn)信息,上一個(gè)節(jié)點(diǎn)信息,這里都符合,得到下一圖步驟。
- 但是此時(shí)我們無(wú)法循環(huán)到下一個(gè)節(jié)點(diǎn)3, 因此邏輯無(wú)法成立,我們需要借助第三個(gè)指針,P3,如下圖
- 如上,我們可以得到本節(jié)點(diǎn)信息,上一個(gè)節(jié)點(diǎn)信息,下一個(gè)節(jié)點(diǎn)信息,在經(jīng)過(guò)p2 指針的轉(zhuǎn)向后,我們無(wú)法通過(guò)P2.next得到下一個(gè)節(jié)點(diǎn),因此我們循環(huán)時(shí)候,需要做如下調(diào)整 P2 = P1, P1 = P3, P3=P3.next,然后接著操作P1 節(jié)點(diǎn)指針,繼續(xù)循環(huán)到最后p3.Next為null為止,如下圖最終狀態(tài)
-
如上最終狀態(tài),因?yàn)槲覀兠看沃恍薷牧薖1 節(jié)點(diǎn)的指針指向,所以循環(huán)結(jié)束后,還有最后節(jié)點(diǎn)沒(méi)有處理,我們需要在循環(huán)結(jié)束后處理。
-
如上分析,我有如下實(shí)現(xiàn):
魯棒性分析
- 在指針操作時(shí)候,最容易出現(xiàn)的三個(gè)問(wèn)題:
- 輸入的鏈表頭指針是努力,或者整個(gè)鏈表只有一個(gè)節(jié)點(diǎn),必須在前三個(gè)步驟中判斷
- 反轉(zhuǎn)后出現(xiàn)環(huán)形鏈表,在如上處理過(guò)程中,最容易忽略的頭節(jié)點(diǎn)指針指向null,導(dǎo)致環(huán)形鏈表
- 鏈表斷裂, 最后一個(gè)步驟沒(méi)有對(duì)最后的節(jié)點(diǎn)進(jìn)行指針指向操作,導(dǎo)致最后一個(gè)節(jié)點(diǎn)處斷裂。
合并兩個(gè)排序的鏈表
-
題目:輸入兩個(gè)遞增的鏈表,合并這兩個(gè)鏈表并使得新的鏈表中節(jié)點(diǎn)任然按原有順序有序。如下圖:
-
鏈表一,鏈表而是兩個(gè)遞增鏈表,合并兩個(gè)鏈表得到鏈表三
-
這個(gè)問(wèn)題我們需要注意的和上一個(gè)問(wèn)題類似,還是鏈表斷裂問(wèn)題,還有環(huán)形鏈表問(wèn)題,因?yàn)樾枰瑫r(shí)操作兩個(gè)鏈表的指針。
-
我們有如下分析:
- 將一二個(gè)鏈表看出需要處理的一個(gè)組,比較第一個(gè)元素,得到小的一個(gè),剔除當(dāng)成新鏈表的head節(jié)點(diǎn):
- 將一二個(gè)鏈表看出需要處理的一個(gè)組,比較第一個(gè)元素,得到小的一個(gè),剔除當(dāng)成新鏈表的head節(jié)點(diǎn):
- 將1 節(jié)點(diǎn)剔除后,將剩下的鏈表1 ,鏈表2 看成是一個(gè)整體,仍然比較第一個(gè)節(jié)點(diǎn)的大小,繼續(xù)如上步驟
- 繼續(xù)同樣的邏輯合并剩余的節(jié)點(diǎn),這是典型的遞歸流程,我們可以定義遞歸函數(shù)完成合并過(guò)程。.
- 如上分析有如下代碼
魯棒性分析
- 首先還是空指針問(wèn)題,一旦輸入空鏈表立刻npl,因此我們應(yīng)該對(duì)空鏈表單獨(dú)處理
更加復(fù)雜的指針操作案例樹(shù)的子結(jié)構(gòu)
- 題目:輸入兩顆二叉樹(shù)A,B,判斷B是不是A樹(shù)的子結(jié)構(gòu)。二叉樹(shù)的定義我們用之前章節(jié)中講解的二叉樹(shù)實(shí)現(xiàn)原理中定義的樹(shù)節(jié)點(diǎn)來(lái)實(shí)現(xiàn)。
-
例如有如下兩棵二叉樹(shù),A中有一部分子樹(shù)結(jié)構(gòu)和B是一直的,因此B是A的子結(jié)構(gòu):
-
依據(jù)之前二叉樹(shù)實(shí)現(xiàn)原理 的分析,樹(shù)操作中指針比值鏈表更加復(fù)雜,與樹(shù)相關(guān)的問(wèn)題我們通常都會(huì)用遞歸去解決。
-
如上題中查找A中包含B,可分為兩步:
- 第一步在A樹(shù)中查找B的根節(jié)點(diǎn)一樣的節(jié)點(diǎn)R,如果找到,執(zhí)行下一步
- 第二步,判斷A中以R為根節(jié)點(diǎn)的子樹(shù)與B樹(shù)的結(jié)構(gòu)是否一致,
-
用如上圖來(lái)分析:
- 首先在A中找 8 這個(gè)節(jié)點(diǎn),發(fā)現(xiàn)A的根節(jié)點(diǎn)就是8 ,我們將A以8節(jié)點(diǎn)為根節(jié)點(diǎn)的樹(shù),與B節(jié)點(diǎn)比較
- 將8 的左子樹(shù)看成完整的樹(shù),與B中左子樹(shù) 比較,還是按第一步驟邏輯 發(fā)現(xiàn)不同則不需第三部
- 如果相同則需要,將8 的右子樹(shù)看出完整的樹(shù),與B中右子樹(shù)比較,還是按第一步驟邏輯。
- 如果以上都不同,則回到第一步,將A的左子樹(shù)看出是一個(gè)完整的樹(shù)與B的根節(jié)點(diǎn)比較,
- 依次邏輯遍歷整個(gè)A樹(shù),直到找到B一樣結(jié)構(gòu)或者遍歷到葉子節(jié)點(diǎn)為止。
-
依據(jù)如上分析,我們有如下遞歸實(shí)現(xiàn),其中某些函數(shù)是用之前文章 二叉樹(shù)實(shí)現(xiàn)原理的某些功能:
- 以上考察二叉樹(shù)遍歷算法的理解 以及遞歸的能力
- 考察代碼的魯棒性,每個(gè)題型都有大量的指針操作,稍不注意就會(huì)有npl奔潰。我們應(yīng)該在程序開(kāi)始的時(shí)候采用防御性編程的方式
- 每次范問(wèn)指針地址之前都需要考慮這個(gè)指針是否有可能是null
上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–代碼完整性案例分析
下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–解決問(wèn)題的方法- 二叉樹(shù)的的鏡像
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法--代码鲁棒性案例分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 小猫钓鱼的故事(整篇) 小猫钓鱼的故事完
- 下一篇: 数据结构与算法--解决问题的方法- 二叉