面试手撕代码(6)

如何判断一个字符串是数字

public class Main11 {
    public static void main(String[] args) {
        String s1 = "-12418413212";
        System.out.println(isInteger(s1));
    }
    //只能判断正整数
    public static boolean isNumeric(String str) {
        for (int i = str.length(); --i >= 0;) {
            if(!Character.isDigit(str.charAt(i))){
                return false;
            }
        }
        return true;
    }
    //正则,速度快
    public static boolean isInteger(String str) {
        Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
        return pattern.matcher(str).matches();
    }
}

有1亿个数字其中有2个是重复的,快速找到该数字——位图

考查位图,把数字值直接映射到数组下标,这里重复的数字只有两次,为了空间最优,就用位图bit来表示(只有0和1),1byte=8bit,1byte可以存储8个数字的计数,所以建立一个byte数组用来计数:
byte[] bucket=new byte[(最大值-最小值)/8+1];

public class findRepeatable{
    public static void main(String[] args){
        int[] arr=new int[100000000];//1亿长度
        for(int i=0;i<arr.length;i++){
            arr[i]=i+1;
        }
        arr[99999999]=2020;
        long time=new Date().getTime();
        int res = core(arr);
        long time2=new Date().getTime();
        System.out.println("repeat number:"+res);
        System.out.println("millisecond:"+(time2-time));
    }
    public static int core(int[] arr){
    	//O(N)的时间或者最小值和最大值
    	int min=arr[0];
        int max=arr[0];
        for(int i=0;i<arr.length;i++){
            if(arr[i]<min)
                min=arr[i];
            if(arr[i]>max)
                max=arr[i];
        }
        //空间复杂度O(N)
        byte[] bucket=new byte[(max-min)/8+1];
        for(int i=0;i<arr.length;i++){
            int num=arr[i];
            int j=(num-min)/8;
            int k=(num-min)%8;
            if(((bucket[j]>>k)&1)>0){//重复了
                System.out.println("Number of repeats:"+num);
                break;
            }else{
               bucket[j]|=(1<<k);
            }
        }
    }
}


从10万个数中找10个最大的数

对海量数据处理,一般是先分块,然后处理后合并

1.先用某种规则分块
2.为了找到最大的10个元素,则用大小为10的最小堆
3.最后将每一块再进行合并


300. 最长上升子序列

动态规划,时间复杂度O(n^2)

class Solution {
    public int lengthOfLIS(int[] nums){
        if(nums.length==0) return 0;
        int[] dp=new int[nums.length];
        Arrays.fill(dp,1);
        int res=0;
        for(int i=0;i<nums.length;i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            res=Math.max(res,dp[i]);
        }
        return res;
    }
}

二分查找,时间复杂度O(nlog(n))
定义一个tail 数组,其中 tail[i] 表示长度为 i + 1 的上升子序列的末尾最小是几。

class Solution {
    public int lengthOfLIS(int[] nums){
        int len = nums.length;
        if(len <= 1) return len;
        // tail 数组的定义:tail[i] 表示长度为 i + 1 的上升子序列的末尾最小是几
        int[] tail = new int[len];
        tail[0] = nums[0];
        //end表示最后一个被赋值的索引
        int end = 0;
        for (int i = 1; i < len; i++) {
            //如果新来的数字比tail尾数大的话,直接添加进来
            if(nums[i] > tail[end]){
                end++;
                tail[end] = nums[i];
            }else{//否则使用二分查找法找到第一个比该数字大的数然后更新,如果相等就不用更新
                int left = 0;
                int right = end;
                while (left < right){
                    int mid = (left + right) >>> 1;
                    if(tail[mid] < nums[i]){
                        left = mid + 1;
                    }else{
                        right = mid;
                    }
                }
                //这里的tail[left] >= nums[i]
                tail[left] = nums[i];
            }
        }
        return end + 1;
    }
}

150. 逆波兰表达式求值

逆波兰表达式:逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

用Stack写,容易理解

public class No150 {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (String s : tokens){
            if(s.equals("+")){
                stack.push(stack.pop() + stack.pop());
            }else if(s.equals("-")){
                stack.push(- stack.pop() + stack.pop());
            }else if(s.equals("*")){
                stack.push(stack.pop() * stack.pop());
            }else if(s.equals("/")){
                int num = stack.pop();
                stack.push(stack.pop() / num);
            }else {
                stack.push(Integer.parseInt(s));
            }
        }
        return stack.pop();
    }
    //switch case代替if else 可以优化效率
    public static int evalRPN2(String[] tokens) {
        Stack<Integer> numStack = new Stack<>();
        Integer op1, op2;
        for (String s : tokens) {
            switch (s) {
                case "+":
                    op2 = numStack.pop();
                    op1 = numStack.pop();
                    numStack.push(op1 + op2);
                    break;
                case "-":
                    op2 = numStack.pop();
                    op1 = numStack.pop();
                    numStack.push(op1 - op2);
                    break;
                case "*":
                    op2 = numStack.pop();
                    op1 = numStack.pop();
                    numStack.push(op1 * op2);
                    break;
                case "/":
                    op2 = numStack.pop();
                    op1 = numStack.pop();
                    numStack.push(op1 / op2);
                    break;
                default:
                    numStack.push(Integer.valueOf(s));
                    break;
            }
        }
        return numStack.pop();
    }
}

用数组模拟栈来优化,很巧妙,数组最大长度tokens.length / 2 + 1

class Solution {
    public int evalRPN(String[] tokens) {
        int[] numStack = new int[tokens.length / 2 + 1];
        int index = 0;
        for (String s : tokens) {
            switch (s) {
                case "+":
                    numStack[index - 2] += numStack[--index];
                    break;
                case "-":
                    numStack[index - 2] -= numStack[--index];
                    break;
                case "*":
                    numStack[index - 2] *= numStack[--index];
                    break;
                case "/":
                    numStack[index - 2] /= numStack[--index];
                    break;
                default:
                    numStack[index++] = Integer.parseInt(s);
                    break;
            }
        }
        return numStack[0];
    }
}

406. 根据身高重建队列

贪心算法:
首先排序,身高降序排列,索引升序排列
然后逐个放到队列中去,索引值等于他们的k值
最后返回输出的队列

public class No406 {
    public int[][] reconstructQueue(int[][] people) {
//        Arrays.sort(people, new Comparator<int[]>() {
//            @Override
//            public int compare(int[] o1, int[] o2) {
//                //身高降序排列,索引升序排列
//                return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
//            }
//        });

        Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);

        List<int[]> output = new LinkedList<>();
        for (int[] p : people){
            output.add(p[1], p);
        }
        return output.toArray(new int[0][2]);
    }
}

字符串压缩

190. 颠倒二进制位

98. 验证二叉搜索树

class Solution {
    // long pre=Long.MIN_VALUE;
    // public boolean isValidBST(TreeNode root) {
    //     if(root==null) return true;
    //     if(!isValidBST(root.left)){
    //         return false;
    //     }
    //     if(root.val<=pre){
    //         return false;
    //     }
    //     pre=root.val;
    //     return isValidBST(root.right);
    // }
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode pre = null;//前驱指针
        while (root != null || !stack.isEmpty()){
            while (root != null){
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            //处理最左边的这个节点
            if(pre != null && root.val <= pre.val) return false;
            pre = root;
            root = root.right;
        }
        return true;
    }
}

518 零钱兑换——背包问题

class Solution {
    //完全背包问题
    //状态:背包的容量,可选择的物品
    //选择:装或者不装
    //dp的含义:dp[i][j]表示只用前i个硬币的面值,凑出金额j有dp[i][j]种方法
    //base case:凑出的⽬标⾦额为0
    //状态转移方程:(1)不把第i个物品装进去:dp[i][j]=dp[i-1][j];(2)装进去dp[i][j]=dp[i][j-coins[i-1]]
    public int change(int amount, int[] coins) {
        int n=coins.length;
        int[] dp=new int[amount+1];
        dp[0]=1;//base case
        for(int i=0;i<n;i++){//对应数组coins[i-1]
            for(int j=1;j<=amount;j++){
                if(j-coins[i]>=0){
                    dp[j]=dp[j]+dp[j-coins[i]];
                }
            }
        }
        return dp[amount];
    public int change(int amount, int[] coins) {
        int n=coins.length;
        int[][] dp=new int[n+1][amount+1];
        for(int i=0;i<=n;i++){
            dp[i][0]=1;//base case,如果凑出的⽬标⾦额为 0,那么“⽆为⽽治”就是唯⼀的⼀种凑法。
        }
        for(int i=1;i<=n;i++){//第i类硬币,对应数组coins[i-1]
            for(int j=1;j<=amount;j++){
                if(j-coins[i-1]>=0){
                    dp[i][j]=dp[i-1][j]+dp[i][j-coins[i-1]];
                }else{
                    dp[i][j]=dp[i-1][j];
                }
            }
        }
        return dp[n][amount];
    }
}

695. 岛屿的最大面积

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int res=0;
        if(grid.length==0||grid[0].length==0) return res;
        for(int row=0;row<grid.length;row++){
            for(int col=0;col<grid[0].length;col++){
                if(grid[row][col]==1){
                    int area=Area(grid,row,col,grid.length,grid[0].length);
                    res=Math.max(area,res);
                }
            }
        }
        return res;
    }
    public int Area(int[][] grid,int i,int j,int rows,int cols){
        //base case,越界或者已经访问过
        if(i<0||j<0||i>rows-1||j>cols-1||grid[i][j]!=1){
            return 0;
        }
        //访问过,直接赋值2
        grid[i][j]=2;
        //四周发散
        return 1+Area(grid,i-1,j,rows,cols)+Area(grid,i+1,j,rows,cols)+Area(grid,i,j-1,rows,cols)
            +Area(grid,i,j+1,rows,cols);
    }
}

264. 丑数

148. 排序链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode sortList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        // 获取链表长度
        int len = listNodeLength(head);
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        //两两归并,四四归并,八八归并,需要循环logn次
        for (int i=1;i<len;i<<=1){
            ListNode pre = dummy;
            ListNode cur = dummy.next;
            while (cur!=null){//遍历整个链表,cur是每次移动两组链表
                ListNode left = cur;
                ListNode right = split(left,i);
                cur = split(right,i);//cur相当于跳过两个分割链表的长度了
                pre.next = mergeTwoLists(left,right);
                while (pre.next!=null){
                    pre = pre.next;
                }
            }
        }
        return dummy.next;

    }
    /**
     * 获取链表长度
     * @param head
     * @return
     */
    public int listNodeLength(ListNode head){
        int len=0;
        ListNode cur=head;
        while (cur!=null){
            len++;
            cur=cur.next;
        }
        return len;
    }

    /**
     * 根据步长分割链表
     * @param head
     * @param step
     * @return
     */
    public ListNode split(ListNode head,int step){
        if(head==null) return null;
        for (int i=1;head.next!=null && i<step;i++) {//防止越界
            head=head.next;
        }
        ListNode right=head.next;
        head.next=null;//要断开链表,这步别漏
        return right;
    }

    /**
     * 合并两个链表
     * @param l1
     * @param l2
     * @return
     */
    public ListNode mergeTwoLists(ListNode l1,ListNode l2){
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        while (l1!=null && l2!=null){
            if(l1.val<l2.val){
                cur.next=l1;
                l1=l1.next;
            }else{
                cur.next=l2;
                l2=l2.next;
            }
            cur=cur.next;
        }
        cur.next=l1==null?l2:l1;
        return dummy.next;
    }
}

64. 最小路径和

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值