校招必备之剑指offer JAVA 全代码多实现【1~10】

4 篇文章 0 订阅
3 篇文章 0 订阅

校招必备之剑指offer JAVA 全代码多实现【1~10】

校招必备之剑指offer JAVA 全代码多实现【11~20】

校招必备之剑指offer JAVA 全代码多实现【21~30】

​​​​​​​

1、题目描述

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

思路1:暴力搜索,时间复杂度o(n^2),就不贴出来了,很简单。

思路2:因为所给二维数组的特殊性,所以可以采取线性时间复杂度,即o(n)。所走的轨迹类似于阶梯。arr[i][j]下方和右方的数大于本身,所以一旦发现target大于arr[i][j],所以可以往下或者往右所搜。又因为我们一开始是从最右开始搜索,即arr[i][j]是本行最大的元素,所以可以直接向下,直到找到数字。

public class Solution {
    public boolean Find(int target, int [][] array) {
        int m=array.length;//判空
        if(m==0){
            return false;
        }
        int n=array[0].length;
        for(int i=0;i<m;i++){
            for(int j=n-1;j>=0;j--){
                if(array[i][j]<target){
                    break;
                }else if(array[i][j]==target){
                    return true;
                }
            }
        }
        return false;
    }
}

2、题目描述

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

思路1:左遍历直接创建新的StringBuffer,将所获取的字符添加进去,遇到‘ ’则添加"%20"

public class Solution {
    public String replaceSpace(StringBuffer str) {
    	if(str.length()==0||str==null) return "";
        int n=str.length();
	    StringBuffer sb=new StringBuffer();
         for(int i=0;i<n;i++){
	            char ch=str.charAt(i);
	            if(ch==' ') {
	            	sb.append("%20");
	            	continue;
	            }
	            sb.append(ch);
	        }
        return sb.toString();
    }
}

思路2:右替换,先获取总长度,每次遇到空格+2,其余字符+1,然后将目标字符串从右开始移动到位置上。代码不贴了,知道思路就行。

3、题目描述

输入一个链表,按链表从尾到头的顺序返回一个ArrayList。【注意是从尾到头】

思路1:直接用头插法,add(int index,Object element);

import java.util.ArrayList;
public class Solution {
    
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {   
        ArrayList<Integer> res=new ArrayList<>();
        while(listNode!=null){
            res.add(0,listNode.val);
            listNode=listNode.next;
        }
        return res;
    }
}

思路2:递归思想,递归到最后一个非空的结点,然后开始往回添加。

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

思路3:利用栈

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

4、题目描述

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

思路:通过前序遍历在中序遍历中找到根节点。例如第一遍前序遍历得“1”,那么在中序遍历我们可以得到根节点为1,那么472组成了1的左子树,5386组成了右子树。然后通过递归以此类推

public class Solution {
    
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        return getNode(pre,in,0,0,in.length);
    }
    public TreeNode getNode(int[] preorder,int[] inorder,int preStart,int inStart,int endStart){
        if(preStart>preorder.length-1||inStart>=endStart){
            return null;
        }
        TreeNode node=new TreeNode(preorder[preStart]);
        int index=-1;
        for(int i=inStart;i<endStart;i++){
            if(inorder[i]==node.val){
                index=i;
            }
        }
         node.left=getNode(preorder,inorder,preStart+1,inStart,index);
         node.right=getNode(preorder,inorder,preStart+index-inStart+1,index+1,endStart);
         return node;
    }
    
}

5、题目描述

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

思路1:简单的排序题,直接O(n)暴力跑一遍。

import java.util.ArrayList;
import java.lang.Math;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        if(array==null||array.length==0) return 0;
        int min=array[0];
        for(int i=0;i<array.length;i++){
            min=Math.min(min,array[i]);
        }
        return min;
    }
}

思路2:因为经过旋转后可以看做是两个递增的子数组,且第一个数组的最小值必然大于第二个数组的最大值;因此可以用二分查找的思想。这里我觉得别人讲的十分好,就复制了别人的过程

需要考虑三种情况:

(1)array[mid] > array[high]:

出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。

low = mid + 1

(2)array[mid] == array[high]:

出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边

还是右边,这时只好一个一个试 ,

high = high - 1

(3)array[mid] < array[high]:

出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左

边。因为右边必然都是递增的。

high = mid

注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字

比如 array = [4,6]

array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;

如果high = mid - 1,就会产生错误, 因此high = mid

但情形(1)中low = mid + 1就不会错误

public class Solution {
    public int minNumberInRotateArray(int [] array) {//3456778123
        if(array.length==0) return 0;
        int left=0,right=array.length-1;
        int mid=0;
        while(left<right){
            mid=(left+right)>>1;
            if(array[mid]>array[right]){
                left=mid+1;
            }else if(array[mid]==array[right]){
                right=right-1;
            }else{
                right=mid;
            }
        }
        return array[left];
    }
}

6、题目描述

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

非常常见经典的算法题

思路1:递归,很简单,但是随着n的增长,大量的重复计算,开销很大,最好用其他方式。时间复杂度大概在O(2^n)

public class Solution {
    public int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1||n==2) return 1;
        return Fibonacci(n-1)+Fibonacci(n-2);
    }
}

思路2:循环 O(n)

public class Solution {
    public int Fibonacci(int n) {
        if(n==0) return 0;
        if(n==1) return 1;
        int first=0;
        int second=1;
        int temp;
        while(n>=2){
            temp=second;
            second=first+second;
            first=temp;
            n--;
        }
        return second;
        
    }
}

7、题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

 又是一道经典的入门动态规划。

思路1:假设要上N阶台阶,可以从N-1阶上,也可以从N-2阶台阶上,所以F(N)=F(N-1)+F(N-2)。有了递推式就可以设置初始值来DP

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

思路2:我们也可以很容易发现,其实就是斐波那契数列,也如上一题解决;

public class Solution {
    public int JumpFloor(int target) {
        if(target==1) return 1;
        if(target==2) return 2;
        return JumpFloor(target-1)+JumpFloor(target-2);
    }
}

8、题目描述

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

注:这个牛客网上标注的贪心,看的我一脸疑惑?是我对贪心有误解?感觉就是找规律啊==,觉得这种题蛮无聊的,数学题。

思路:因为第n级可由任意之前一级到达,所以F(n)=F(n-1)+F(n-2)+……+F(1)+1【注1代表从地上直接跳上n级】

而F(n-1)恰好等于后面那一坨,换句话说F(n)=2*F(n-1)。然后就随便做了递归迭代想怎么做都可以。老实说个人感觉这题从算法上说蛮垃圾的。。。代码就最简单的递归。

public class Solution {
    public int JumpFloorII(int target) {
        if(target==1) return 1;
        return 2*JumpFloorII(target-1);
    }
}

9、题目描述

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

思路:类似DP的做法。前K个共有f(k)种方法。横着放g(k),竖着放h(k) 可以得到表达式f(k)=g(k-1)+h(k-1)

放第K个方块时,如果竖着放,ok没有任何问题。可得h(k)=f(k-1)

如果横着放,就需要再放一块来重新组成矩形 。我们不难看出,如果k+1需要横着放,必须第k个也横着放,即g(k+1)=f(k-1)。这里很容易可以理解将k+1换成k时,得到g(k)=f(k-2)

所以结合以上两种情况,f(k)=f(k-1)+f(k-2),此时我们不难看出其实也就是斐波那契数列。

所以解法同上,不贴代码了。

 

10、题目描述

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

思路:一开始想的是一直和1做与运算,然后与出来的是最后结果是1则说明最后一位是1,然后一直右移,后来发现死循环了。原因是负数右移,最高位会补1导致最后-1的补码是全1,然后死循环。

后来看了别人的思考过程,才明白解法。首先要有这个预备知识,否则大概率是无法以这种最优解法做出来的。首先来回顾一下原码、反码、补码定义。

原码:正负数不变

反码:正数不变,负数除最高位符号位外,其余位取相反数。

补码:正数不变,负数在反码的基础上最后一位+1

然后我们假定一个数 -5 它的补码是 1011 ,而-6的补码是1010 不难看出 当-5和-6相与时,得到1010也就是最后一位1将会被置0。由于篇幅限制,可以自行验证。所以我们可以得到n&(n-1)可使最后一位1置0,有多少个1就可以做多少次这个运算,直到n=0,所以代码也很容易就出来了。

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值