剑指offer(java版)面试题3——面试题13 代码+易错点+考点知识点总结

面试题3:二维数组中的查找

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

测试用例:
        (1)输入的数组是空指针
        (2)输入的数组中没有目标数
        (3)输入的数组中有目标数
//我的代码1  时间复杂度为O(mn) 运行时间为257ms 慢了!
        public class Solution {
        public boolean Find(int target, int [][] array) {
            if(array == null) return false;  #处理空指针 
            for(int i = 0;i < array.length;i++){
                for(int j=0;j<array[i].length;j++){
                    if(array[i][j] == target){
                        return true;
                    }else if(array[i][j] > target){
                        break;
                    }
                }
            }
            return false;
        }
    }

注意:上面的有一块是可以优化的,就是break那里,因为还可以分为是否在第一个位置和不在第一个位置,虽然这样也可以完成!

// 我的代码2 时间复杂度为O(n)  运行时间为157ms 
            public class Solution {
                public boolean Find(int target, int [][] array) {
                    if(array == null) return false; 
                    int i = array.length-1,j = 0;
                    while(i>=0 && j<array[0].length){
                        if(array[i][j] == target){
                            return true;
                        }else if(array[i][j] > target){
                            i--;
                        }else{
                            j++;
                        }
                    }
                    return false;
                }
            }

这个比较快,而且充分应用了对角点的特点。

面试题4:替换空格

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

//第一种:这种方法是取巧型的,主要借助了java的底层封装。这种代码主要就是用了StringBuffer的底层可扩充的性能
        public class Solution {
          public String replaceSpace(StringBuffer str){
                if(str == null) return null;
                StringBuffer s = new StringBuffer();
                for(int i = 0;i<str.toString().length();i++) {
                        char a = str.charAt(i);
                    if(String.valueOf(a).equals(" ")) {
                        s.append("%20");

                    }else {
                        s.append(str.charAt(i));
                    }
                }
                return s.toString();
              }
    }

关于字符串的javaAPI和操作知识点:

  1. java中String和StringBuffer是不一样的,String是不可变得,而StringBuffer则是长度可变的。并且返回值时也不一样,如果错把StringBuffer当成String返回则会报错!
    2.String应该是用的最多的,其中常用的方法有:(假设str为一个字符串变量)
    1. 字符串的长度: str.length length后面木有括号!
    2. 按照索引找到字符串的字符 str.charAt(i) #i就是索引数字
    3. StringBuffer类型不能直接使用String的方法,那么需要转化的时候,可以调用strB.toString()就变成了字符串类型。
    4. StringBuffer类型可以进行长度扩充,使用append()方法即可。

面试题5:打印链表

输入一个链表,从尾到头打印链表每个节点的值(注意是从尾到头)

//方法一:借助栈
        定义一下链表的结构:          

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

                import java.util.Stack;
                import java.util.ArrayList;
                public class Solution {
                    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
                        if(listNode == null) {
                            ArrayList<Integer> a = new ArrayList<>();
                            return a;
                        }
                        ArrayList<Integer> a = new ArrayList<>();
                        // 借助栈的先进后出,所以需要导入java.util.Stack
                        Stack<Integer> s =new Stack<Integer>();        
                        while(listNode != null) {
                            s.push(listNode.val);
                            listNode = listNode.next;
                        }
                        while(!s.isEmpty()) {
                            a.add(s.pop());
                        }
                        return a;
                    }
                }

这道题会有两个易出错的地方:
(1)常规处理输入为空的时候,我们返回的是null,但是这道题会有输入为{}的情况,这个时候需要该题返回的是[],所以需要特别的处理。
(2)java中的栈需要导入的包是java.util.Stack
与栈相关的方法有:s.push(element) 进栈
s.pop(element) 出栈
s.isEmpty() 判断栈是否为空(注意,不是用的遍历个数,而是直接判断栈是否为空)

这道题考察了数据结构栈来辅助进行逆序!

相关java知识点:

  1. List集合代表一个元素有序、可重复的集合。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按照元素的添加顺序设置元素的索引,索引从0开始。
    ArrayList是基于数组实现的List类。
    List中的常用方法:
    1. void add(int index,Object element)将元素添加到索引index处。
    2. Object get(int index) 返回索引处的元素

面试题6:重建二叉树

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

//代码:

            定义二叉树:
             * Definition for binary tree
             * public class TreeNode {
             *     int val;
             *     TreeNode left;
             *     TreeNode right;
             *     TreeNode(int x) { val = x; }
             * }

            import java.util.*;
            public class Solution {
                public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
                    if(pre==null || in == null) return null;
                    if(pre.length==0 || in.length == 0) return null;
                    TreeNode a = new TreeNode(pre[0]);
                    for(int i=0;i<pre.length;i++) {
                        if(pre[0] == in[i] ) {
                            a.left = reConstructBinaryTree(Arrays.copyOfRange(pre, 1, i+1),Arrays.copyOfRange(in, 0, i));
                            a.right = reConstructBinaryTree(Arrays.copyOfRange(pre, i+1, pre.length),Arrays.copyOfRange(in, i+1, pre.length));
                        }
                    }
                    return a;
                }
            }

二叉树相关知识点:

前序遍历:遍历顺序规则为【根左右】
中序遍历:遍历顺序规则为【左根右】
后序遍历:遍历顺序规则为【左右根】

什么是【根左右】?就是先遍历根,再遍历左孩子,最后遍历右孩子;

java数组方法:
上面用到了Arrays.copyOfRange(in, 0, i))用来拷贝数组。其中第一个参数是要拷贝的数组,第二个和第三个参数是要拷贝的数组下标范围(拷贝的时候包括前标,但是不包括后标,也就是前闭后开)

面试题7:用两个栈实现队列

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

//代码:
            import java.util.Stack;

            public class Solution {
                Stack<Integer> stack1 = new Stack<Integer>();
                Stack<Integer> stack2 = new Stack<Integer>();

                public void push(int node) {
                    while(!stack2.isEmpty()){
                        stack1.push(stack2.pop());
                    }
                    stack1.push(node);
                }

                public int pop() {
                    while(!stack1.isEmpty()){
                        stack2.push(stack1.pop());
                    }
                    return stack2.pop();
                }
            }       

知识点:

这道题主要考的就是队列的特性是先进先出,栈的特性是先进后出。所以模拟的时候需要注意这个问题!

面试题8:旋转数组的最小数字

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

//代码:
            import java.util.ArrayList;
            public class Solution {
                public int minNumberInRotateArray(int [] array) {
                    if(array==null || array.length==0) return 0;
                    int min = array[array.length -1];
                    for(int i = array.length-1;i>=0;i--) { #这个位置一定要注意-1)
                        if(array[i] < min) {
                            min = array[i];
                        }else if(array[i]>min) {
                            break;
                        }
                    }
                    return min;
                }
            }

面试题9:斐波那契数列

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

//代码:(使用循环实现)
            public class Solution {
                public int Fibonacci(int n) {
                    if(n==0) return 0; //这里可能会忽略处理,通过代码要求对0进行处理
                    if(n==1 || n==2) return 1;
                    int [] a ; 
                    a = new int [39];
                    a[0] = 1;a[1]=1;
                    for(int i=2;i<n;i++) {
                        a[i] = a[i-1]+a[i-2];
                    }
                    return a[n-1];
                }
            }

注意:这个应该还有一种使用递归实现的算法

知识点:

在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

面试题10:跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

//代码:
            public class Solution {
                public int JumpFloor(int target) {
                    if(target == 0) return 0;
                    if(target == 1) return 1;
                    if(target == 2) return 2;
                    int [] a;
                    a = new int [target];
                    a[0] = 1;
                    a[1] = 2;
                    for(int i=2;i<target;i++) {
                        a[i] = a[i-1] + a[i-2];
                    }
                    return a[target-1];
                }
            }

>

注意:
(1) 这道题主要考察的是递归和循环
(2)是斐波那契数列的变体,只不过是前两个初值不同而已

面试题11:变态跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

//代码:
                public class Solution {
                    public int JumpFloorII(int target) {
                        if(target == 0) return 0;
                        if(target == 1) return 1;
                        int [] a;
                        a = new int [target];
                        a[0] = 1;
                        for(int i=1;i<target;i++) {
                            for(int j=0;j<i;j++) {
                                a[i] += a[j];
                            }
                            a[i] += 1;
                        }
                        return a[target-1];
                    }
                }

注意:
(1) 这道题虽然看上去和上面那道题很像,但是实际上原理相差的就比较多了,虽然也是考的递归和循环,但是需要自己手动找规律,也不属于斐波那契数列了!
下面是大神总结的思路,我的并没有写出最后的那个公式:
这里写图片描述

面试题12: 矩形覆盖

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

//代码:
public class Solution {
    public int RectCover(int target) {
        if(target == 0) return 0;
        if(target == 1) return 1;
        if(target == 2) return 2;
        int [] a ;
        a = new int [target];
        a[0] = 1;
        a[1] = 2;
        for(int i=2;i<target;i++) {
        a[i] = a[i-1]+a[i-2];
        }
        return a[target-1];
    }
}

注意:这道题的思路和青蛙那道一模一样,代码都没变!!

面试题13:位运算

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

代码:
                public class Solution {
                    public int NumberOf1(int n) {
                        if(n == 0) return 0;
                        int count = 0;
                        while(n!=0) {
                            count += 1;
                            n = n & (n-1);
                        }
                        return count;
                    }
                }

思路解析:

如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

猜你喜欢

转载自blog.csdn.net/katrina_ali/article/details/80640200