剑指offer 1-80

记录每天做的题,不定时更新

面试题03. 数组中重复的数字###

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字

/**
方法一:排序后,遍历数组,判断条件:连续两个元素相等
方法二:遍历放入哈希表。时间复杂度O(n)
方法三:遍历元素,与它大小相等的数组下标元素位置交换,如果相同返回这个重复值
**/
class Solution {
    //自己第一次写的,建立了一个数组来保存原始数组的信息,复杂度太高
    // public int findRepeatNumber(int[] nums) {
    //     int []a=new int[nums.length];
    //     for(int i=0;i<nums.length;i++){
    //         a[nums[i]]++;
    //         if(a[nums[i]]>1)
    //             return nums[i];
    //     }
    //     return -1;
    // }
     public int findRepeatNumber(int[] nums) {
       for(int i=0;i<nums.length;){
           if(nums[i]!=i&&nums[i]!=nums[nums[i]]){
               swap(nums,nums[i],i);
           }
           else if(nums[i]==i){
               i++;
           }
           //这个else if必须放在上一个后面,否则不能判断0
           else if(nums[i]==nums[nums[i]]){
               return nums[i];
           }
       }
       return -1;
    }
    //交换元素
    public void swap(int[]nums,int i,int j){
        int temp;
        temp=nums[i];
        nums[i]=nums[j];
        nums[j]=temp;
    }
}

面试题04. 二维数组的查找###

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

/**
思路一:暴力遍历
思路二:将数组左下当做flag值,target小于flag就删除行,target大于flag就删除列,等于就返回。
**/
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        //i是二维数组的行数
        int i=matrix.length-1;
        int j=0;
        //j是二维数组的列数
        while(i>=0&&j<matrix[0].length){
            if(target==matrix[i][j]){
                return true;
            }
            int c=(target<matrix[i][j])?--i:++j;
        }
        return false;
    }
}

面试题05. 替换空格###

因为strng在java里是定长的,需要构造 StringBuilder对象。

class Solution {
    public String replaceSpace(String s) {
        StringBuilder res = new StringBuilder();
        // for(Character c : s.toCharArray())
        // {
        //     if(c == ' ') res.append("%20");
        //     else res.append(c);
        // }
        // return res.toString();
        // }
        int i=0;
        while(i<s.length()){
            char a=s.charAt(i);
            if(a==32)
                res.append("%20");
            else
                res.append(a);
            i++;
        }
        return res.toString();
    }
   
}

面试题06. 从尾到头打印链表###

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
 /**
 思路一:递归链表,把节点的值添加到动态数组里
 思路二:用链表实现一个栈,出栈对应输出
 **/
class Solution {
    private ArrayList<Integer>temp=new ArrayList<Integer>();
    private LinkedList<Integer>stack=new LinkedList<Integer>();
    public int[] reversePrint(ListNode head) {
       //return first(head);
       return second(head);
    }
    //方法一
    private int[] first(ListNode head){
        cur(head);
        int result[]=new int[temp.size()];
        for(int i=0;i<temp.size();i++){
            result[i]=temp.get(i);
        }
        return result;
    }
    private void cur(ListNode head){
        if(head==null)
            return;
        cur(head.next);
        temp.add(head.val);
    }
    //方法二
    private int[] second(ListNode head){
        //入栈
        while(head!=null){
            stack.addLast(head.val);
            head=head.next;
        }
        //出栈
        int[]result=new int[stack.size()];
        for(int i=0;i<result.length;i++){
            result[i]=stack.removeLast();
        }
        return result;
    }
   
}

面试题07. 重建二叉树###

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    HashMap<Integer, Integer> idx_map = new HashMap<>();//用来记录中序遍历的书序
    int[] preorder;
    int[] inorder;
    int pre_index=0;//根节点索引
    public TreeNode buildTree(int[] preorder, int[] inorder) {
       this.preorder = preorder;
        this.inorder = inorder;
        int idx = 0;
        for (Integer val : inorder)
        idx_map.put(val, idx++);
        return helper(0, inorder.length-1);
    }
    TreeNode helper(int left,int right){
        //递归返回条件
        if(left>right)
           return null;
        //1.找出根节点值,根节点在中序遍历中的位置
        int root_val=preorder[pre_index];
        TreeNode root=new TreeNode(root_val);
        int root_inorder=idx_map.get(root_val);

        pre_index++;
        
        //2.根节点指向左指树
        root.left=helper(left,root_inorder-1);
        //3.根节点指向右孩树
        root.right=helper(root_inorder+1,right);
        return root;
    }
}

面试题09. 用两个栈实现队列###

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

//复杂度极高,因为需要在两个栈pop push出数据
class CQueue {
    //思路:appendTail的数据push进stack1,stack1的数据pop到stack2.
    //出队对应stack2的pop,入队对应stack1的push
    private Stack<Integer> stack1=new Stack<Integer>();
    private Stack<Integer> stack2=new Stack<Integer>();//栈有pop,push,peak三个方法
    int size;
    public CQueue() {
        size=0;
    }
    
    public void appendTail(int value) {
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }
        stack1.push(value);
        size++;

    }
    
    public int deleteHead() {
        if(size==0)
            return -1;
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        size--;
        return stack2.pop();
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

面试题10- I. 斐波那契数列###

//显然用递归的思想解题,递归表达式F(N) = F(N - 1) + F(N - 2),返回条件
//递归做法在这里超时了
// class Solution {
//     public int fib(int n) {
//         return recursion(n)%1000000007;
//     }
//     private int recursion(int n){
//         if(n==1)
//             return 1;
//         if(n==0)
//             return 0;
//         int result=recursion(n-1)+recursion(n-2);
//         return result;
//     }
// }
//因为递归里有很多重复计算的值,我们可以用循环自下而上的计算
class Solution {
    public int fib(int n) {
        if(n==0)
            return 0;
        if(n==1)
            return 1;
        int result=0;
        int one=1;
        int two=0;
        for(int i=2;i<=n;i++){
            result=(one+two)%1000000007;
            two=one;
            one=result;

        }
        return result;
    } 
}

面试题11. 旋转数组的最小数字###

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
参考博客:二分法的细节

//遍历复杂度O(N),二分查找O(log(N))
//可以把数组分为两个排序数组,第一个中的数大于等于第二个数组中的数
class Solution {
    public int minArray(int[] numbers) {
        int left=0;
        int right=numbers.length-1;
        while(left<right){
            int mid=(left+right)/2;
            //mid在第一个排序数组中,我们要找的值在后面,所以left=mid+1;
            if(numbers[mid]>numbers[right]){
                left=mid+1;
            //难点:因为数组中可能出现重复值
            }else if(numbers[mid]==numbers[right]){
                right--;
            //mid在第二个数组中,搜索范围往前
            }else if(numbers[mid]<numbers[right]){
                right=mid;
            }
        }
        return numbers[left];
    }
}
posted @ 2020-02-17 21:04  Meditation,  阅读(111)  评论(0)    收藏  举报