来试一试吗?不会吧,这三道题你不会?

课题1.求数组的峰值元素

峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。你可以假设 nums[-1] = nums[n] = -∞。

示例:

输入: nums = [1,2,3,1]

输出: 2

解释: 3 是峰值元素,你的函数应该返回其索引 2。

课题2.摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。给定一个整数序列,返回作为摆动序列的最长子序列的长度。通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

输入: [1,7,4,9,2,5]

输出: 6

课题3.最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]

输出: 4

先思考::::::::::::::::::::::::::::::;:::;

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

课题1:

思路:二分查找

如果 nums[i]<nums[i+1],并且我们从位置 i 向右走到了位置 i+1,那么位置 i 左侧的所有位置是不可能在后续的迭代中走到的。

这是因为我们每次向左或向右移动一个位置,要想「折返」到位置 i以及其左侧的位置,我们首先需要在位置 i+1 向左走到位置 i,但这是不可能的。

对于当前可行的下标范围 [l, r],我们随机一个下标 i;
如果下标 i是峰值,我们返回 i 作为答案;
如果 nums[i] < nums[i+1],那么我们抛弃 [l, i] 的范围,在剩余 [i+1, r] 的范围内继续随机选取下标;
如果 nums[i] > nums[i+1],那么我们抛弃 [i, r] 的范围,在剩余 [l, i-1]的范围内继续随机选取下标。

时间复杂度: O(logn),其中 n是数组nums 的长度

package Solution;
class Solution {
    public int findPeakElement(int[] nums) {
        int n = nums.length;
        int left = 0, right = n - 1, ans = -1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (compare(nums, mid - 1, mid) < 0 && compare(nums, mid, mid + 1) > 0) {
                ans = mid;
                break;
            }
            if (compare(nums, mid, mid + 1) < 0) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return ans;
  }
    // 辅助函数,输入下标 i,返回一个二元组 (0/1, nums[i])
    // 方便处理 nums[-1] 以及 nums[n] 的边界情况
    public int[] get(int[] nums, int idx) {
        if (idx == -1 || idx == nums.length) {
            return new int[]{0, 0};
        }
        return new int[]{1, nums[idx]};
    }

    public int compare(int[] nums, int idx1, int idx2) {
        int[] num1 = get(nums, idx1);
        int[] num2 = get(nums, idx2);
        if (num1[0] != num2[0]) {
            return num1[0] > num2[0] ? 1 : -1;
        }
        if (num1[1] == num2[1]) {
            return 0;
        }
        return num1[1] > num2[1] ? 1 : -1;
    }
    public static void main(String[] args)
    {   int[]nums=new int[]{1,2,3,1};
    	Solution s=new Solution();
    	System.out.println(s.findPeakElement(nums));
    }
}

课题2

思路:贪心

观察这个序列可以发现,我们不断地交错选择「峰」与「谷」,可以使得该序列尽可能长。证明非常简单:如果我们选择了一个「过渡元素」,那么在原序列中,这个「过渡元素」的两侧有一个「峰」和一个「谷」。

我们假设在原序列中的出现顺序为「峰」「过渡元素」「谷」。如果「过渡元素」在选择的序列中小于其两侧的元素,那么「谷」一定没有在选择的序列中出现,我们可以将「过渡元素」替换成「谷」;同理,如果「过渡元素」在选择的序列中大于其两侧的元素,那么「峰」一定没有在选择的序列中出现,我们可以将「过渡元素」替换成「峰」。这样一来,我们总可以将任意满足要求的序列中的所有「过渡元素」替换成「峰」或「谷」。并且由于我们不断地交错选择「峰」与「谷」的方法就可以满足要求,因此这种选择方法就一定可以达到可选元素数量的最大值。

这样,我们只需要统计该序列中「峰」与「谷」的数量即可(注意序列两端的数也是「峰」或「谷」),但需要注意处理相邻的相同元素。

在实际代码中,我们记录当前序列的上升下降趋势。每次加入一个新元素时,用新的上升下降趋势与之前对比,如果出现了「峰」或「谷」,答案加一,并更新当前序列的上升下降趋势。

时间复杂度:O(n),其中 n 是序列的长度。我们只需要遍历该序列一次。

package Solution;
class Solution {
    public int wiggleMaxLength(int[] nums) {
        int n = nums.length;
        if (n < 2) {
            return n;
        }
        int prevdiff = nums[1] - nums[0];
        int ret = prevdiff != 0 ? 2 : 1;
        for (int i = 2; i < n; i++) {
            int diff = nums[i] - nums[i - 1];
            if ((diff > 0 && prevdiff <= 0) || (diff < 0 && prevdiff >= 0)) {
                ret++;
                prevdiff = diff;
            }
        }
        return ret;
    }



    public static void main(String[] args)
    {   int[]nums=new int[]{1,7,4,9,2,5};
    	Solution s=new Solution();
    	System.out.println(s.wiggleMaxLength(nums));
    }
 }

课题3

思路:动态规划

实验定义 dp[i] 为考虑前 i 个元素,以第 i个数字结尾的最长上升子序列的长度,注意 nums[i] 必须被选取。

从小到大计算 dp 数组的值,在计算 dp[i] 之前,我们已经计算出 dp[0…i−1] 的值,则状态转移方程为:
dp[i]=max(dp[j])+1,其中0≤j<i且num[j]<num[i]

整个数组的最长上升子序列即所有 dp[i] 中的最大值
LIS 
length

 =max(dp[i]),其中0≤i<n。

时间复杂度:O(n2);n为数组nums的长度。

package Solution;
class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxans = 1;
        for (int i = 1; i < nums.length; i++) {
            dp[i] = 1;
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            maxans = Math.max(maxans, dp[i]);
        }
        return maxans;
    }
    public static void main(String[] args)
    {   int[]nums=new int[]{10,9,2,5,3,7,101,18};
    	Solution s=new Solution();
    	System.out.println(s.lengthOfLIS(nums));
    }
 }

如有分析不明确,还有不够好的,希望大家指正,和提供更好的思路,谢谢。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Kewen_and_Ulrikh

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

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

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

打赏作者

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

抵扣说明:

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

余额充值