如何解hard算法题?

前言

上一篇文章写bitCount源码解析,对困难题有一些抽象的理解。
困难题就是一个个简单的知识点组成,加上其内在的逻辑联系。所以一个困难题,应该是先观其规律,再进行逻辑转换问题,再拆解小问题,用积累的知识点解决。
当然,如果没有积累到/思考角度不对,就看题解,多调研,别浪费时间!

一、案例

在这里插入图片描述

二、困难题拆解

1、自己的思路

	// target:从两个数组选出两组数据,合并倒序,就得到一个最大组合值。
    // 转换成有规律的问题。
    // 问题
    // 两数组肯定尽量单调(最大的那个单调),但是取出的单调栈合并长度并不一定为k
    // 如果两者合并长度大于等于k,倒排之后(依次选择),取前k个即可。
    // 如果不够k怎么办?

很明显,我的角度错了,但也有可取之处,比如单调栈。
而我角度错了的原因,就是没有将问题从模拟问题转化为有规律的问题,这样才能用代码实现!

2、官方的思路

将k分解为k = x + y,不断模拟两个数值数组的最大拼接,记录最大的那一个。
现在已经将问题规律化了,然后拆解小问题,就涉及到小知识点了
1.带remain的单调栈来取数值数数组指定长度最大子序列。
2.O(N)合并两个数值数组,使其该数值数组最大值。
3.两数值数组比较大小的处理方式。

3、源码

Java

class Solution {
    // target:从两个数组选出两组数据,合并倒序,就得到一个最大组合值。
    // 转换成有规律的问题。
    // 问题
    // 两数组肯定尽量单调(最大的那个单调),但是取出的单调栈合并长度并不一定为k
    // 如果两者合并长度大于等于k,倒排之后(依次选择),取前k个即可。
    // 如果不够k怎么办?

    // 调研思路
    // 将k分成 k = x + y,不断寻找x和y,然后拼接,比较,记录最大值。 
    public int[] maxNumber(int[] nums1, int[] nums2, int k) {
        int m = nums1.length,n = nums2.length;

        int start = Math.max(0,k - n);
        int end = Math.min(k,m);
        int[] maxRs = new int[k];
        // 强行分解k = x + y,进行遍历记录最大值。
        // hard-mark:问题转换(规律非模拟问题)+问题拆解(多个小知识点)+多个小问题的逻辑联系。
        for(int i = start;i <= end;i++){
            int[] seq1 = getSeq(nums1,i);
            int[] seq2 = getSeq(nums2,k - i);

            int[] curRs = merge(seq1,seq2);

            if(compare(maxRs,0,curRs,0) < 0) System.arraycopy(curRs,0,maxRs,0,k);
        }
        return maxRs;
    }
    // 知识点1:O(N)合并两个数值数组,使其该数值数组最大值。
    public int[] merge(int[] nums1,int[] nums2){
        int m = nums1.length,n = nums2.length;
        if(m == 0) return nums2;
        if(n == 0) return nums1;

        int i = 0,j = 0;
        int[] rs = new int[m + n];

        for(int k = 0;k < m + n;k++){
            if(compare(nums1,i,nums2,j) > 0) rs[k] = nums1[i++];
            else rs[k] = nums2[j++];
        }
        return rs;
    }
    // 知识点2:两数值数组比较大小,尽量返回int,而不是boolean
    public int compare(int[] nums1,int i,int[] nums2,int j){
        int m = nums1.length,n = nums2.length;

        while(i < m && j < n){
            int gap = nums1[i] - nums2[j];
            if(gap != 0) return gap;

            ++i;
            ++j;
        }
        return (m - i) - (n - j);
    }
    // 知识点3:单调减栈 配合 remain,可以做到求指定长度的最大子序列。
    public int[] getSeq(int[] nums,int k){
        int[] stack = new int[k];
        int top = 0,remain = nums.length - k;

        for(int cur : nums){
            while(top > 0 && stack[top - 1] < cur && remain > 0){
                top--;
                remain--;// 不需要的上限
            }
            if(top < k){
                stack[top++] = cur;
            }else remain--;
        }
        return stack;
    }
}

golang

func maxNumber(nums1 []int, nums2 []int, k int) []int {
    m,n := len(nums1),len(nums2)
    start,end := max(0,k - n),min(k,m)
    maxRs := make([]int,k)
    // k = x + y,取子序列,再合并,最后比较大小,并记录。
    for i := start;i <= end;i++ {
        seq1 := getSeq(nums1,i)
        seq2 := getSeq(nums2,k - i)

        curRs := merge(seq1,seq2)

        if compare(curRs,0,maxRs,0) > 0 {
            copy(maxRs,curRs)
        }
    }
    return maxRs
}
// 知识点1:O(N)合并两个数值数组,使其该数值数组最大值。
func merge(nums1 []int,nums2 []int) []int {
    m,n := len(nums1),len(nums2)
    if m == 0 {
        return nums2
    }
    if n == 0 {
        return nums1
    }
    i,j := 0,0
    rs := make([]int,m + n)
    for k := 0;k < m + n;k++ {
        if compare(nums1,i,nums2,j) > 0 {
            rs[k] = nums1[i]
            i++
        }else {
            rs[k] = nums2[j]
            j++
        }
    }
    return rs
}
// 知识点2:两数值数组比较大小,尽量返回int,而不是boolean
func compare(nums1 []int,i int,nums2 []int,j int) int {
    m,n := len(nums1),len(nums2)
    for ; i < m && j < n; {
        gap := nums1[i] - nums2[j]
        if gap != 0 {
            return gap
        }
        i++
        j++
    }
    return (m - i) - (n - j)
}
// 知识点3:单调减栈 配合 remain,可以做到求指定长度的最大子序列。
func getSeq(nums []int,k int) []int {
    stack := make([]int,k)
    top := 0
    remain := len(nums) - k

    for _,cur := range nums {
        for ; top > 0 && stack[top - 1] < cur && remain > 0; {
            top--;
            remain--;
        }
        if top < k {
            stack[top] = cur;
            top++
        }else {
            remain--
        }
    }
    return stack
}
// 取大值
func max(i int,j int) int {
    if i > j {
        return i
    }
    return j
}
// 取小值
func min(i int,j int) int {
    if i < j {
        return i
    }
    return j
}

总结

1)一个困难题,应该是先观其规律,再进行逻辑转换问题,再拆解小问题,用积累的知识点解决。
2)如果没有积累到/思考角度不对,就看题解,多调研,别浪费时间!
3)多解决困难题,对提升解决问题能力有帮助,但是这是长期提升,而短期需要找工作则边界收益不大,性价比低。

参考文献

[1] LeetCode 321.拼接最大数
[2] LeetCode 321.拼接最大数-官方题解
[3] go 数组copy用法
[4] go foreach用法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值