每日一练<3>

每日一练

大家好呀!我是小笙,接下来分享下10月份每日一题的解题思路~

1441. 用栈操作构建数组

给你一个数组 target 和一个整数 n。数组 target 是严格递增的,并且只包含 1n 之间的数字

从 1 遍历到 n,如果在 target 数组中,则添加 “Push” 反之添加 “Push” “Pop”

示例

输入:target = [1,3], n = 3
输出:["Push","Push","Pop","Push"]
解释: 
读取 1 并自动推入数组 -> [1]
读取 2 并自动推入数组,然后删除它 -> [1]
读取 3 并自动推入数组 -> [1,3]

暴力解决

class Solution {
    public List<String> buildArray(int[] target, int n) {
        List<String> res = new ArrayList<>();
        int index = 0;
        for(int i=1;i<=n;i++){
            if(index >= target.length){
                return res;
            }else if(target[index] == i){
                index++;
                res.add("Push");
            }else{
                res.add("Push");
                res.add("Pop");
            }
        }
        return res;
    }
}
779. 第K个语法符号

我们构建了一个包含 n 行( 索引从 1 开始 )的表。首先在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为011替换为10

image-20221020201515269

示例

输入: n = 1, k = 1
输出: 0
解释: 第一行:0

解题思路:首先,我们需要寻找规律,这道题很容易出现空间的溢出或者时间的超时

规律:下一行数据 = 上一行数据 + 上一行数据的反转(0 替换成 1;1 替换成 0)

所以我想我可以通过第一行的数据来推导出接下来每一行的数据,因为都是第一行数据演变过来的

回推的时候,k 有两种情况:

  • k 是在 n 行的前半段,意味着他就是上一行的第 k 个数据,也就是没有进行反转
  • 如果 k 出现在第 n 行数据后半段的时候,那折半处理的时候就会进行反转一次(0 替换成 1;1 替换成 0)
  • 因此,我只要记录下反转的次数不就可以直接推出第 n 行第 k 个数据是多少了嘛
class Solution {
    public int kthGrammar(int n, int k) {
        // 反转次数
        int inverse = 0;
        while(n > 1){
            if(k > (int)(Math.pow(2,n-1)) / 2){
                inverse++;
                k -= (int)(Math.pow(2,n-1)) / 2;
            }
            n--;
        }
        return inverse % 2 == 0?0:1;
    }
}
901. 股票价格跨度

编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)

换句话来说就是一个列表中刚添加进来的数前面最大的连续比添加的这个值小或者等于的数的个数

示例

输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1S.next(80) 被调用并返回 1S.next(60) 被调用并返回 1S.next(70) 被调用并返回 2S.next(60) 被调用并返回 1S.next(75) 被调用并返回 4S.next(85) 被调用并返回 6。

注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。

暴力破解:原本以为过不了,没想到

class StockSpanner {
    List<Integer> list = null;

    public StockSpanner() {
        list = new ArrayList<>();
    }
    
    public int next(int price) {
        int len = list.size(),res = 1;
        list.add(price);
        while(len-- > 0){
            if(price >= list.get(len)){
                res++;
            }else{
                break;
            }
        }
        return res;
    }
}

优化:单调栈

解题思路:就是传入 100 30 40 60 100 30 是可以重叠成 100 100 30,只要我们记住他们的下标就可以,比如我们在添加 60 的时候,就不需要比较 30 不是嘛,所以在添加 60 的时候数据是这样的 -> 100 40 我们只要知道当前 60 的下标和 100 的下标,我们不就知道中间有多少数字是小于 60 的嘛

class StockSpanner {
    // 可以用来折叠数据,分别记录下标和该下标下的数值
    Deque<int[]> stack;
    // 记录下标
    int index;

    public StockSpanner() {
        stack = new ArrayDeque<int[]>();
        stack.push(new int[]{-1, Integer.MAX_VALUE});
        index = -1;
    }

    public int next(int price) {
        index++;
        while (price >= stack.peek()[1]) {
            // 进行折叠,这部分数据没有必要重复操作
            stack.pop();
        }
        int res = index - stack.peek()[0];
        stack.push(new int[]{index, price});
        return res;
    }
}
1768. 交替合并字符串

给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替添加字母来合并字符串。如果一个字符串比另一个字符串长,就将多出来的字母追加到合并后字符串的末尾。

示例

输入:word1 = "abc", word2 = "pqr"
输出:"apbqcr"
解释:字符串合并情况如下所示:
word1:  a   b   c
word2:    p   q   r
合并后:  a p b q c r

暴力解决:取出长字母后面的字符串,然后再添加到交叉拼接的字符串后面

class Solution {
    public String mergeAlternately(String word1, String word2) {
        int len1 = word1.length(),len2 = word2.length(),min = Math.min(len1,len2);
        String append = len1>len2?word1.substring(len2):word2.substring(len1);
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<min;i++){
            sb.append("" + word1.charAt(i) + word2.charAt(i));
        }
        return sb.append(append).toString();
    }
}

优化取出长字母后面的字符串的方法的算力

class Solution {
    public String mergeAlternately(String word1, String word2) {
        int m = word1.length(), n = word2.length();
        int i = 0, j = 0;
        StringBuilder ans = new StringBuilder();
        while (i < m || j < n) {
            if (i < m) {
                ans.append(word1.charAt(i));
                ++i;
            }
            if (j < n) {
                ans.append(word2.charAt(j));
                ++j;
            }
        }
        return ans.toString();
    }
}
915. 分割数组

给定一个数组 nums ,将其划分为两个连续子数组 left 和 right, 使得:

  • left 中的每个元素都小于或等于 right 中的每个元素
  • left 和 right 都是非空的
  • left 的长度要尽可能小

在完成这样的分组后返回 left 的 长度

例子

输入:nums = [5,0,3,8,6]
输出:3
解释:left = [5,0,3],right = [8,6]

解题思路:遍历从末尾开始,每次都和第一个数字比较,一旦出现比第一个数字还要小的数,就统计从第一个到当前位置的最大值,然后再从末尾向当前位置遍历,找出小于这个最大值的点则是最后的答案

class Solution {
    public int partitionDisjoint(int[] nums) {
        // 遍历从末尾开始,每次都和第一个数字比较
        for(int i=nums.length-1;i>0;i--){
            if(nums[i] < nums[0]){
                int max = 0;
                // 找出最大值
                for(int j=0;j<=i;j++){
                    max = max>nums[j]?max:nums[j];
                }
                if(max == nums[0]){
                    return i+1;
                }
                // 从末尾向当前位置遍历,找出小于这个最大值的点则是最后的答案
                for(int j=nums.length-1;j >= i;j--){
                    if(max > nums[j]){
                        return j+1;
                    }
                }
                return nums.length-1;
            }
        }
        return 1;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Al_tair

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值