初级算法(十)--动态规划、设计问题、数学

初级算法(十)–动态规划、设计问题、数学

1、引言

动态规划

1、爬楼梯
2、买卖股票的最佳时机
3、最大子序和
4、打家劫舍

解题步骤
1、确定dp数组
2、找到状态转移公式
3、确定初始条件以及边界条件
4、计算结果

设计问题

1、打乱数组
2、最小栈

数学

1、Fizz Buzz
2、计数质数
3、3的冥

2、动态规划

2.1 爬楼梯

​ 这题开始想的方法就是使用递归,但是力扣会显示超出时间限制。

	/**
     * 方法一:递归,但是力扣会显示超时
     * @param n
     * @return
     */
    public int climbStairs(int n) {
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        return climbStairs(n-1)+climbStairs(n-2);
    }

​ 第二种方就是经典的使用动态规划,使用相应的解题步骤。dp[i]表示走到第i级台阶有几种方法。

    /**
     * 方法二:非递归的方法,用一个数组表示一个到达几级台阶需要几种方法
     * @param n
     * @return
     * 执行用时:0ms,内存消耗:38MB
     */
    public int climbStairs2(int n){
        if(n==1)
            return 1;
        if(n==2)
            return 2;
        int[] dp=new int[n+1];
        dp[1]=1;
        dp[2]=2;
        for (int i = 3; i <=n ; i++) {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }

2.2 买卖股票的最佳时机

这题的思路也是使用动态规划,不同的是使用的是二维数组作为dp数组。这个二维数组只有两列,列项0代表的是未持有股票 的最大利润,列值代表的是持有股票的最大利润。

    /**
     * 使用动规
     * @param prices
     * @return
     * 执行用时: 24ms,内存消耗: 53.6MB
     */
    public int maxProfit(int[] prices) {
        if(prices==null)
            return 0;
        int n=prices.length;
        //列值代表0是表示,未持有股票的最大利润
        //列值代表1是表示,持有股票的最大利润
        int[][] dp=new int[n][2];
        dp[0][0]=0;
        dp[0][1]=-prices[0];
        for (int i = 1; i < n ; i++) {
            //第i+1天未持有股票的最大利润,有两种情况,一种是在第i+1天已经卖了当天的股票
            //另一种是既没买也没卖
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            //第i+1天持有股票的最大利润,有两种情况,一种是前面的某台买了一种股票,直到现在都没有卖
            //另一种就是买了当天的股票
            dp[i][1]=Math.max(dp[i-1][1],-prices[i]);
        }
       //最后肯定是股票卖出去了才算是利润最大化
        return dp[n-1][0];
    }

2.3 最大子序和

确定dp数组,这题比较难判断dp数组和状态转移方程。dp[i] 表示终点在 i 的最大子序和。至于状态转移方程,则是前面 i-1 的最大子序和为负数的话,就需要从新开始,就是从第 i 项 开始。

    /**
     * 使用动态规划
     * 思路:
     * 1、确定状态
     * 2、找到转移公式
     * 3、确定初始条件以及边界条件
     * 4、计算结果
     * @param nums
     * @return
     * 执行用时: 2ms,内存消耗:51.1MB
     */
    public int maxSubArray(int[] nums) {
        if(nums.length==1)
            return nums[0];
        int n=nums.length;
        int[] dp=new int[n];
        dp[0]=nums[0];
        int max=dp[0];
        for (int i = 1; i <n ; i++) {
            //确定状态转移方程,如果dp[i-1]<0的话,则直接将dp[i]充值为nums[i]的值
            dp[i]=nums[i]+Math.max(dp[i-1],0);
            max=Math.max(max,dp[i]);
        }
        return max;
    }

2.4 打家劫舍

这题的思路和前面买卖股票的最佳时机其实是一样的,dp数组表示为二维数组,有两个列。列值为0代表的是对这家已经打劫过,列值1代表的是没有打劫过。

    /**
     * 打家劫舍
     * @param nums
     * @return
     * 执行用时:0ms,内存消耗:39MB
     */
    public int rob(int[] nums) {
        if(nums.length==1)
            return nums[0];
        int n=nums.length;
        //列值0代表的是拿了钱,列值1代表的是没拿钱
        int[][] dp=new int[n][2];
        dp[0][0]=nums[0];
        dp[0][1]=0;
        for (int i = 1; i <n ; i++) {
            dp[i][0]=Math.max(dp[i-1][1]+nums[i],dp[i-1][0]);
            dp[i][1]=Math.max(dp[i-1][0],dp[i-1][1]);
        }
        return Math.max(dp[n-1][0],dp[n-1][1]);
    }

3、设计问题

3.1 打乱数组

主要是考类的构造

//执行用时:50ms,内存消耗:47.4MB
public class Solution2 {

    private int[] temp;
    private Random random;
    public Solution2(int[] nums) {
        this.temp=nums;
        random=new Random();
    }

    public int[] reset() {
      return temp;
    }

    public int[] shuffle() {
        if(temp==null)
            return null;
        int[] clone = temp.clone();
        for (int i = 1; i < clone.length; i++) {
            int j=random.nextInt(i+1);
            swap(clone,i,j);
        }
        return clone;
    }
    private void swap(int[] a, int i, int j) {
        if (i != j) {
            a[i] = a[i] + a[j];
            a[j] = a[i] - a[j];
            a[i] = a[i] - a[j];
        }
    }

}

3.2 最小栈

这题使用了辅助的数据结构,就是单链表。

//使用辅助数据结构单链表
//执行用时:3ms,内存消耗:43.1MB
public class MinStack {
    ListNode head;
    public void push(int val) {
        if(head==null)
            head=new ListNode(val,val,null);
        else{
           head=new ListNode(val,Math.min(val,head.min),head);
        }
    }

    public void pop() {
        if(head==null)
            throw new IllegalStateException("栈为空");
        head=head.next;
    }

    public int top() {
        if(head==null)
            throw new IllegalStateException("栈为空");
        return head.val;
    }

    public int getMin() {
        if(head==null)
            throw new IllegalStateException("栈为空");
        return head.min;
    }
    class ListNode{
        public int val;
        public int min;
        public ListNode next;
        public ListNode(int val, int min, ListNode next) {
            this.val = val;
            this.min = min;
            this.next = next;
        }
    }

}

4、数学

4.1 Fizz Buzz

这个问题简单,就是几个 if else 语句判断一下。

    /**
     * Fizz Buzz
     * 思路:循环中判断就行
     * @param n
     * @return
     * 执行用时:1ms,内存消耗;42.3MB
     */
    public List<String> fizzBuzz(int n) {
        List<String>  answer=new ArrayList<>();
        for (int i = 1; i <=n; i++) {
            if(i%3==0 && i%5==0){
                answer.add("FizzBuzz");
            }else if(i%3==0){
                answer.add("Fizz");
            }else if(i%5==0){
                answer.add("Buzz");
            }else{
                answer.add(String.valueOf(i));
            }
        }
        return answer;
    }

4.2 计数质数

​ 这题如果用原始的判断质数的方法,力扣会显示超出时间限制。最好的方式使用埃氏筛。利用素数的各个倍数的等差还是它本身的方法,进行筛选。

    /**
     * 使用埃氏筛,从素数2开始,一个素数的倍数的合数进行标记,且为等差数列,差的就是其本身
     * @param n
     * @return
     * 执行用时: 157ms,内存消耗: 68MB
     */
    public int countPrimes(int n) {
        int[] isPrime = new int[n];
        Arrays.fill(isPrime, 1);
        int ans = 0;
        for (int i = 2; i < n; ++i) {
            if (isPrime[i] == 1) {
                ans += 1;
                if ((long) i * i < n) {
                    for (int j = i * i; j < n; j += i) {
                        isPrime[j] = 0;
                    }
                }
            }
        }
        return ans;
    }

4.3 3 的幂

可以用递归的方式求解

    /**
     * 判断n是否是3的幂次方
     * 思路:使用递归
     * @param n
     * @return
     * 执行用时: 8ms,内存消耗:40.9MB
     */
    public boolean isPowerOfThree(int n) {
        if(n==0)
            return false;
        if(n%3==0)
            return isPowerOfThree(n/3);
        else if(n==1)
            return true;
        else
            return false;
    }

也可以根据题目给的范围,来一个“高效”的解决办法

     /**
     * 充分利用题目的范围,如果n是3的幂次方,那么它一定能够被1162261467整除
     * @param n
     * @return
     * 执行用时: 7ms,内存消耗:41.2MB
     */
    public boolean isPowerOfThree2(int n) {
        return (n > 0 && 1162261467 % n == 0);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值