日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

数据结构与算法--代码鲁棒性案例分析

發(fā)布時(shí)間:2023/12/4 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构与算法--代码鲁棒性案例分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

代碼魯棒性

  • 魯棒是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):
/**** 查找單向鏈表倒數(shù)第k個(gè)節(jié)點(diǎn)*/public static ListNode findLastKNode(ListNode head, int k){if(head == null || k == 0){return new ListNode(-1);}ListNode beforeNode = head;ListNode afterNode = head;for (int i = 0; i < k - 1; i++) {if(beforeNode.getNext() != null){beforeNode = beforeNode.getNext();}else {return new ListNode(-1);}}//確保有 k 個(gè)節(jié)點(diǎn)if(beforeNode.getNext() == null){return new ListNode(-1);}while (beforeNode.getNext() != null){beforeNode = beforeNode.getNext();afterNode = afterNode.getNext();}return afterNode;}
魯棒性分析
  • 雙指針?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)如下:
public class ListNode implements Comparable<ListNode> {private Integer value;private ListNode next; ...... }
  • 鏈表反轉(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):

/*** 反轉(zhuǎn)單向鏈表* */public static ListNode reverOverListNode(ListNode head){if(head == null){return head;}ListNode before = head;ListNode middle = head;ListNode after = head;if(middle.getNext() == null){return head;}middle = middle.getNext();if(middle.getNext() == null){middle.setNext(before);head.setNext(null);return middle;}after = middle.getNext();while (after.getNext() != null){middle.setNext(before);before = middle;middle = after;after = after.getNext();}//處理最后兩個(gè)節(jié)點(diǎn)的指針head.setNext(null);middle.setNext(before);after.setNext(middle);return after;}
魯棒性分析
  • 在指針操作時(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):

  • 將1 節(jié)點(diǎn)剔除后,將剩下的鏈表1 ,鏈表2 看成是一個(gè)整體,仍然比較第一個(gè)節(jié)點(diǎn)的大小,繼續(xù)如上步驟

  • 繼續(xù)同樣的邏輯合并剩余的節(jié)點(diǎn),這是典型的遞歸流程,我們可以定義遞歸函數(shù)完成合并過(guò)程。.
  • 如上分析有如下代碼
/*** 遞歸合并兩個(gè)順序鏈表* */public static ListNode mixTwoSortList(ListNode sortOne, ListNode sortTwo){if(sortOne == null && sortTwo == null){return new ListNode();}if(sortOne == null){return sortTwo;}if(sortTwo == null){return sortOne;}ListNode mergeHead = null;if(sortOne.getValue() < sortTwo.getValue()){mergeHead = sortOne;mergeHead.setNext(mixTwoSortList(sortOne.getNext(), sortTwo));}else {mergeHead = sortTwo;mergeHead.setNext(mixTwoSortList(sortTwo.getNext(), sortOne));}return mergeHead;}
魯棒性分析
  • 首先還是空指針問(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ù)節(jié)點(diǎn)對(duì)象定義** @author liaojiamin* @Date:Created in 15:24 2020/12/11*/ public class BinaryNode {private Object element;private BinaryNode left;private BinaryNode right; ...... }
  • 例如有如下兩棵二叉樹(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)原理的某些功能:

/*** 判斷 A樹(shù)中是否包含B樹(shù)* @author liaojiamin* @Date:Created in 16:43 2021/3/30*/ public class BinaryNodeComparable {/*** 遞歸方式遍歷樹(shù)* */public static boolean isComparable(BinaryNode tree1, BinaryNode tree2){if(tree1 == null || tree2 == null){return false;}boolean result = false;if(tree1.compareTo(tree2.getElement()) == 0){result = tree1HaveTree2(tree1, tree2);}if(!result){result = isComparable(tree1.getLeft(), tree2);}if(!result){result = isComparable(tree1.getRight(), tree2);}return result;}/*** 依次比較跟,左,右節(jié)點(diǎn)* */public static boolean tree1HaveTree2(BinaryNode tree1, BinaryNode tree2){//t2 遍歷完了并且都一致,則存在包含if(tree2 == null){return true;}//t2 不為空,t1 為空 ,則不存在包含if(tree1 == null){return false;}if(tree1.compareTo(tree2.getElement()) != 0){return false;}return tree1HaveTree2(tree1.getLeft(), tree2.getLeft())&& tree1HaveTree2(tree1.getRight(), tree2.getRight());}public static void main(String[] args) {BinaryNode node1 = new BinaryNode(null, null, null);BinarySearchTree tree1 = new BinarySearchTree();Random random = new Random();for (int i = 0; i < 20; i++) {node1 = tree1.insert(Integer.valueOf(i), node1);}tree1.printTree(node1);System.out.println("-------------");BinaryNode node2 = new BinaryNode(null, null, null);BinarySearchTree tree2 = new BinarySearchTree();for (int i = 0; i < 3; i++) {node2 = tree2.insert(Integer.valueOf(i), node2);}tree2.printTree(node2);System.out.println(isComparable(node1, node2));} }
  • 以上考察二叉樹(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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。