文章目录

  • 51 构建乘积数组
  • 52 正则表达式匹配
  • 53 表示数值的字符串
  • 54 字符流中第一个不重复的字符
  • 55 链表中环的入口节点
  • 56 删除链表中重复节点
  • 57 二叉树的下一个节点
  • 58 对称的二叉树
  • 59 按之字形顺序打印二叉树

欢迎关注个人数据结构专栏哈
剑指offer(1-10题)详解
剑指offer(11-25题)详解
剑指offer(26-33题)详解
剑指offer(34-40题)详解
剑指offer(41-50题)详解
剑指offer(51-59题)详解
剑指offer(60-67题)详解
微信公众号:bigsai
声明:大部分题基本未参考题解,基本为个人想法,如果由效率太低的或者错误还请指正!

51 构建乘积数组

题目描述

给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

思路
这题刚开始还没想到,刚开始还想着用啥位运算?刚开始想着怎么用总数变成对应的数,但是人家要求不能用除法。得用乘法。(不要按照公式每个每个的死算,这样太低效)。其实把上面等式右侧看成两部分就行了。A[0]*A[1]*...*A[i-1]A[i+1]*...*A[n-1]

在具体处理上,我们使用两个数组,一个数组leftmut[]记录从左向右的叠乘,一个数组rightmut[]记录从右向左的叠乘。这样正常情况下每个位置的B[i]就变成了leftmut[i-1]*rightmut[i+1].当然,特殊情况和边界需要特殊考虑下!


实现代码为:

import java.util.ArrayList;
public class Solution {public  int[] multiply(int[] A) {//特殊情况暂不考虑int len=A.length;int B[]=new int[len];if(len<2)return B;int leftmut[]=new int [len];int rightmut[]=new int [len];leftmut[0]=A[0];rightmut[len-1]=A[len-1];for(int i=1;i<len;i++){leftmut[i]=A[i]*leftmut[i-1];//从左向右递乘法rightmut[len-1-i]=A[len-1-i]*rightmut[len-i];//从右向左递乘法}B[0]=rightmut[1];B[len-1]=leftmut[len-2];for(int i=1;i<len-1;i++){B[i]=leftmut[i-1]*rightmut[i+1];}return B;}
}

52 正则表达式匹配

题目描述

请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配

思路
这题后面要优化,感觉我写的逻辑太乱了。本来想能不能先把非符号去掉。。能不能dp。。想不出来。进了讨论区瞅了眼发现递归妙用。

遇到相同的或者.就往下递归匹配。遇到x*就分类讨论看看pattern能不能前进(2位),str能不能前进。把所有可能都递归下去(有真就行)。但是感觉这样效率真的不高啊。。。虽然过了。后面代码和逻辑都要优化。

实现代码:

import java.util.Arrays;
public class Solution {public static boolean match(char[] str, char[] pattern){if(str.length==0)//匹配串为0{if(pattern.length==0)return true;else {if(pattern.length%2==1)return false;else {for(int i=1;i<pattern.length;i++)if(pattern[i]!='*')return false;return true;}}}else if (pattern.length==0) {//匹配串为0,肯定不行return false;}else {if(pattern.length==1){if(pattern[0]==str[0]||pattern[0]=='.'){if(str.length==1)return true;}return false;}else {if(pattern[1]=='*'){if(pattern[0]=='.'){return match(Arrays.copyOfRange(str, 1, str.length),pattern)||match(str, Arrays.copyOfRange(pattern, 2, pattern.length));}if(pattern[0]==str[0]){return match(Arrays.copyOfRange(str, 1, str.length), pattern)||match(Arrays.copyOfRange(str, 1, str.length), Arrays.copyOfRange(pattern, 2, pattern.length))||match(str, Arrays.copyOfRange(pattern, 2, pattern.length));}else {return match(str, Arrays.copyOfRange(pattern, 2, pattern.length));}}else {if (pattern[0]==str[0]||pattern[0]=='.') {return match(Arrays.copyOfRange(str, 1, str.length), Arrays.copyOfRange(pattern, 1, pattern.length));}else {return false;}}}}}
}

参考讨论区
讨论区的比萨大叔讲的挺好的,另外就是我这里事用数组拷贝来比较,而他用数组指标代替其实效率更高的。空间可以重复利用,并且数组复制占用太多空间还是递归的!!可以参考下!

链接:https://www.nowcoder.com/questionTerminal/45327ae22b7b413ea21df13ee7d6429c?f=discussion
来源:牛客网public class Solution {public boolean match(char[] str, char[] pattern) {if (str == null || pattern == null) {return false;}int strIndex = 0;int patternIndex = 0;return matchCore(str, strIndex, pattern, patternIndex);
}public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {//有效性检验:str到尾,pattern到尾,匹配成功if (strIndex == str.length && patternIndex == pattern.length) {return true;}//pattern先到尾,匹配失败if (strIndex != str.length && patternIndex == pattern.length) {return false;}//模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符|| matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符|| matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个} else {return matchCore(str, strIndex, pattern, patternIndex + 2);}}//模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回falseif ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {return matchCore(str, strIndex + 1, pattern, patternIndex + 1);}return false;}
}

53 表示数值的字符串

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。

思路
经典字符串处理题!字符串问题其实还是比较麻烦的。因为可能会有很多纰漏,需要考虑点比较多。这题需要考虑的点大致有:

  • 最多有两个独立数字 e(E)前面可为小数或者整数,而后面只能为整数
  • 正负号只能在独生数字开始
  • 除了数字、+-.Ee其他字符均非法
  • 只有前面有小数点。注意e(E)前后不能为空(小数点也是只不过这题数据较弱没写也过了可以自己添上)

逻辑实现上,可以用一些int类似数组标记独立数字,小数点数字长度啥的。然后对字符串进行各个分类讨论,判断各个符号出现次数,是否合法等等。详细可以参考代码:

//数据比较弱。 12.e+4   12. 这类需要特殊判断  public static boolean isNumeric(char[] str) {boolean smallpoint=false;//小数点boolean ise=false;//是否遇到eint localindex=0;//当前数字指标for(int i=0;i<str.length;i++){if(localindex==0&&(str[i]=='+'||str[i]=='-')){localindex++;continue;}else if (str[i]=='.'&&localindex>0) {if(smallpoint||ise)//当有小数点或者在e后面return false;else {smallpoint=true;}}else if ((str[i]=='e'||str[i]=='E')&&localindex>0) {if(ise)return false;else {ise=true;localindex=0;}}else if (str[i]>='0'&&str[i]<='9') {localindex++;}else {return false;}}if (localindex>0) {return true;}else         return false;}

参考评论区:

54 字符流中第一个不重复的字符

题目描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述:

如果当前字符流没有存在出现一次的字符,返回#字符。

思路
这题首先要理解题意吧。题目就是给了两个操作,insertFirstAppearingOnce两个函数,至于一些其他需要你自己实现。你可以选择字符数组、或者String做容器储存。这里我是用StringBuider储存。

insert肯定都没问题,但FirstAppearingOnce这个你不要每次都从头开始找,暴力枚举、肯定会炸的。你需要用个字符储存记录。也就是insert同时实现动态查找。而FirstAppearingOnce返回参数即可。

具体实现,我用的hashmap储存每个出现的次数。用index标记当前位置的出现第一个数。对于插入,如果不等于index位置的数,那么不需要改变第一个只出现次数为1的数,如果插入的是index位置字符,那么就需要从index往后查找下一个,如果index和字符串长度一样,那么就返回#

实现代码为:

import java.util.HashMap;
public class Solution {//Insert one char from stringstream   int index=0;StringBuilder sb=new StringBuilder();HashMap<String, Integer>map=new HashMap<String, Integer>();public void Insert(char ch){sb.append(ch);if(map.containsKey(ch+"")) {//前面有该元素map.put(ch+"", 2);if((index<sb.length())&&ch==sb.charAt(index))//正是第一次出现的{for(;index<sb.length();index++){System.out.println(index);if(map.get(sb.charAt(index)+"")==1){break;}}}        }else {map.put(ch+"", 1);}        }//return the first appearence once char in current stringstreampublic  char FirstAppearingOnce(){if(index==sb.length())return '#';else {return sb.charAt(index);}   }
}

55 链表中环的入口节点

题目描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

思路
没想到什么更好的办法,就是没加一个暴力枚举它的下一个是否在前面。因为链表入口的确定需要比较的是地址而不是数值,判断相等只能用==比较。而链表有环那么环一定在后半部分,本来next应该指向null的那个最后节点指向前面某个节点node.这个node就是入口节点。后面如果发现更好方法会补充。

实现代码

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
import java.util.ArrayList;
public class Solution {public ListNode EntryNodeOfLoop(ListNode pHead){ArrayList<ListNode>list=new ArrayList<ListNode>();while (pHead.next!=null) {for(int i=0;i<list.size();i++){if(pHead.next==list.get(i)){return pHead.next;}}ListNode pHead2=pHead;list.add(pHead2);pHead=pHead.next;}return null;}
}

56 删除链表中重复节点

题目描述

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

思路
这题虽然很容易读懂题,但是处理起来如果选择方法不当可能还比较麻烦。因为是已经排好序的,只要是相同的那么节点值有相同的都要删除。但是删除时候你要考虑头节点问题。和尾部不能空指针异常(要判断是否删到头)。

对于头问题,如果头就有相等的那么这个头你就要处理一下,还有我们知道曾经链表我们引入一个带头节点的链表为了方便操作链表首。这里我们也可以这样操作。先用个node的next指向head,head先前移,最后返回head.next即可

有了这个头节点,就可以在不越界的情况下判断(node.next和node.next.next)的值是否等,如果等那么就往下删光所有值为它的节点。在这个过程要注意不要越界操作!!

实现代码为:

/*public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}
*/
public class Solution {public static ListNode deleteDuplication(ListNode pHead){if(pHead==null)return null;ListNode teamnode=new ListNode(Integer.MAX_VALUE);//当作头节点teamnode.next=pHead;pHead=teamnode;while (teamnode.next!=null) {System.out.println(teamnode.val);while(teamnode.next.next!=null&&teamnode.next.val==teamnode.next.next.val){int delete=teamnode.next.val;while (teamnode.next!=null&&teamnode.next.val==delete) {teamnode.next=teamnode.next.next;}if(teamnode.next==null)return pHead.next;}teamnode=teamnode.next;}return pHead.next;//先处理一下}
}

57 二叉树的下一个节点

题目描述

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路
这里他给的是一个节点(左右和父节点)。这题的处理方法虽然还是蛮多的,但是有的不一定好。关于二叉树的几种遍历以前记过可以看看。

比如你可以根据这个节点找到根节点吧?根节点知道的二叉树的非递归中序遍历前面讲过吧,根据顺序可以找到下一个。但是这样真的太低效了。

分析二叉树和分析这个节点的位置。 分类讨论就完全ojbk

  • 右侧有儿子:这个节点只要有右侧节点那么它的下一个节点肯定在右侧节点,因为二叉树中序是左中右。只需要找到右侧最左的那个节点就ok。
  • 右侧无儿子找到上面第一个是父亲左节点的节点。因为这个节点没右儿子。就要看这个节点所在的这个子树的第一个中间节点了。就是[左侧区域最右]->中的这个查找过程。当然,如果它的父节点祖先节点都不存在是左儿子的情况,那么它就是最右侧的节点,返回null就行了。

实现代码为:

/*
public class TreeLinkNode {int val;TreeLinkNode left = null;TreeLinkNode right = null;TreeLinkNode next = null;TreeLinkNode(int val) {this.val = val;}
}
*/
public class Solution {public TreeLinkNode GetNext(TreeLinkNode pNode){if(pNode.right!=null){pNode=pNode.right;while (pNode.left!=null) {pNode=pNode.left;}}else if (pNode.next!=null&&pNode.next.left==pNode) {pNode=pNode.next;}else {while (pNode.next!=null&&pNode.next.right==pNode) {pNode=pNode.next;}if(pNode.next!=null)pNode=pNode.next;else {pNode=null;}}return pNode;}
}

58 对称的二叉树

题目描述

请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

思路
看看对不对称,照照镜子就行!看看镜子里你伸右手它是不是伸左手。而二叉树是不是对称的,你只需要根据某种遍历方式同时进行左右顺序颠倒的对比,查看节点的结构(有无左右孩子)和节点数值是否相等,如果有一点不一样直接停止返回即可!而这里笔者使用两个队列进行层序遍历一个规则是从左向右,另一个规则是从右向左 比较到最后就行了。

实现代码为:

import java.util.ArrayDeque;
import java.util.Queue;
/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}
}
*/
public class Solution {boolean isSymmetrical(TreeNode pRoot){if(pRoot==null)return true;Queue<TreeNode>q1=new ArrayDeque<>();//策略是左右Queue<TreeNode>q2=new ArrayDeque<>();//策略是右左q1.add(pRoot);q2.add(pRoot);while (!q1.isEmpty()&&!q2.isEmpty()) {TreeNode t1=q1.poll();TreeNode t2=q2.poll();if(t1.val!=t2.val)return false;if(!(t1.left==null)^(t2.right==null)&&!(t1.right==null)^(t2.left==null))//左右子树结构相同{if(t1.left!=null) {q1.add(t1.left);q2.add(t2.right);}           if(t1.right!=null) {q1.add(t1.right);q2.add(t2.left);}         }   else {return false;}}return true;}
}

59 按之字形顺序打印二叉树

题目描述

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

思路
就是一个特殊的层序遍历。更换层的时候需要更换节点顺序,这需要我们用两个内存空间配合达到分清奇偶的目的。这里有的是从左到右,有的是从右到左,理论上可以借助栈将集合的元素反转但是没必要。我用两个List集合直接刚就行了。
首先进行分析:

  • 第一行从左到右,第二行从右到左,第三行从左到右。两个list装的是节点而还需要每次遍历根据奇数和偶数的特性将节点装起来
  • (普遍方法)你可以全部按照正常的顺序分层装起来,只不过如果偶数层遍历的时候从右往左加进结果集合。比较好想,容易操作,但是偶数层在添加节点时候不能同时遍历。
  • 但是笔者瞎搞发现一个规律。全部从右往左遍历。只不过在奇数行先添加(左后右)。而偶数行进行右左添加,相当于这个顺序操作一次被颠倒一次,每次添加节点都可以直接访问而不需要单独的访问。(这个方法可能复杂了上面一条其实就可以了)

实现代码(需要自己画图理解):

import java.util.ArrayList;/*
public class TreeNode {int val = 0;TreeNode left = null;TreeNode right = null;public TreeNode(int val) {this.val = val;}
}
*/
public class Solution {public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {ArrayList<ArrayList<Integer>>list=new ArrayList<ArrayList<Integer>>();if(pRoot==null)return list;ArrayList<TreeNode>nodelist1=new ArrayList<TreeNode>();//用来模拟堆栈用ArrayList<TreeNode>nodelist2=new ArrayList<TreeNode>();nodelist1.add(pRoot);int num=1;//做奇数偶数while (!nodelist1.isEmpty()||!nodelist2.isEmpty()) {ArrayList<Integer>team=new ArrayList<Integer>();if(num%2==1) {for(int i=nodelist1.size()-1;i>=0;i--){TreeNode teamNode=nodelist1.get(i);team.add(teamNode.val);if(teamNode.left!=null)nodelist2.add(teamNode.left);if(teamNode.right!=null)nodelist2.add(teamNode.right);}nodelist1.clear();}else {for(int i=nodelist2.size()-1;i>=0;i--){TreeNode teamNode=nodelist2.get(i);team.add(teamNode.val);if(teamNode.right!=null)nodelist1.add(teamNode.right);if(teamNode.left!=null)nodelist1.add(teamNode.left);}nodelist2.clear();}list.add(team);num++;}return list;}}

剑指offer(51-59题)详解相关推荐

  1. 剑指offer python实现_剑指Offer第2题详解(附Python、Java代码实现)

    题目描述 请实现一个函数,将一个字符串中的每个空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 这个题较为 ...

  2. 【剑指offer】BN层详解

    [剑指offer]系列文章目录 梯度消失和梯度爆炸 交叉熵损失函数 文章目录 [剑指offer]系列文章目录 BN层的本质原理 BN层的优点总结 BN层的过程 代码实现 BN层的本质原理 BN层(Ba ...

  3. 《剑指offer》75题 C++详细题解

    目录 简单: 剑指 Offer 03. 数组中重复的数字 map: unordered_map: 原地交换 剑指 Offer 05. 替换空格 剑指 Offer 06. 从尾到头打印链表 出栈入栈 双 ...

  4. 《剑指offer》刷题总结

    从三月初开始刷剑指offer上面的题,到现在花了近二十天的时间终于刷完了.应该说,掌握上面的技巧应付一些公司面试题和小公司的笔试题是完全没有问题的.之前参加一个公司笔试,算法题就有一题是剑指offer ...

  5. 剑指offer第41题 和为s的两个数

    剑指offer第41题 和为s的两个数 #include<iostream> using namespace std;bool sumk(int*list,int length,int*n ...

  6. 【LeetCode】剑指 Offer 51. 数组中的逆序对

    [LeetCode]剑指 Offer 51. 数组中的逆序对 文章目录 [LeetCode]剑指 Offer 51. 数组中的逆序对 package offer;public class Soluti ...

  7. 《剑指offer》刷题笔记(发散思维能力):求1+2+3+...+n

    <剑指offer>刷题笔记(发散思维能力):求1+2+3+-+n 转载请注明作者和出处:http://blog.csdn.net/u011475210 代码地址:https://githu ...

  8. 《剑指offer》刷题——【链表】从尾到头打印链表

    <剑指offer>刷题--[链表]-<从尾到头打印链表> 问题分析: 递归实现: 1. 无返回值 2. 有返回值(ArrayList) 问题分析: 从头到尾打印链表比较简单,那 ...

  9. 《剑指Offer》刷题之最小的K个数

    <剑指Offer>刷题之最小的K个数 我不知道将去向何方,但我已在路上! 时光匆匆,虽未曾谋面,却相遇于斯,实在是莫大的缘分,感谢您的到访 ! 题目: 给定一个数组,找出其中最小的K个数. ...

  10. 挑战一天(12h)刷完《剑指offer》67题

    提前说明,挑战的人不是我. 大家周末好,我是爱上 B 站的小吴,最近一段时间我在网站 AlgoMooc 录制剑指 Offer 的视频,同时上传了一部分到 B 站,基于推荐算法,B 站疯狂的给我推送如何 ...

最新文章

  1. Mysql5.7-CentOS7安装
  2. ANSYS——相同模型不同创建方式的同载荷同约束下的比较
  3. 20172301 2017-2018-2《程序设计与数据结构》课程总结
  4. 2015年《大数据》高被引论文Top10文章No.3——我国政府数据开放现状和保障机制...
  5. Python3 SSH远程连接服务器
  6. Bookmarklet
  7. 如何写出一个较好的快速排序程序
  8. 【完结】深度学习CV算法工程师从入门到初级面试有多远,大概是25篇文章的距离...
  9. “C语言” 读书札记(五)之[让我们一起学习汇编吧!(段)]
  10. [SCM]源码管理 - SVN:externals
  11. c语言二级java难吗_计算机二级考JAVA还是C?
  12. 计算机专业有关电路的书,计算机专业电路基础试题.doc
  13. Windows中的NTUSER.DAT文件是什么?
  14. Neverland Test 2.0
  15. 未来-区块链-Micron:区块链永远不会忘记:内存对这项革命性技术的推动作用...
  16. windows控制台命令: 快捷键大集合
  17. mysql 2002_解决MySQL报错ERROR 2002 (HY000)
  18. 浏览器缩放时,页面布局发生变化
  19. fatal: unable to access ‘‘: Failed to connect to 27.0.0.1 port 7890 after 2097 ms:Connection
  20. 数据仓库系列(3):数据的价值如何体现

热门文章

  1. 你所不知道的VoIP通信技术?
  2. 搞懂朴素贝叶斯分类算法
  3. Ruby 之Gem kaminari
  4. 登录samba后提示“你可能没有权限访问网络资源”的解决方法
  5. ireport 循环_ireport detail循环原理
  6. 企业提供下载链接的安全解决方案
  7. 用PPT作数模论文图片的方法与技巧
  8. 如何在React.js文件中设置标签的背景图片
  9. vue文件在服务器上乱码,解决vue-pdf查看pdf文件及打印乱码的问题
  10. 顺丰推出“丰食”平台 单挑美团饿了么胜算几何?