剑指Offer错误总结

前言

自信满满去牛客网刷剑指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;
}	

矩阵中的路径

错误原因:

  1. DFS没有返回true
  2. 计算元素位置错误
  3. 乱用i++
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));

二叉搜索树的第K个节点

错误原因:没有搞清楚应该使用全局变量还是局部变量。

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,求所有和为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基础开始吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值