剑指offer算法系列

大数相加 实现任意两个整数的加法

当数字大到超过long的范围后是会发生溢出的,所以在这里把它当作字符串进行处理。按照正常的思路,我们自己在计算数字相加的时候,从低位开始,每次记录当前的进位,与下两个数字相加,当思路清晰了,解决问题的方法也就简单明了
代码如下:

public static String BigNumberPlus(String one,String two){

        int lengthOne = one.length() , lengthTwo = two.length() , length = lengthOne;
        String leaveStr = "" ;
        if(lengthOne < lengthTwo){
            length = lengthOne;
            leaveStr = two.substring(0,lengthTwo- length);
        }
        else{
            length = lengthTwo;
            leaveStr = one.substring(0,lengthOne - length);
        }

        String res = "";
        int mod = 0;
        for(int i = length - 1;i >= 0;i--){
            int o = one.charAt(i) - '0';
            int t = two.charAt(i) - '0';
            int temp = o + t + mod;
            res += temp % 10;
            mod = temp / 10;
        }

        for(int i = leaveStr.length() - 1;i >= 0;i--){
            int l = leaveStr.charAt(i);
            int temp = l + mod;
            res += temp % 10;
            mod = temp / 10;
        }

        return new StringBuilder(res).reverse().toString();
    }

不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?
这里写图片描述
例如,上图是一个7 x 3 的网格。有多少可能的路径?

说明:m 和 n 的值均不超过 100。

示例 1:

输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:

输入: m = 7, n = 3
输出: 28

思路:动态规划,因为只能向右,或者向下搜索。反过来看,会发现所在的路线都一定会经历,起点所在的行与列上,也就是road[0][i]、road[i][0]。并且设置最开始road[0][0] = 1;(i,j)所在位置只和左边和上边有关系;即road[i][j] = road[i][j-1] + road[i-1][j];
代码如下:

    public int uniquePaths(int m, int n) {
        int [][]dp = new int[m][n];
        dp[0][0] = 1;
        for(int i = 0;i < m;i++)
            for(int j = 0;j < n;j++){
            if(i - 1 >= 0)
                dp[i][j] += dp[i-1][j];
            if(j - 1 >= 0)
                dp[i][j] += dp[i][j-1];
            }
        return dp[m-1][n-1];
    }

不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

这里写图片描述

网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

思路:
与上面一个题思路大致相同,不过由于多了障碍,所以多了障碍判断,其次由于障碍物设置为1,那么就把初始值设置为-1,最后求反即可
代码如下:

    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int rows = obstacleGrid.length;
        int cols = obstacleGrid[0].length;
        obstacleGrid[0][0] = -1;
        for(int i = 0;i < rows;i++)
            for(int j = 0;j < cols;j++){
                if(obstacleGrid[i][j] != 1){
                    if((i - 1) >= 0 && obstacleGrid[i-1][j] != 1){
                        obstacleGrid[i][j] += obstacleGrid[i-1][j];
                    }
                    if((j-1) >= 0 && obstacleGrid[i][j-1] != 1)
                        obstacleGrid[i][j] += obstacleGrid[i][j-1];
                }
            }
        return obstacleGrid[rows-1][cols-1] * -1;
    }

跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

思路:
根据题目描述,我们首先想到求解思路是贪心算法,着个遍历每种走法,直到成功跳出后返回。对于当下的i,存在nums[i]种走法,那么我们逐个往前推进即可
代码如下:

    /**
     * 55. 跳跃游戏
     * @param nums
     * @return
     */
    boolean res = false;
    public boolean canJump(int[] nums) {

        return canJump(nums,0,nums.length - 1);
    }

    public boolean canJump(int []nums,int start,int road){
        if(nums[start] >= road)
            return true;
        else{
            for(int i = 1;i <= nums[start];i++){
                if(res)
                    return res;
                res |= canJump(nums,start + i,road - i);
            }
        }
        return res;
    }

买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:

输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
思路:
剔除题目背景是股票买卖(其描述,显然不符合实际市场环境),其真是表达的意思是,求连续最长递增序列。理解了题目意思,代码写起来就比较容易了。记录每次买入点start,卖出点end,累计求和

    /**
     * 122. 买卖股票的最佳时机 II
     * @param prices
     * @return
     */
    public static int maxProfit(int[] prices) {
        int lg = prices.length;
        int res = 0 , start = 0 , end = 0;
        for(int i = 1;i < lg;i++){
            if(prices[i] > prices[i-1]){
                end =i;
            }
            else{
                res += prices[end] - prices[start];
                start = i;
            }
        }
        return res;
    }

时间复杂度O(n)、空间复杂度O(1)


25. k个一组翻转链表

给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 k = 2 时,应当返回: 2->1->4->3->5

当 k = 3 时,应当返回: 3->2->1->4->5

说明 :

你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

思路:
k个一组,对每一组的数据进行进行逆转。也就是将一条链表分为k组,逆转的片段,递归的向后链接剩余的片段。

ListNode p = head,q = head.next,r = q.next;
q.next = p;
p = q;
q = r;

拼多多校招题 :最大乘积

给定一个无序数组,包含正数、负数和0,要求从中找出3个数的乘积,使得乘积最大,要求时间复杂度:O(n),空间复杂度:O(1)
输入描述:
第一行是数组大小n,第二行是无序整数数组A[n]

输出描述:
满足条件的最大乘积

输入例子1:
4
3 4 1 2

输出例子1:
24

方法一:通过对n个数排序,第0,1个数的乘积(s1)和第n-3,n-2个数的乘积(s2),取s1、s2中的最大者,并且和最大值n-1相乘,得到最大的3个数乘积之和; 时间复杂度为O(n*logn),空间复杂度为O(n)

方法二:通过建立大小为3的小根堆,维持最大的3个数,建立大小为2的小根堆维持最小的两个数,接下来的比较方法同方法一;时间复杂度为接近O(n)
代码如下:


    /**
     * [编程题] 最大乘积
     */
    public static int test6(){
        Scanner sc = new Scanner(System.in);
        PriorityQueue<Integer>max = new PriorityQueue<Integer>(2, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        PriorityQueue<Integer>min = new PriorityQueue<>(3);
        int n = sc.nextInt() , temp;

        for(int i = 0;i < n;i++){
            temp = sc.nextInt();
            if(min.size() < 3)
                min.offer(temp);
            else{
                if(temp > min.peek()){
                    min.poll();
                    min.offer(temp);
                }
            }
            if(max.size() < 2)
                max.offer(temp);
            else{
                if(temp < max.peek()){
                    max.poll();
                    max.offer(temp);
                }
            }
        }
        int res1 = 1 , res2 = 1;
        int tt = -1;
        for(Integer it : min){
            res1 *= it;
            tt = tt > it ? tt : it;
        }
        res2 *= tt;
        for(Integer it : max){
            res2 *= it;
        }
        return res1 > res2 ? res1 : res2;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值