剑指offer题集

[3] 数组中重复的数字

题目一:找出数组中重复的数字

Description

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

Solution

第一次提交的代码:利用哈希表

public boolean duplicate ( int numbers[], int length, int[] duplication){
    if (numbers == null || length <= 1) return false;
    int[] table = new int[length];
    for (int i = 0; i < length; i++) {
        table[numbers[i]]++;
        if (table[numbers[i]] > 1) {
            duplication[0] = numbers[i];
            return true;
        }
    }
    return false;
}

第二次提交的代码:交换

public boolean duplicate(int numbers[],int length,int [] duplication) {
    if (numbers == null || length <= 1) return false;
    for(int i = 0; i < length; i++){
        if(numbers[i] < 0 || numbers[i] > length-1) return false;
    }
    int temp;
    for(int i = 0; i < length; i++){
        while(numbers[i] != i){
            if(numbers[numbers[i]] == numbers[i]){
                duplication[0] = numbers[i];
                return true;  
            }
            else{
                temp = numbers[i];
                numbers[i] = numbers[temp];
                numbers[temp] = temp;
            }
        }
    }
    return false;
}

Note

1、Java中对整型数组默认初始化为0,因此哈希的解法中不用为table再进行初始化。
2、注意数组中元素进行交换,一定不能写成如下的形式...

temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[numbers[i]] = temp;

题目二:不修改数组找出重复的数字

Description

在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

Solution

未在牛客网上提交,可能通不过。

public boolean duplicate(int numbers[], int length, int[] duplication){
    if (numbers == null || length <= 1) return false;
    for(int i = 0; i < length; i++){
        if(numbers[i] < 1 || numbers[i] > length-1) return false;
    }
    int low = 1, high = length - 1;
    int middle;
    while(low < high){
        middle = (low+high)/2;
        if(count(numbers, length, low, middle) > middle-low+1) high = middle;
        else low = middle + 1;//注意low的值
    }
    duplication[0] = low;
    return true;
}
public int count(int numbers[], int length, int low, int high){
    int count = 0;
    for(int i = 0; i < length; i++){
        if(numbers[i] >= low && numbers[i] <= high) count++;
    }
    return count;
}

参考代码:

public int duplicate(int numbers[], int length, int[] duplication){
    if (numbers == null || length <= 0) return -1;
    int start = 1;
    int end = length - 1;
    while(end >= start){
        int middle = (end-start)/2 + start;
        int count = countRange(numbers,length,start,middle);
        if(end == start){ //考虑到给定的数组中无重复的元素
            if(count > 1) return start;
            else break;
        }
        if(count > (middle-start+1)) end = middle;
        else start = middle + 1;
    }
    return -1;
    
}
public int countRange(int numbers[], int length, int low, int high){
    if(numbers == null) return 0; //子函数也要注意异常的判断
    int count = 0;
    for(int i = 0; i < length; i++){
        if(numbers[i] >= low && numbers[i] <= high) count++;
    }
    return count;

Note

在不改变原有数组、不申请额外空间的情况下,找出数组中重复的值——二分查找算法。这道题不涉及到有序,但仍然可以使用二分法的思想。

[4] 二维数组中的查找

Description

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

Solution

public boolean findValues (int[][] array, int target){
    if(array == null || array.length == 0 ||array[0].length == 0) return false;
    int m = array.length, n = array[0].length;
    int a = 0, b = n - 1;
    while(true){
        if(array[a][b] < target){
            if(a == m-1) return false;
            else a++;
        }
        else if(array[a][b] > target){
            if(b == 0) return false;
            else b--;
        }
        else return true;
    }
}

看了参考代码后,可以改成如下:

public boolean findValues (int[][] array, int target){
    if(array == null || array.length == 0 ||array[0].length == 0) return false;
    int m = array.length, n = array[0].length;
    int a = 0, b = n - 1;
    while(a < m && b >= 0){ //while是有条件的
        if(array[a][b] == target) return true;
        else if(array[a][b] < target) a++;
        else b--;
    }
    return false;
}

Note

1、首次做这道题时,由于二维数组是有序的,所以使用了二分查找的思路,矩阵左上角和右下角的横纵坐标/2,用这个值与target进行比较,再依次二分...最后定位到target在某两行内,再对第一行的后部分和第二行的前部分进行二分查找,搜索target...后来把这种做法提交到牛客网上,发现通过率貌似只有50%左右,而且未通过的测试用例,利用这个方法是找不到的,才发现...这个方法不可行。

2、注意二维数组,判断数组内有无元素,不仅需要判断array.length,还需要判断array[0].length,例如 {% raw %} int[][] array= new int[][]{{}}; {% endraw %} ,它的array.length为1,但array[0].length为0。

[5] 替换空格

Description

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy,则经过替换之后的字符串为We%20Are%20Happy。

Solution

public String replaceSpace(StringBuffer str){
    if(str == null) return null;
    int n = str.length(), spaceNumber = 0;
    for(int i = 0; i < n; i++){
        if(str.charAt(i) == ' ') spaceNumber++;
    }
    int m = n+2*spaceNumber;
    str.setLength(m);
    for(int i = n-1, j = m-1; i >= 0; i--, j--){
        char temp = str.charAt(i);
        if(temp != ' ') str.setCharAt(j, temp);
        else{
            str.setCharAt(j, '0');
            str.setCharAt(j-1, '2');
            str.setCharAt(j-2, '%');
            j-=2;
        }
    }
    return str.toString();
}

Note

1、StringBuffer、StringBuilder的用法

  • toString():将StringBuffer,StringBuilder对象转换为String字符串,常用在需要输出的时候,因为StringBuffer和StringBuilder的对象不能直接输出。
  • append():用于在字符串的后面追加字符串
  • charaAt():返回指定索引位置的字符,索引从0开始
  • deleteCharAt():删除指定索引位置的字符
  • delete():删除从开始索引到结束索引的字符串
  • insert():在指定索引位置之前插入字符串
  • indexOf():返回指定字符串的开始字符索引位置,还可以从某个字符索引位置开始向后匹配,没有找到匹配的就会返回-1
  • lastIndexOf():和indexOf()的用法一样,只不过是从后往前匹配,也支持从指定索引开始从后往前去匹配
  • reverse():反转字符串
  • length():返回字符串的长度
  • setCharAt():设置指定索引处的字符
  • setLength():设置字符序列的长度
  • substring:返回一个新的 String ,其中包含此序列中当前包含的字符的子序列。

2、String、StringBuffer、StringBuilder的区别

  • 从性能、速度方面来说:StringBuilder > StringBuffer > String

  • 从线程安全的角度去看,StringBuffer是线程安全的,而StringBuilder是线程不安全的

  • 总结:

    String适用于少量的字符串操作的情况
    StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况
    StringBuffer适用多线程下在字符缓冲区进行大量操作的情况

2、往前遍历时,不一定需要遍历到第一个字符,只要两个指针重合即可。

[6] 从尾到头打印链表

Description

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

Solution

/*
public class ListNode { 
    int val;
    ListNode next = null;
    
    ListNode(int val) {
        this.val = val;
    }
}
*/

递归写法:

import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> array = new ArrayList<>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode == null){
            return array;
        }else{
            printListFromTailToHead(listNode.next);
            array.add(listNode.val);
        }
        return array;
    }
}

我写得不够简洁...

参考牛客网上某回答...

public class Solution {
    ArrayList<Integer> arrayList=new ArrayList<Integer>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode!=null){
            this.printListFromTailToHead(listNode.next);
            arrayList.add(listNode.val);
        }
        return arrayList;
    }
}  

用栈实现:

import java.util.ArrayList;
import java.util.Stack;
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> array = new ArrayList<>();
    Stack<Integer> stack = new Stack<>();
    while(listNode != null){
        stack.push(listNode.val);
        listNode = listNode.next;
    }
    while(!stack.empty()){
        array.add(stack.peek());
        stack.pop();
    }
    return array;
}

Note

1、Stack的用法

  • empty():测试此栈是否为空
  • peek():查看此栈顶部的对象,但不删除它
  • pop():删除此栈顶部的对象,并将该对象作为此函数的值返回
  • push():将对象存入此栈的顶部
  • search():返回一个对象在此栈上的基于1的位置

[7] 重建二叉树

Description

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

Solution

我的写法:

public TreeNode reConstructBinaryTree(int[] pre,int[] in) {
    if(pre == null || in == null || pre.length == 0 || in.length == 0) return null;
    return func(pre,in,0,pre.length-1,0,in.length-1);
}
public TreeNode func(int[] pre, int[] in, int preLow, int preHigh, int inLow, int inHigh){
    int inMid = 0;
    TreeNode node = null;
    for(int i = inLow; i <= inHigh; i++){
        if(in[i] == pre[preLow]){
            inMid = i;
            node = new TreeNode(in[inMid]); 
            node.left = null;
            node.right = null;
            break;
        }
    }
    if(inLow <= inMid-1){
        node.left = func(pre,in,preLow+1,preLow+inMid-inLow,inLow,inMid-1);
    }
    if(inMid+1 <= inHigh){
        node.right = func(pre,in,preLow+inMid-inLow+1,preHigh,inMid+1,inHigh);
    }
    return node;
}

参考写法:

public TreeNode reConstructBinaryTree(int[] pre,int[] in) throws Exception{
    if(pre == null || in == null || pre.length == 0 || in.length == 0) return null;
    return func(pre,in,0,pre.length-1,0,in.length-1);
}
public TreeNode reconstructCore(int[] pre, int[] in, int preLow, int preHigh, int inLow, int inHigh) throws Exception{
    int rootValue = pre[0];
    TreeNode root = new TreeNode(rootValue);
    root.left = null;
    root.right = null;
    if(preLow == preHigh){
        if(inLow == inHigh && pre[preLow] == in[inLow]) return root;
        else throw new Exception("Invalid input.");
    }
    //在中序遍历序列中找到根节点的值
    int inRoot = inLow;
    while(inRoot <= inHigh && in[inRoot] != rootValue) ++inRoot;
    if(inRoot == inHigh && in[inRoot] != rootValue) throw new Exception("Invalid input.");
    int leftLength = inRoot - inLow;
    int leftPreHigh = preLow + leftLength;
    if(leftLength > 0){
        //构建左子树
        root.left = reconstructCore(pre,in,preLow+1,leftPreHigh,inLow,inRoot-1);
    }
    if(leftLength < preHigh - preLow){
        //构建右子树
        root.right = reconstructCore(pre,in,leftPreHigh+1,preHigh,inRoot+1,inHigh);
    }
    return root;
}

Note

1、前序遍历序列的第一个数字是根节点的值,因此一进函数就可以根据该值新建节点,不用等在中序遍历序列中找到它才建。
2、异常处理!异常输入可能有(1)前序遍历和中序遍历的节点个数不相等(2)在中序遍历中找不到前序遍历中的节点。即使测试用例几乎都是正常的或一些边界数据,但是我们的函数应该考虑到异常的情况,并通过条件来抛出异常。
3、代码要具有可读性,学习下参考代码中leftLength等变量的建立以及在适当的位置写文字注释。

[8] 二叉树的下一个结点

Description

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

Solution

/*
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 leftNode = pNode.right;
            while(leftNode.left != null){
                leftNode = leftNode.left;
            }
            return leftNode;
        }else{
            TreeLinkNode nextNode = pNode.next;
            while(true){
                if(nextNode == null) return null;
                else{
                    if(nextNode.left == pNode) return nextNode;
                    else{
                        pNode = nextNode;
                        nextNode = nextNode.next;                  
                    }
                }
            }
            
        }
    }
}

参考写法:

public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode == null) return null;
        TreeLinkNode pNext = null;
        if(pNode.right != null){
            TreeLinkNode pRight = pNode.right;
            while(pRight.left != null){
                pRight = pRight.left;
            }
            pNext = pRight;
        }else if(pNode.next != null){
            TreeLinkNode pCurrent = pNode;
            TreeLinkNode pParent = pNode.next;
            while(pParent != null && pCurrent == pParent.right){
                pCurrent = pParent;
                pParent = pParent.next; 
            }
            pNext = pParent; 
        }
        return pNext;
    }
}

Note

1、注意是while(true)不是...while(1),并且一般情况下while当中是有内容的 ,不会是无条件循环。
2、拿到这种题,首先通过画图,分情况(最开始看到这道题...想法是往上找到根节点后再用中序遍历...服了自己
3、如果函数中有多个可能return的地方,可以不用在每个地方写return,应该在开始时定义一个变量,在各个分支进行赋值,在函数的结尾再return。

[9] 用两个栈实现队列

Description

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

Solution

我的写法:

import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() {
        int val;
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.peek());
                stack1.pop();
            }          
        }
        val = stack2.peek();
        stack2.pop();
        return val;
    }
}

参考写法:

import java.util.Stack;
 
public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
     
    public void push(int node) {
        stack1.push(node);
    }
     
    public int pop() throws RuntimeException{
        if(stack1.empty()&&stack2.empty()){
            throw new RuntimeException("Queue is empty!");
        }
        if(stack2.empty()){
            while(!stack1.empty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }
}

Note

1、如果栈为空,此时再pop,应该抛出异常。
2、stack.pop()函数既会弹出栈顶元素又会返回该元素。

[10] 斐波那契数列

Description

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。
n<=39

Solution

public int fibonacci(int n){
    if(n < 2) return n == 0 ? 0 : 1;
    int[][] array = {{1,1},{1,0}};
    array = calculate(array,n-1);
    return array[0][0];
}

public int[][] calculate(int[][] array, int n){
    if(n == 1) return array;
    int[][] value;
    value = calculate(array,n/2);
    value = matrixMulti(value,value);
    if(n%2 != 0) value = matrixMulti(value,array);
    return value;
}

//矩阵的乘法
public int[][] matrixMulti(int[][] x, int[][] y){
    if(x == null || y == null || x.length == 0 || y.length == 0 || x[0].length != y.length) return null;
    int a = x.length, b = y[0].length;
    int c = x[0].length;
    int[][] result = new int[a][b];
    for(int i = 0; i < a; i++){
        for(int j = 0; j < b; j++){
            for(int k = 0; k < c; k++){
                result[i][j] += x[i][k] * y[k][j];
            }
        }
    }
    return result;
}

补充:快速排序

Solution

public void quickSort(int[] array, int start, int end){
    if(start == end) return;
    int index = findPosition(array,start,end);
    if(index < start) quickSort(array,start,index-1);
    if(index < end) quickSort(array,index+1,end);
}
public int findPosition(int[] array, int start, int end){
    if(array==null || array.length==0 || start<0 || end>=array.length){
        throw new RuntimeException("Invalid Parameters.");
    }
    int index = array[start];
    while(start < end){
        while(start < end && array[end] > index){
            end--;
        }
        array[start] = array[end];
        while(start < end && array[start] <= index){
            start++;
        }
        array[end] = array[start];
    }
    array[start] = index;
    return start;
}

Note

还是严奶奶的写法比较容易记...没有想到快排居然忘了QAQ
核心是,先在数组中选择一个数字作为基准元素,接下来把数组中的数字分为两部分,比基准元素小的数字移到数组的左边,比基准元素大的数字移到数组的右边。每次排序实际上就是把基准元素放到正确的位置上。

[11] 旋转数组中的最小数字

Description

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

Solution

我的写法...

public int minNumberInRotateArray(int[] array) {
    if(array == null || array.length == 0) return 0;
    int result = keyFunc(array,0,array.length-1);
    return result == 0 ? array[0] : result;
}
public int keyFunc(int[] array, int start, int end){
    int result = 0;
    if(end-1 == start){
        if(array[start] > array[end]) result = array[end];
    }
    else{
        int middle = (start+end)/2;
        if(array[start] >= array[middle]) result = keyFunc(array,start,middle);
        if(array[middle] >= array[end]) result = result == 0 ? keyFunc(array,middle,end) : result;
    }
    return result;
}

参考写法:

public static int minNumberInRotateArray(int [] array) {
    if(array == null || array.length == 0) return 0;
    int start = 0, end = array.length-1;
    int middle = start;
    while(array[start] >= array[end]){
        // 如果start和index2指向相邻的两个数,
        // 则start指向第一个递增子数组的最后一个数字,
        // index2指向第二个子数组的第一个数字,也就是数组中的最小数字
        if(end-start == 1){
            middle = end;
            break;
        }
        // 如果下标为start、index2和indexMid指向的三个数字相等,
        // 则只能顺序查找
        middle = (start+end)/2;
        if(array[start] == array[end] && array[start] == array[middle]){
            return findMinNumber(array,start,end);
        }
        // 缩小查找范围
        if(array[middle] >= array[start]){
            start = middle;
        }
        if(array[middle] < array[end]){
            end = middle;
        }
    }
    return array[middle];
}
public static int findMinNumber(int[] array, int start, int end){
    int value = array[start];
    for(int i = start+1; i <= end; i++){
        if(value > array[i]) value = array[i];
    }
    return value;
}

Note

第一反应居然是顺序遍历...摔...
由于考虑到start/end/middle相等的情况,不知道该从哪边找,于是写成了递归...在不知道往哪边找的时候就往两边找..看了参考代码,原来不知道往哪边找的时候,就顺序找...

[12] 矩阵中的路径

Description

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

Solution

我的写法...

public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
    if(matrix == null || matrix.length == 0 || rows == 0 || cols == 0 || str == null || str.length == 0 || matrix.length != rows*cols){
        throw new RuntimeException("invalid input");
    }
    int length = rows*cols;
    boolean result = false;
    int[] flag = new int[length];
    for(int i = 0; i < length; i++){
        if(matrix[i] == str[0]){
            flag[i] = 1;
            result = keyFunc(matrix,rows,cols,str,flag,1,i);
            flag[i] = 0;
        }
        if(result == true) break;
    }
    return result;
}
public boolean keyFunc(char[] matrix, int rows, int cols, char[] str, int[] flag, int count, int i){
    boolean result = false;
    if(count == str.length) result = true;
    if(!result && count<str.length && i%cols>0 && flag[i-1]==0 && matrix[i-1]==str[count]) {
        flag[i-1] = 1;
        result = keyFunc(matrix, rows, cols, str, flag, ++count, i-1);
        count--;
        flag[i-1] = 0;
    }
    if(!result && count<str.length && i%cols<cols-1 && flag[i+1]==0 && matrix[i+1]==str[count]){
        flag[i+1] = 1;
        result = keyFunc(matrix,rows,cols,str,flag,++count,i+1);//count++
        count--;
        flag[i+1] = 0;
    }
    if(!result && count<str.length && i/cols>0 && flag[i-cols]==0 && matrix[i-cols]==str[count]){
        flag[i-cols] = 1;
        result = keyFunc(matrix,rows,cols,str,flag,++count,i-cols);
        count--;
        flag[i-cols] = 0;
    }
    if(!result && count<str.length && i/cols<rows-1 && flag[i+cols]==0 && matrix[i+cols]==str[count]){
        flag[i+cols] = 1;
        result = keyFunc(matrix,rows,cols,str,flag,++count,i+cols);
        count--;
        flag[i+cols] = 0;
    }
    return result;
}

参考写法:

public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
    if(matrix==null || matrix.length==0 || rows<1 || cols<1 || str==null || str.length==0 || matrix.length!=rows*cols
        throw new RuntimeException("invalid input");
    }
    int[] flag = new int[rows*cols];
    for(int row = 0; row < rows; row++){
        for(int col = 0; col < cols; col++){
            if(keyFunc(matrix,rows,cols,str,flag,0,row,col)) return true;
        }
    }
    return false;
}
public boolean keyFunc(char[] matrix, int rows, int cols, char[] str, int[] flag, int count, int row, int col)
    if(count == str.length) return true;
    boolean result = false;
    if(row>=0 && row<rows && col>=0 && col<cols && flag[cols*row+col]==0 && matrix[cols*row+col]==str[count]){
        flag[cols*row+col] = 1;
        count++;
        result = keyFunc(matrix,rows,cols,str,flag,count,row,col-1) ||
                keyFunc(matrix,rows,cols,str,flag,count,row,col+1) ||
                keyFunc(matrix,rows,cols,str,flag,count,row-1,col) ||
                keyFunc(matrix,rows,cols,str,flag,count,row+1,col);
        if(!result){
            flag[cols*row+col] = 0;
            count--;
        }
    }
    return result;
}

Note

虽然之前在LeetCode上也刷过类似的题,但还是耗时比较久,由于没考虑清楚,比如递归传参count++与++count的区别,以及回溯过程中count也需要--。
参考写法判断的是当前坐标是否满足条件,对于相邻坐标的判断,通过递归传参到下次判断。这样代码简单多了...

[13] 机器人的运动范围

Description

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

Solution

我的写法:

public int movingCount(int threshold, int rows, int cols)
{
    int[] visited = new int[rows*cols];
    return movingCountCore(threshold,rows,cols,0,0,0,visited);
}
public int movingCountCore(int threshold, int rows, int cols, int row, int col, int count, int[] visited){
    if(row>=0 && row<rows && col>=0 && col<cols && visited[row*cols+col] == 0){
        if(countSum(row)+countSum(col) > threshold){
            visited[row*cols+col] = -1;
        }else{
            count++;
            visited[row*cols+col] = 1;
            count = movingCountCore(threshold,rows,cols,row,col-1,count,visited);
            count = movingCountCore(threshold,rows,cols,row,col+1,count,visited);
            count = movingCountCore(threshold,rows,cols,row-1,col,count,visited);
            count = movingCountCore(threshold,rows,cols,row+1,col,count,visited);
        }
    }
    return count;
}
public int countSum(int row){
    int sum = 0;
    while(row != 0){
        sum += row%10;
        row /= 10;
    }
    return sum;
}

参考写法:

public int movingCountCore(int threshold, int rows, int cols, int row, int col, boolean[] visited){
    int count = 0;
    if(check(threshold,rows,cols,row,col,visited))
    {
        visited[row*cols+col]=true;
        
        count = 1 + movingCountCore(threshold,rows,cols,row,col-1,visited)
            + movingCountCore(threshold,rows,cols,row,col+1,visited)
            + movingCountCore(threshold,rows,cols,row-1,col,visited)
            + movingCountCore(threshold,rows,cols,row+1,col,visited);
    }
}
boolean check(...)//判断机器人能否进入当前坐标

Note

对于此类从某个格子出发可以到达多少格子的问题,在递归时可以为每个格子设置一个count的值,该count值表示从该格子出发能够到达的格子数(包括自己)。

[14] 剪绳子

Description

给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

Solution

public int cutRope(int target) {
    if(target < 2) return 0;
    if(target == 2 || target == 3) return target-1;
    int[] result = new int[target+1];
    result[1] = 1;
    result[2] = 2;
    result[3] = 3;
    for(int i = 4; i <= target; i++){
        for(int j = 1; j <= i/2; j++){
            int temp = result[j]*result[i-j];
            result[i] = result[i] < temp ? temp : result[i];
        }
    }
    return result[target];
}

[15] 二进制中1的个数

Description

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

Solution

我的写法...

public int getNumberOf1(int n){
    char[] charArray = Integer.toBinaryString(n).toCharArray();
    int length = charArray.length;
    int count = 0;
    for(int i = 0; i < length; i++){
        if(charArray[i] == '1') count++;
    }
    return count;
}

参考写法1:

public int getNumberOf1(int n){
    int count = 0;
    while(n != 0){
        if((n&1) == 1){
            count++;
        }
        n = n>>>1;//注意是无符号右移,如果是>>,则遇到负数时,全部补1,会进入死循环
    }
    return count;
}

参考写法2:

public int getNumberOf1(int n){
    int count = 0;
    int x = 1;
    while(x != 0){
        if((n&x) == x){
            count++;
        }
        x = x<<1;
    }
    return count;
}

参考写法3:

public int getNumberOf1(int n){
    int count = 0;
    while(n != 0){
        n = (n-1)&n;
        count++;
    }
    return count;
}

Note

1、在参考写法2中,循环的次数等于整数二进制的位数,32位的整数需要循环32次。效率不够高。而参考写法3中,根据:把一个整数减去1,再和原整数做与运算,会把该整数最右边的1变成0.那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
2、if(n&1==1)这样的写法是错误的,需要加上括号,即if((n&1) == 1)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 前言 2. 实现 Singleton 3. 数组中重复的数字 4. 二维数组中的查找 5. 替换空格 6. 从尾到...
    Observer_____阅读 2,919评论 0 1
  • 说明: 本文中出现的所有算法题皆来自牛客网-剑指Offer在线编程题,在此只是作为转载和记录,用于本人学习使用,不...
    秋意思寒阅读 1,145评论 1 1
  • 1.二维数组的查找 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从...
    linjiason阅读 720评论 0 0
  • 剑指offer第二版总结——基于牛客网 1. 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增...
    小张同学_loveZY阅读 2,528评论 0 3
  • 今天第一小组在班里,为大家讲一个故事,这个故事就叫邯郸学步。 这个故事里面,里面的各个角色,是司...
    张思怡阅读 398评论 0 2