剑指offer 题目详解

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

题目: 一个二维数组,每一行按照从左到右递增,每一列按照从上到下递增,查找数组中是否存在某个数。如数组:java

1  2  8    9node

2  4  9   12c++

4  7  10  13面试

6  8  11  15算法

 

思路:这道题有其特殊性,从右上角或者左下角开始查找。好比是查找7,咱们从右上角开始,9大于7,则排除最右边一列,减小列下标,查找13的话就增长行下标,减小一行,查找的方向是肯定的,这样就容易实现了。数组

 

    public static void main(String args[]) {  
        // 测试用的例子  
        int a[][] = { { 1, 2, 8, 9 }, { 2, 4, 9, 12 }, { 4, 7, 10, 13 },  
                { 6, 8, 11, 15 } };  
        System.out.println(find(a, 7));  
    }  
    public static boolean find(int array[][], int number) {
        int row=array.length;
        int col=array[0].length;
        int start=0;
        int end=col-1;
        while(start<row&&end>=0) {
            if(array[start][end]==number) {
                return true;
            }else if(array[start][end]<number){
                start++;
            }else {
                end--;
            }
            
        }
        return false;
    }

 

二、面试题4:替换空格

 

题目大体为:app

    实现一个函数,把字符串中的每一个空格替换成"%20"。函数

思路:这道题其实考察的是替换成新的值后,字符串变长会覆盖后面原有的值。可是c++的字符串和java是不同的,c++是用数组生成的字符串,因此会覆盖后面的值。所以本题咱们首先实现java下的要求,并增长一例,数组状况下的替换方法。测试

首先是字符串的形式:this

    public static void main(String args[]) {  
        String s = "We are happy.";
        String a=replace(s);
        System.out.println(a);
    }  
    public static String replace(String s) {
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<s.length();i++) {
            if(s.charAt(i)==' ') {
                sb.append("%20");
            }else {
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }

 而后是数组形式:

    public static void main(String args[]) {  
        String s = "We are  happy.  ";
        char[] a=s.toCharArray();
        System.out.println(replace1(a));
    }  
    public static char[] replace1(char[] a) {
        int sum=0;
        for(int i=0;i<a.length;i++) {
            if(a[i]==' ') {
                sum++;
            }
        }
        char[] newLength=new char[a.length+sum*2];
        int index=newLength.length-1;
        for(int i=a.length-1;i>=0;i--) {
            if(a[i]==' ') {
                newLength[index--]='0';
                newLength[index--]='2';
                newLength[index--]='%';
            }else {
                newLength[index--]=a[i];
            }
        }
        return newLength;
    }

3.面试题5:从尾到头打印链表

题目: 输入一个链表的头结点,从尾到头反过来打印每一个结点的值。

用一个栈去存储当前节点的值,顺序遍历下去,最后从栈中pop中便可。

此处给出两种解法,一种递归,一种迭代。

    public static void main(String args[]) {  
            ListNode head=new ListNode(0);
        ListNode one=new ListNode(1);
        ListNode two=new ListNode(2);
        ListNode three=new ListNode(3);
        ListNode four=new ListNode(4);
        ListNode five=new ListNode(5);
        head.setNext(one);
        one.setNext(two);
        two.setNext(three);
        three.setNext(four);
        four.setNext(five);
        five.setNext(null);
        printListReverse_1(head);
        System.out.println();
        printListReverse_2(head);
        System.out.println();
    }
    public static void printListReverse_1(ListNode head) {
        Stack<ListNode> stack=new Stack<>();
        while(head!=null) {
            stack.push(head);
            head=head.getNext();
        }
        while(!stack.isEmpty()) {
            System.out.print(stack.pop().getValue()+ " ");
        }
        
    }
    public static void printListReverse_2(ListNode head) {
        if(head!=null) {
            if(head.getNext()!=null) {
                printListReverse_2(head.getNext());
            }
            System.out.print(head.getValue()+", ");
        }
    }

 

四、面试题6:重建二叉树

题目:已知前序遍历序列和中序遍历序列,要求重建二叉树

思路:(leetcode中相似)

假设树的先序遍历是12453687,中序遍历是42516837。

这里最重要的一点就是先序遍历能够提供根的所在,而根据中序遍历的性质知道根的所在就能够将序列分为左右子树。

好比上述例子,咱们知道1是根,因此根据中序遍历的结果425是左子树,而6837就是右子树。

接下来根据切出来的左右子树的长度又能够在先序便利中肯定左右子树对应的子序列(先序遍历也是先左子树后右子树)。

根据这个流程,左子树的先序遍历和中序遍历分别是245和425,右子树的先序遍历和中序遍历则是3687和6837,咱们重复以上方法,能够继续找到根和左右子树,直到剩下一个元素。

能够看出这是一个比较明显的递归过程,对于寻找根所对应的下标,咱们能够先创建一个HashMap,以避免后面须要进行线行搜索,这样每次递归中就只须要常量操做就能够完成对根的肯定和左右子树的分割。

public class BinaryTreeNode {  
    private int value;  
    private BinaryTreeNode left;  
    private BinaryTreeNode right;  
  
    public BinaryTreeNode(int value) {  
        this.value = value;  
    }  
  
    public BinaryTreeNode(int value, BinaryTreeNode left, BinaryTreeNode right) {  
        this.value = value;  
        this.left = left;  
        this.right = right;  
    }  
  
    public int getValue() {  
        return value;  
    }  
  
    public void setValue(int value) {  
        this.value = value;  
    }  
  
    public BinaryTreeNode getLeft() {  
        return left;  
    }  
  
    public void setLeft(BinaryTreeNode left) {  
        this.left = left;  
    }  
  
    public BinaryTreeNode getRight() {  
        return right;  
    }  
  
    public void setRight(BinaryTreeNode right) {  
        this.right = right;  
    } 
    
}
public class Item {
    public static void main(String args[]) {  
        int preOrder[] = { 1, 2, 4, 5, 3, 6, 8, 7 };  
        int inOrder[] = { 4,2,5,1,6,8,3,7 };  
        Map<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<inOrder.length;i++) {
                map.put(inOrder[i], i);
        }
        BinaryTreeNode root = constructTree(preOrder, 0,preOrder.length-1,inOrder,0,inOrder.length-1,map);  
    }

    private static BinaryTreeNode constructTree(int[] preOrder, int preStart,int preEnd,int[] inOrder,int inStart,int inEnd,Map<Integer,Integer> map) {
        if(preStart>preEnd||inStart>inEnd) {
            return null;
        }
        BinaryTreeNode root=new BinaryTreeNode(preOrder[preStart]);
        int index=map.get(root.getValue());
        root.setLeft(constructTree(preOrder,preStart+1,index-inStart+preStart,inOrder,inStart,index-1,map));
        root.setRight(constructTree(preOrder,index-inStart+preStart+1,preEnd,inOrder,index+1,inEnd,map));
        return root;
    }
    
}  

 

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

题目:用两个栈实现队列的两个函数appendTail和deleteHead。

思路:栈的特性是:后进先出,而队列的特性是:先进先出。这里使用两个栈实现队列有点负负得正的意思。栈1负责添加,而栈2负责删除。

package test;

import java.util.Stack;

//基于链表实现的下压堆栈


public class SQueue<T> {  
    private Stack<T> stack1;
    private Stack<T> stack2;
    public SQueue () {
        this.stack1=new Stack<>();
        this.stack2=new Stack<>();
    }
    public void appendTail(T node) {
        stack1.push(node);
    }
    public T deleteHead() {
        if(stack2.isEmpty()) {
            if(stack1.isEmpty()) {
                try{
                    throw new Exception("队列为空");
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }else {
                while(!stack1.isEmpty()) {
                    stack2.push(stack1.pop());
                }
                
            }
        }
        return stack2.pop();
    }
}
  

        
        
public class Item {
    public static void main(String args[]) {  
        SQueue<Integer> sq=new SQueue<>();
        for(int i=0;i<5;i++) {
            sq.appendTail(i);
        }
        for (int i = 0; i < 5; i++) {  
            System.out.print(sq.deleteHead() + "、");  
        }  
        System.out.println();  
    }
    
}  

 

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

题目:一个递增排序的数组的一个旋转(把一个数组最开始的若干元素搬到数组的末尾,称之为数组的旋转),输出旋转数组的最小元素。

思路:不论怎么旋转总有一半是排序好的。咱们就找排序好的那一半,二分解决。同时考虑重复的状况。

假设原数组是{1,2,3,3,3,3,3},那么旋转以后有多是{3,3,3,3,3,1,2},或者{3,1,2,3,3,3,3},

这样的咱们判断左边缘和中心的时候都是3,咱们并不知道应该截掉哪一半。

解决的办法只能是对边缘移动一步,直到边缘和中间不在相等或者相遇,这就致使了会有不能切去一半的可能。

因此最坏状况就会出现每次移动一步,总共移动n此,算法的时间复杂度变成O(n)。

    public static void main(String args[]) {  
            int a[] = {3,1,2,3,3,3,3};
        System.out.println(findMin(a));  
    }
    public static int findMin(int[] nums) {
        int left=0;
        int right=nums.length-1;
        while(left<right) {
            int mid=left+(right-left)/2;
            if(nums[mid]>nums[right]) {
                left=mid+1;
            }else if(nums[mid]<nums[left]) {
                right=mid;
            }else {
                right--;
            }
        }
        return nums[left];
    }      

 

面试题9:斐波那契数列

思路:用递归实现的过程当中会出现重复计算的状况,此时,能够利用动态规划的思想,保存中间结果,这样能够避免没必要要的计算。

public class Item09 {  
    public static void main(String args[]) {  
        int n = 3;  
        System.out.println(fibonacci(n));  
    }  
  
    public static int fibonacci(int n) {  
        if (n == 0) {  
            return 0;  
        } else if (n == 1) {  
            return 1;  
        } else {  
            //由zero和one保存中间结果  
            int zero = 0;  
            int one = 1;  
            int fN = 0;  
            for (int i = 2; i <= n; i++) {  
                fN = one + zero;  
                zero = one;  
                one = fN;  
            }  
            return fN;  
        }  
    }  
}  

面试题10:二进制中1的个数

题目:实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
思路:先分析把一个数减去1的状况,若是该二进制数最右边一位为1,那么减去1为0,也就是最后一位至关于作了取反操做。
接着假设该二进制数最后一位不为1,而是0,若是最右边的1位于第m位,那么减去1时,第m位由1变成0,而第m位以后的全部0都变成1,整数中第m位以前的全部位都保持不变。。举个例子:一个二进制数1100,减1后变成1011。
综合上面两种状况,把一个整数减去1,都是把最右边的1变成0,若是他的右边还有0的话,全部的0都变成1,而它左边的全部位保持不变。因此思路就是把一个整数减去1,再与原整数作与运算,就会把最右边的第一个1变成0,那么一个整数的二进制中有多少个1,就能够进行多少次这样的操做。
public class Item10 {  
    public static void main(String args[]) {  
  
        int n = 9;  
        System.out.println("9的二进制表示中1的个数为:" + numberOf1(n));  
  
    }  
      
    /** 
     * 利用了与的操做 
     * @param n 
     * @return 
     */  
    public static int numberOf1(int n) {  
        int count = 0;  
  
        while (n != 0) {  
            count++;  
            n = (n - 1) & n;  
        }  
  
        return count;  
    }  
  
}  

还有一些相关的题目:

好比用一条语句判断一个数是否是2的整数次方。一个整数若是是2的整数次方,那么它的二进制表示中有且只有一位是1,而其余全部位都是0。根据以前的分析,咱们只要判断该数减一后,再跟以前的数进行与运算获得的结果是否为0,便可。

 

面试题11:数值的整数次方

 
题目: 实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不须要考虑大数问题。

思路:须要考虑到 exponent为负数或者为0的状况,因此用传统的for循环就不行了,所以咱们提出以下解法,考虑对指数折半,这样只须要计算一半的值,若指数是奇数,则-1再折半,不然直接折半。

public class Item11 {  
    public static void main(String args[]) {  
        int base = 2;  
        int exponent_1 = 9;  
        int exponent_2 = 10;  
        System.out.println("当exponent为奇数:" + power(base, exponent_1));  
        System.out.println("当exponent为偶数:" + power(base, exponent_2));  
    }  
  
    /** 
     * 整数次方 
     *  
     * @param base底 
     * @param exponent指数 
     * @return 
     */  
    public static double power(double base, int exponent) {  
        if (exponent == 0) {  
            return 1;  
        }  
  
        if (exponent == 1) {  
            return base;  
        }  
        if (exponent >> 1 == 0) {// 偶数  
            int exponent_1 = exponent >> 1;  
            double tmp = power(base, exponent_1);  
            return tmp * tmp;  
        } else {// 非偶数  
            int exponent_2 = exponent - 1;// -1后就是偶数  
            double tmp = power(base, exponent_2);  
            return tmp * base;// 最后还有先开始减掉的一个base  
        }  
    }  
}  
相关文章
相关标签/搜索