自信满满去牛客网刷剑指offer,结果前几题没一道过的。所以痛定思痛,决定把自己犯的睿智错误记录下来,以此为戒。
错误原因:Math.pow参数搞反了。
//我想写个3^n,结果写成了下面这个n^3,看了好久才发现。
Math.pow(n,3);
错误原因:搜索的时候没有返回sum+1;
public int dfs(int i,int j){
visited[i][j] = true;
if(check(i,j) > threshold){
return 0;
}
int sum = 0;
if(i<visited.length - 1 && !visited[i+1][j])
sum += dfs(i+1,j);
if(j<visited[0].length - 1 && !visited[i][j+1])
sum += dfs(i,j+1);
//这是我一开始写的,没有加上1,而且我dfs末节点返回的是0,就导致了sum始终为0
//所以dfs末节点返回的时候一定要当心,注意什么时候返回false,什么时候返回true
//返回0的时候一定要当心,如果不进行处理的话一直会是0
return sum;
//return sum+1;
}
错误原因:
public boolean dfs(int i,int j,int index){
//然后数组元素的计算也出了问题,一开始写成了row*i+col*j
//应该在纸上先找找规律,不要这么自信
if(matrix[j+cols*i] != str[index])
return false;
//首先错在这里,没有返回true的情况,所以怎么返回都是false。
if(matrix[j+cols*i] == str[index] && index == str.length-1)
return true;
visited[i][j] = true;
boolean up = false;
boolean down = false;
boolean left = false;
boolean right = false;
if(i>0 && !visited[i-1][j])
//最后是这里,找了半天没看出来,
//在这里使用index++会导致每次条件判断后无论成立不成立都会使index++,
//明明没有找到符合当前字符的坐标,却把字符向后移动了一位,所以i++千万不要乱用。
//正确的写法应该是 up = dfs(i-1,j,index+1);
up = dfs(i-1,j,index++);
if(i<rows-1 && !visited[i+1][j])
down = dfs(i+1,j,index+1);
if(j>0 && !visited[i][j-1])
left = dfs(i,j-1,index+1);
if(j<cols-1 && !visited[i][j+1])
right = dfs(i,j+1,index+1);
return up||down||right||left;
}
错误原因:忘记left++
while(right < num.length){
while(!queue.isEmpty() && queue.peekLast() < num[right])
queue.removeLast();
queue.addLast(num[right++]);
if(right-left == size){
res.add(queue.peekFirst());
}else if(right-left > size){
//说明此时left需要向右移动
if(queue.peekFirst() == num[left]){
queue.removeFirst();
//我把left++写在这里了,
//导致只有当队列头元素和左边界相等的时候left才会自增
left++;
}
res.add(queue.peekFirst());
//应该在这里写 left++;
}
}
错误原因:Java优先队列的名字不会打,默认是小顶堆记不清。
//首先PriorityQueue拼不对
static PriorityQueue<Integer> smallHeap = new PriorityQueue<>();
//其次lambda表达式写错了,降序排列的CMP应该是return y-x;
static PriorityQueue<Integer> bigHeap = new PriorityQueue<>((x,y)->(y-x));
错误原因:没有搞清楚应该使用全局变量还是局部变量。
TreeNode res = null;
//int target = 1;
TreeNode KthNode(TreeNode pRoot, int k)
{
//target = k;
findNode(pRoot, k);
return res;
}
/**
* 中序遍历递归寻找Kth节点
**/
public void findNode(TreeNode p, int k){
if(k == 0)
return;
if(p == null)
return;
//我在这边使用了局部变量k,这会导致当左子树k=0返回的时候,
//在父节点看来k还是为1,所以还会向右子树递归,导致结果出错。
//应该定义一个全局变量target来保存k的状态。
findNode(p.left, k);
k--;
if(k==0){
res = p;
return;
}
findNode(p.right, k);
}
错误原因:没有考虑清楚当前节点没有右子树且是其父节点的右子树的情况。
/*
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 == null)
return null;
//右子树不为空的时候返回右子树的最左节点
if(pNode.right != null){
TreeLinkNode tmp = pNode.right;
while(tmp.left != null){
tmp = tmp.left;
}
return tmp;
}
//不存在右子树的情况
//说明根节点,直接返回null
if(pNode.next == null)
return null;
//说明不是根节点,判断是该节点是父节点的左节点还是右节点
TreeLinkNode father = pNode.next;
//左节点,返回父节点
if(father.left == pNode)
return father;
else{
//右节点,不断向上判断直到父节点是null,
//或者找个一个是其父节点的左节点
//一开始的时候没有考虑清楚,以为这种情况下直接返回其祖父节点就行了
//其实不是这样,还会要在草稿纸上多画画
while(father.next != null){
TreeLinkNode tmp = father.next;
if(tmp.left == father)
return tmp;
else
father = father.next;
}
return null;
}
}
}
题目大意:给出一个排好序的链表,删除其中重复的所有节点。
错误原因:没有考虑头结点为空的情况。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
//一开始没有写这一段
//导致会在while(cur.next != null)中空指针异常
if(pHead == null)
return null;
ListNode voidHead = new ListNode(0);
voidHead.next = pHead;
ListNode pre = voidHead;
ListNode cur = pHead;
while(cur.next != null){
ListNode next = cur.next;
if(next.val != cur.val){
cur = cur.next;
pre = pre.next;
}else{
while(next != null && next.val == cur.val){
next = next.next;
}
pre.next = next;
cur = pre.next;
}
if(cur == null)
break;
}
return voidHead.next;
}
}
题目大意:给出一个链表,判断其是否有环,如有的话返回环的入口,没有则返回null。
错误原因:在快慢指针重新从头出发的时候没有初始化慢指针。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast = pHead.next;
ListNode slow = pHead;
while(slow != null && fast != null){
//找到环了
if(slow == fast){
int count = 1;
slow = slow.next;
while(slow != fast){
count++;
slow = slow.next;
}
fast = pHead;
//这里忘记初始化了,导致死循环
slow = pHead;
while(count-- >0){
fast = fast.next;
}
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}else{
slow = slow.next;
fast = fast.next;
if(fast != null)
fast = fast.next;
else
return null;
}
}
return null;
}
}
题目大意:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。
但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
错误原因:忘记判断当字符为其他字符的情况。
public class Solution {
public boolean isNumeric(char[] str) {
if(str == null || str.length == 0)
return false;
int index = 0;
boolean numFlag = false;
boolean dotFlag = false;
boolean eFlag = false;
for(int i=0;i<str.length;i++){
char c = str[i];
if(c == '+' || c == '-'){
if(i == 0 || str[i-1] == 'e' || str[i-1] == 'E')
continue;
else
return false;
}
else if(c >= '0' && c<= '9'){
numFlag = true;
continue;
}
else if(c == '.'){
if(dotFlag || eFlag)
return false;
else{
dotFlag = true;
continue;
}
}
else if(c == 'e' || c == 'E'){
if(eFlag)
return false;
else{
eFlag = true;
numFlag = false;
}
}
//忘记考虑当前字符既不是.也不是e也不是+和数字了
//导致测试用例1a3.14没有通过。
//所以分情况讨论的题目一定要把测试用例中的所有情况列出来
else {
return false;
}
}
return numFlag;
}
}
题目大意:
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配。
错误原因:数组越界,做的太慢。虽然两次过了,但是做了20多分钟,等于没做出来。谁叫这道题要分类讨论呢,每次看到就瑟瑟发抖感觉都有心理阴影了。
更新:没想到6月20日的每日一题是这道题,那么错哪了呢?数组开小了,dp[][]
的长宽应该是对应字符串的长宽+1.
public class Solution {
public boolean match(char[] str, char[] pattern)
{
if(str == null && pattern == null)
return true;
if(str == null || pattern == null)
return false;
//初始化一个dp数组,dp[i][j]代表的是s[i-1]和p[j-1]的情况。
boolean[][] dp = new boolean[str.length+1][pattern.length+1];
dp[0][0] = true;
for(int i=0;i<pattern.length+1;i++){
//问题出在这里一开始写的pattern[i],所以越界了
//自己上面都写了j代表patter[j-1]居然还写错,不太应该
if( i > 1 && pattern[i-1] == '*')
//这里一开始也写错了,写成了patter[i] = patter[i-2]
//这居然都没有发现,还好错误在数组越界的附近,所以看到了
dp[0][i] = dp[0][i-2];
}
for(int i=1;i<str.length+1;i++){
for(int j=1;j<pattern.length+1;j++){
if(pattern[j-1] != '*'){
if(pattern[j-1] == '.' || pattern[j-1] == str[i-1]){
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j] = false;
}
}else{
//当前字符与*前面的字符相同,这个时候可以把str中的字符向前移动一位
if(str[i-1] == pattern[j-2] || pattern[j-2] == '.')
dp[i][j] |= dp[i-1][j];
//无论相同还是不同都可以无视后面两位,直接把p中字符向前移动两位
dp[i][j] |= dp[i][j-2];
}
}
}
return dp[str.length][pattern.length];
}
}
题目大意:将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
错误原因:字符串数组初始化错误;没有考虑负数的情况。
public class Solution {
public int StrToInt(String str) {
if(str == null || str.length() == 0)
return 0;
int res = 0;
boolean flag = false;
//这里一开始写成了new char[],出了错我还一脸懵逼
//一定是我写的时候被笔试邮件分神了。
char[] chars = str.toCharArray();
for(int i=0;i<chars.length;i++){
if(chars[i] == '+' || chars[i] == '-'){
if(i!=0)
return 0;
if(chars[i] == '-')
flag = true;
}
else if(chars[i] >= '0' && chars[i] <= '9'){
res = res*10 + chars[i] - '0';
}
else{
return 0;
}
}
//一开始忘记判断是负数了
return flag?-res:res;
}
}
题目大意:不用加减乘除做加法
错误原因:忘记怎么做了。。。。这么短的代码你也能忘?
public class Solution {
public int Add(int num1,int num2) {
while(num2 != 0){
//这里应该先计算出进位然后右移
int tmp = (num1&num2)<<1;
//计算出当前不用进位的和
num1 = num1 ^ num2;
//把进位当加数进行下一次循环
num2 = tmp;
}
return num1;
}
}
题目:总之就是约瑟夫环
错误:又双叒叕忘了。
这次记好了,递推公式为ans = (ans+m)% i (2<=i<=n)
。
代码:
public int LastRemaining_Solution(int n, int m) {
if(n == 0 || m == 0)
return -1;
int ans = 0;
// 最后一轮剩下2个人,所以从2开始反推
for (int i = 2; i <= n; i++) {
ans = (ans + m) % i;
}
return ans;
}
题目大意:逆转一个字符串。
错误原因:
万万没想到这道题和leetcode上不一样,还稍微简单一些。
牛客上的题目不需要把多个连续空格取去除。
public class Solution {
public String ReverseSentence(String str) {
if(str == null || str.length() == 0)
return "";
//注意处理一下" "情况,按照牛客的要求应该返回" "
if(str.trim().equals(""))
return str;
String[] strings = str.trim().split(" ");
StringBuilder sb = new StringBuilder();
for(int i=strings.length-1;i>=0;i--){
if(i != 0)
sb.append(strings[i]).append(" ");
else
sb.append(strings[i]);
}
return sb.toString();
}
}
题目大意:给出一个整数S,求所有和为S的连续整数序列。
错误原因:搞错了sum变化的时机。
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if(sum <= 2)
return res;
int limit = (sum+1)/2;
int left = 1;
int right = 2;
int target = sum;
sum = 3;
while(left < right && right <= limit){
//一开始的时候把sum初始化为1,在这里sum += right;
//这样的话有一个问题,即使移动的是左边界,sum也会执行一次+=right
//显然是错误的。
if(sum == target){
ArrayList<Integer> tmp = new ArrayList<>();
for(int i=left;i<right+1;i++){
tmp.add(i);
}
res.add(tmp);
sum -= left;
left++;
}
else if(sum < target){
right++;
//刚开这里写错了
sum += right;
}
else if(sum > target){
sum -= left;
left++;
}
}
return res;
}
}
题目大意:给出一个数组,每个数字都出现两次,只有两个数字只出现一个次,找出这两个数字。
错误原因:
其实这道题也是老朋友了,结果还是没有能够一次过。
在找两个数字异或和哪一位为1的时候出了点差错。
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
if(array == null || array.length < 2)
return;
int sum = 0;
for(int i=0;i<array.length;i++){
sum ^= array[i];
}
int digit =1;
if(sum == 0)
return;
while(true){
//错误出现在这里,一开始我写成了(sum&digit) == 1
//这样会导致只有当最后一位为1的才会退
if((sum&digit) == digit)
break;
else
digit <<= 1;
}
int sum1 =0;
int sum2 =0;
for(int i=0;i<array.length;i++){
if((array[i]&digit) == digit)
sum1 ^= array[i];
else
sum2 ^= array[i];
}
num1[0] = sum1;
num2[0] = sum2;
}
}
这一轮暂时先做到这边,虽然很多题目都已经做过好几遍了,但是还是出了不少差错。
事实证明了一句话“思想很简单,细节是魔鬼”。对于做过好几次的题也不能掉以轻心,还是要多加练习。比如今天做的正则匹配,怎么会忘记字符串比较的题目需要把DP长度多开一位呢?
接下来两天先好好复习一下之前的知识点,先从JAVA基础开始吧。