代码鲁棒性

  • 鲁棒是robust的音译,就是健壮性。指程序能够判断输入是否符合规范,对不合要求的输入能够给出合理的结果。
  • 容错性是鲁棒的一个重要体现。不鲁棒的代码发生异常的时候,会出现不可预测的异常,或者程序奔溃。
  • 由于鲁棒性非常重要,因此我们在写代码的时候,必须进行防御性编程,这个必须成为我们编程的一种习惯,编码过程中应该能够预见可能出现的问题,并适当处理。

最容易出错的双指针操作

  • 链表中倒数第K个节点
  • 题目:输入一个链表,输出链表中倒数第k个结点。例如,链表依次是1,2,3,4,5,6,倒数第三个就是4。
  • 我们依然用之前我们在讲解链表实现时候的链表对象定义
分析
  • 此处题目要求的应该是单向链表,因为双向链表没有复杂度可言。为了得到倒数第K个节点,并且不能从尾部遍历,同样的案例从尾部到头打印单向链表,我们在之前的文章(数据结构与算法–链表实现以及应用)中已经有详细的分析,此处同样也可以利用这种方法。
  • 以上方法中借助其他数据接口,栈实现,时间复杂度是O(N+k),空间复杂度O(2n)。
  • 应该还有更加高效率的算法,我们还是从头遍历链表,假设有n个节点,那么倒数第k个节点从头开始数是第n-k+1 个节点
  • 如果我们能得到节点个数n大小,那么直接从头遍历n-k+1个就得到倒数第k个了 。
  • 我们用双指针实现,利用两个指针中间步数的差值来得到倒数第k个,比如,我们需要n-k+1的差距,也就是n-(k-1)
  • 当指针A 走到末尾的时候,指针B需要走k-1 的距离,那么此时B指针指向的就是倒数第k个
  • 例如倒数第2 个,那么B就比A 少走了2-1 = 1 步骤,就是指向倒数第二个了。
  • 如下图:



  • 如上图一中P1走两步,P2 则指向头结点
  • 接着继续两个指针P1, P2,一起向前走,
  • 当P1走到链表尾,则,P2,正好走到倒数第三位置。
  • 如下diam实现:
   /**** 查找单向链表倒数第k个节点*/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 个节点if(beforeNode.getNext() == null){return new ListNode(-1);}while (beforeNode.getNext() != null){beforeNode = beforeNode.getNext();afterNode = afterNode.getNext();}return afterNode;}
鲁棒性分析
  • 双指针方法中需要注意的点还是挺多的:

    • 当我们输入head 结点为null时候回,由于代码会范问空指针内存,次数程序会奔溃
    • 输入head为头肩底的链表节点总数少于k个,此时,我们需要先走k-1 步骤,如果正好k-1个节点,那么之后的步骤会抛出NPL,如果少于k-1个节点,则此时就已经npl
    • 输入参数k为0 的时候,此时 k-1= -1,非法数值,-1 二进制位符号位1 此时会读成数据位,此时数据变为4294967295,此时for循环将会超过执行次数。
相关问题
  • 问题一:求单向链表中介节点,奇数个返回中间节点,偶数个返回中间两个任意一个,同样双指针A,B, A每个循环走一步,B每个循环走两步,B到末尾,则A就在中间节点
  • 问题二:求解单向链表是否环形链表,同样双指针A,B,A走一步,B走两步,如果到最后B追上了A 则环形,如果B到最后null,则不是环形

最容易死循环的链表问题

  • 题目:定义一个函数,输入链表头结点,反转并输出反转后的链表头结点。
分析
  • 我们依然用 之前链表的实现文章中定义的链表节点如下:
public class ListNode implements Comparable<ListNode> {private Integer value;private ListNode next;
......
}
  • 链表反转涉及到每个节点的指针操作,非常容易出现死循环问题,为了正确理解整个过程,我们应该首先借助图形来直观的分析。如下:

  • 我一开始还是想到双指正方法,第一步骤分别指向钱两个节点,并且改变本节点的指针指向,我们此时需要知道的是,本节点信息,上一个节点信息,这里都符合,得到下一图步骤。

  • 但是此时我们无法循环到下一个节点3, 因此逻辑无法成立,我们需要借助第三个指针,P3,如下图

  • 如上,我们可以得到本节点信息,上一个节点信息,下一个节点信息,在经过p2 指针的转向后,我们无法通过P2.next得到下一个节点,因此我们循环时候,需要做如下调整 P2 = P1, P1 = P3, P3=P3.next,然后接着操作P1 节点指针,继续循环到最后p3.Next为null为止,如下图最终状态

  • 如上最终状态,因为我们每次只修改了P1 节点的指针指向,所以循环结束后,还有最后节点没有处理,我们需要在循环结束后处理。

  • 如上分析,我有如下实现:

  /*** 反转单向链表* */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();}//处理最后两个节点的指针head.setNext(null);middle.setNext(before);after.setNext(middle);return after;}
鲁棒性分析
  • 在指针操作时候,最容易出现的三个问题:

    • 输入的链表头指针是努力,或者整个链表只有一个节点,必须在前三个步骤中判断
    • 反转后出现环形链表,在如上处理过程中,最容易忽略的头节点指针指向null,导致环形链表
    • 链表断裂, 最后一个步骤没有对最后的节点进行指针指向操作,导致最后一个节点处断裂。

合并两个排序的链表

  • 题目:输入两个递增的链表,合并这两个链表并使得新的链表中节点任然按原有顺序有序。如下图:

  • 链表一,链表而是两个递增链表,合并两个链表得到链表三

  • 这个问题我们需要注意的和上一个问题类似,还是链表断裂问题,还有环形链表问题,因为需要同时操作两个链表的指针。

  • 我们有如下分析:

    • 将一二个链表看出需要处理的一个组,比较第一个元素,得到小的一个,剔除当成新链表的head节点:

  • 将1 节点剔除后,将剩下的链表1 ,链表2 看成是一个整体,仍然比较第一个节点的大小,继续如上步骤

  • 继续同样的逻辑合并剩余的节点,这是典型的递归流程,我们可以定义递归函数完成合并过程。.
  • 如上分析有如下代码
 /*** 递归合并两个顺序链表* */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;}
鲁棒性分析
  • 首先还是空指针问题,一旦输入空链表立刻npl,因此我们应该对空链表单独处理

更加复杂的指针操作案例树的子结构

  • 题目:输入两颗二叉树A,B,判断B是不是A树的子结构。二叉树的定义我们用之前章节中讲解的二叉树实现原理中定义的树节点来实现。
/*** 二叉树节点对象定义** @author liaojiamin* @Date:Created in 15:24 2020/12/11*/
public class BinaryNode {private Object element;private BinaryNode left;private BinaryNode right;
......
}
  • 例如有如下两棵二叉树,A中有一部分子树结构和B是一直的,因此B是A的子结构:

  • 依据之前二叉树实现原理 的分析,树操作中指针比值链表更加复杂,与树相关的问题我们通常都会用递归去解决。

  • 如上题中查找A中包含B,可分为两步:

    • 第一步在A树中查找B的根节点一样的节点R,如果找到,执行下一步
    • 第二步,判断A中以R为根节点的子树与B树的结构是否一致,
  • 用如上图来分析:

    • 首先在A中找 8 这个节点,发现A的根节点就是8 ,我们将A以8节点为根节点的树,与B节点比较
    • 将8 的左子树看成完整的树,与B中左子树 比较,还是按第一步骤逻辑 发现不同则不需第三部
    • 如果相同则需要,将8 的右子树看出完整的树,与B中右子树比较,还是按第一步骤逻辑。
    • 如果以上都不同,则回到第一步,将A的左子树看出是一个完整的树与B的根节点比较,
    • 依次逻辑遍历整个A树,直到找到B一样结构或者遍历到叶子节点为止。
  • 依据如上分析,我们有如下递归实现,其中某些函数是用之前文章 二叉树实现原理的某些功能:

/***  判断 A树中是否包含B树*  @author liaojiamin* @Date:Created in 16:43 2021/3/30*/
public class BinaryNodeComparable {/*** 递归方式遍历树* */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;}/*** 依次比较跟,左,右节点* */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));}
}
  • 以上考察二叉树遍历算法的理解 以及递归的能力
  • 考察代码的鲁棒性,每个题型都有大量的指针操作,稍不注意就会有npl奔溃。我们应该在程序开始的时候采用防御性编程的方式
  • 每次范问指针地址之前都需要考虑这个指针是否有可能是null

上一篇:数据结构与算法–代码完整性案例分析
下一篇:数据结构与算法–解决问题的方法- 二叉树的的镜像

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

  1. 数据结构与算法--代码完整性案例分析

    确保代码完整性 在撸业务代码时候,经常面对的是接口的设计,在设计之初,我们必然要先想好入参,之后自然会有参数的校验过程,此时我们需要把可能的输入都想清楚,从而避免在程序中出现各种纰漏.但是难免面面俱到 ...

  2. 青岛大学_王卓老师【数据结构与算法】Week04_12_案例分析与实现2_学习笔记

    本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频. 一方面用于学习记录与分享,另一方面是想让更多的人看到这么好的<数据结构与算法>的学习视频. 如有侵权,请留言作删文处理. 课程视频 ...

  3. 《代码随想录(Carl)》 数据结构与算法 程序的性能分析知识点总结

    <代码随想录(Carl)> 数据结构与算法 程序的性能分析 2.1时间复杂度分析 2.1.1时间复杂度 时间复杂度是一个函数,它定性描述该算法的运行时间. 假设算法的数据规模为n,操作单元 ...

  4. 决策树模型算法研究与案例分析

    决策树模型算法研究与案例分析 (白宁超 2018年8月27日11: 42:33) 导读:决策树算法是一种基本的分类与回归方法,是最经常使用的算法之一.决策树模型呈树形结构,在分类问题中,表示基于特征对 ...

  5. 逻辑回归模型算法研究与案例分析

    逻辑回归模型算法研究与案例分析 (白宁超 2018年9月6日15: 21:20) 导读:逻辑回归(Logistic regression)即逻辑模型,属于常见的一种分类算法.本文将从理论介绍开始,搞清 ...

  6. MATLAB智能算法30个案例分析.史峰等

    <MATLAB智能算法30个案例分析>是2011年由北京航空航天大学出版社出版的图书,作者是郁磊.史峰.王辉.胡斐- <MATLAB智能算法30个案例分析>是作者多年从事算法研 ...

  7. KNN模型算法研究与案例分析

    KNN模型算法研究与案例分析( 白宁超 2018年8月29日15:39:13 ) 导读:机器学习算法中KNN属于比较简单的典型算法,既可以做聚类又可以做分类使用.本文通过一个模拟的实际案例进行讲解.整 ...

  8. matlab 30案例 目录,MATLAB-智能算法30个案例分析-终极版(带目录).doc

    MATLAB-智能算法30个案例分析-终极版(带目录) MATLAB 智能算法30个案例分析(终极版) 1?基于遗传算法的TSP算法(王辉)? 2?基于遗传算法和非线性规划的函数寻优算法(史峰)? 3 ...

  9. MATLAB智能算法30个案例分析pdf

    下载地址:网盘下载 MATLAB智能算法30个案例分析,ISBN:9787512403512,作者:史峰,王辉 等编著 下载地址:网盘下载 转载于:https://www.cnblogs.com/cf ...

最新文章

  1. iOS跳转到各种系统设置界面
  2. starUML -- 各种图绘制
  3. python编程基础之三十三
  4. 【UIKit】表格自定义单元格(UITableViewCll)
  5. 中国石油管道科技研究中心2014届应届毕业生招聘(软件工程师岗)
  6. 源码WIFI--扫描和连接
  7. 网络爬虫中Fiddler抓取PC端网页数据包与手机端APP数据包
  8. 从 FFmpeg 性能加速到端云一体媒体系统优化
  9. 力扣78. 子集(JavaScript)
  10. 特斯拉中国工厂2020投产,还可能为完全自动驾驶更新硬件
  11. 【Codeforces 1426 E】Rock, Paper, Scissors,贪心!算反面
  12. c#获取系统信息:CPU、内存、硬盘、用户、网络
  13. 请千万不要在 JDK 7+ 中使用这个 JSON 包了!切记
  14. wxpython grid设置字体颜色_Ext grid改变行背景颜色 和改变行字体颜色
  15. 怎样无损调整分区大小和扩容分区?
  16. 结构体定义的几种形式
  17. 如果批评《说好不哭》不自由,则赞美周杰伦无意义
  18. 国际知名制作公司名录及网址大全,制作人员必备
  19. testerhome学习笔记3_Bash应用一
  20. lucene(11)

热门文章

  1. linux平台之如何查看svn账号
  2. Andorid之打包出现Proguard returned with erro code 1.See console解决办法
  3. linux c 之使用-O来优化gcc
  4. php基础教程 第六步 学习数组以及条件判断switch补充
  5. 最大尺寸分辨率_未来就在眼前——视涯科技推出最高分辨率硅基OLED显示屏幕...
  6. vue2 怎么用vite_vue3vite简介
  7. 三角形中的“叛徒”--莱洛三角形,一个神奇的存在!
  8. 数学除了摧残祖国的花朵外,竟然还可以赢钱!
  9. stc单片机c语言 pdf,STC单片机C语言程序设计 第13章 STC单片机C语言指针.pdf
  10. php mysql 执行sql文件_PHP执行SQL文件并将SQL文件导入到数据库_PHP