美文网首页
剑指offer题集

剑指offer题集

作者: Lois_Huang | 来源:发表于2019-12-17 19:12 被阅读0次

    [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)

    相关文章

      网友评论

          本文标题:剑指offer题集

          本文链接:https://www.haomeiwen.com/subject/qhjenctx.html