20190326 剑指offer 解题思路整理(一)

20190326 剑指offer 解题思路整理


面试题17 打印从1到最大的n位数

思路:最简单的方法可能是打印1到10^n-1,但有个问题就是可能N太大会溢出。所以应该用字符串的方法来实现,其实可以看成n为字符串,每位可以为0~9的一个全排列,打印的时候前面的0不输出。


面试题18 删除链表中重复的节点 ★★★★★

思路:用pre记录待删除节点的前一个节点,cur表示当前的节点。一旦cur.val==next.val,记录下value,就让cur=cur.next,直到cur的值不为value。如果pre为null,就让head=cur,否则,pre.next=cur,进入下一个循环。


面试题19 正则表达式匹配

思路:string和pattern匹配,普通情况是pattern当前字符的下一字符不为“*”(判断条件1),并且当前指向string的字符与当前指向pattern的字符相同或者pattern当前的字符为‘.’(判断条件2),两个指针都向前加1,否则返回false;特殊情况就是pattern当前字符的下一字符为“*”,可以实现任意次数(0次或n次)匹配,此时若满足条件2,可以选择不匹配(s,p+1)也可以选择匹配一次(s+1,p+2)也可以选择匹配多次(s+1,p)。!!!如果不满足条件2,可以选择不匹配,直接返回(s,p+2)。

注意事项:

  1. 普通匹配与特殊匹配的关键在于*pattern+1是否为*,如果不是,则比较*str和*pattern是否相等或者*pattern是否等于’.'即可,如果不等就不匹配返回false;但如果*pattern+1是*,即便当前*str!=*pattern && *plattern!=’.’,不能直接返回false,而是选择匹配0次,返回(s,p+2)。
  2. 注意递归函数的返回条件,即如果str指针走到了字符串尾部,pattern指针也走到了字符串尾部,则返回true(1),如果str指针没走到字符串尾部,pattern指针确走到了字符串尾部,则返回false(2);
  3. 由于Java是通过数组下标来取字符,所以一定要先确保i,j没有超出边界!!再取字符进行判断!! 例如,判断pattern[j+1]是否为*时,先检查j<pattern.length-1;取str[i],pattern[j]都要先判断i<str.length,j<pattern.length.

面试题20 表示数值的字符串

思路:一个字符串可以表示成A.B[e|E]C的形式,其中A表示整数部分,B表示小数部分,C表示指数部分。第一步识别出A(A可以缺省),识别小数点’.’,再判断B是不是合法的无符号整数,再识别e|E,最后识别指数部分C,C识别完后返回,如果此时在字符串结尾,则返回true。

注意事项:

  1. A和C是有符号的整数,即可以是+/-开头,B是无符号整数;
  2. .123也是合法的,即A可以缺省,但是一旦识别到小数点,B不可以缺省,同样,一旦识别到e|E,C不可以缺省。因此在识别到小数点和e|E后,都需要先判断是否在字符串尾,若是则返回false,若否再进一步识别B或C;
  3. 函数isSignedNum(char[] str,int index) 和函数isUnSignedNum(char[] str,int index)的返回值是int类型,为识别出的整数部分的下一字符的位置;
  4. 需要注意识别有符号整数时,一旦识别到首字符为‘0’就返回,但是无符号整数识别的是小数部分,则可以不考虑开头0的问题。

面试题21 调整数组顺序使奇数位于偶数前面

思路:一个指针从头开始,一个指针从尾部开始,一旦头指针指向的数字是偶数,等到尾部指针指向奇数,然后交换,直到left=right.

注意事项:

  1. 这种题还有很多变体,比如说能整除3的放在不能整除3的前面等,都是一类问题,唯一的区别就在于判断条件不同。因此可以把判断条件单独拿出来定义,从而使得程序有更高的扩展性。
  2. 由于牛客网上要求相对位置不变(稳定性),因此需要换成插入排序的变种。之前两个指针的方法是不稳定的。

面试题22 链表中倒数第k个节点

思路:先让快指针走k步,然后快慢指针同时走,当快指针走到链表结尾的时候,慢指针在链表倒数第k个结点。

注意事项:

  1. 链表为空或者k=0,直接返回null;
  2. k值大于链表长度,因此在快指针先走的阶段需要判断,快指针还未走完k步,是否已经到达链表尾;
  3. 由于快慢指针同步走,最后的判断是fast==null,所以快指针和慢指针相差的节点数是k,而不是k-1;

面试题23 链表中环的入口结点

思路:快慢双指针遍历链表,一旦fast为空,说明不存在环,否则fast一定会追上slow;确定有环后,通过计数得到环中的结点数k,让fast先走k步,再和慢指针同步,这样慢指针走len-k步之后,快指针刚好走完len-k+k回到环的入口,与慢指针相遇。

注意事项:

  1. 第一步确定链表是否有环时,用快慢指针遍历之前,需要判断head.next是否为null,然后fast=head.next;slow=head;(因为退出while循环的条件是fast==slow,所以fast和slow的初始值应不一样)

面试题24 反转链表

思路:当前指针的next指针指向前一个节点之前,需要先记录原先的next指针指向的结点。因此需要三个指针,before,current,after

注意事项:

  1. 如果head=null 或者 head.next=null 直接返回head;
  2. 如果after=null,说明当前节点为原链表的尾结点,新链表的头节点newHead,需要标记一下,循环结束后return newHead;

面试题25 合并两个排序链表

思路:有两种方法,第一种比较简单的递归思路,每一轮递归挑选出一个较小值结点,也可以用迭代的方法实现。

注意事项:

  1. 递归方法比较容易: if(l1.val<=l2.val){newHead=l1;l1.next=Merge(l1.next,l2);}
  2. 迭代方法需要注意,需要新分配一个ListNode cur,cur指向已排好序的链表的尾节点,新增节点都通过赋值给cur.next来加入链表。同时一旦退出循环,要完成cur.next最后一次赋值。

面试题26 树的子结构

思路:子树匹配的问题,用递归来解决,分为两步,第一步找到根节点相同的结点,否则递归地遍历tree1,从左子树种找匹配,再从右子树种找匹配。如果找到根节点相等的结点,进行第二步,从roo2根节点开始比较,递归比较左右子树,同时返回true才匹配成功。

注意事项:

  1. 首先有两个递归函数:一个是递归地在tree1中找与tree2根节点相等的节点;一个是从根节点开始比较左右节点的值;
  2. 递归结束返回true的条件是 root2=null,即已经比较完tree2的叶子节点;
  3. 由于树节点的值是double类型,需要自己写一个相等的判断函数,两数之差绝对值小于0.0000001返回true.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值