【山无遮,海无拦】LeetCode题集 线性枚举之最值算法

目录

1464.数组中两元素的最大乘积 

法一:线性扫描 

法二:排序 

 485. 最大连续 1 的个数

法一:线性扫描

 153. 寻找旋转排序数组中的最小值

法一:线性扫描

法二:二分法

 154. 寻找旋转排序数组中的最小值 II

法一:线性扫描

法二: 二分法

 414. 第三大的数

 法一:线性扫描

法二:排序

628. 三个数的最大乘积

法一:线性扫描

法二: 排序


1464.数组中两元素的最大乘积 
 

题目:给你一个整数数组 nums,请你选择数组的两个不同下标 i 和 j,使 (nums[i]-1)*(nums[j]-1) 取得最大值。
请你计算并返回该式的最大值。

力扣 

法一:线性扫描 

 解题思路:一次遍历数组,同时找出最大值和最小值。

①最大值和最小值赋初值

②扫描数组,遇到大于当前最大值的元素,将最大值赋给第二大,将该元素赋给最大值

③遇到大于第二大的值和小于最大值的元素,将其值赋给第二大

④扫描完数组,得到最大值和第二大的值。

时间复杂度:O(n)

int maxProduct(int* nums, int numsSize)
{
    int first = 0;   //最大的数 
    int second = 0;  //第二大的数 
    for(int i=0; i<numsSize; i++)
    {
        if(nums[i] >= first)
        {
            second = first;
            first = nums[i];
            
        }
        else if(nums[i] > second)
        {
            second = nums[i];
        }
    }
    return (first-1)*(second-1);
}

法二:排序 

 解题思路:用C语言自带的qsort函数对数组进行排序,取最大和最小值即可。

qsort可点击此处学习:qsort

时间复杂度:O(nlogn)

空间复杂度:O(n)

#include <stdlib.h>
int cmp(const void* _a, const void* _b)
{
    int* a = (int*)_a;
    int* b = (int*)_b;
    return *a - *b;
}
int maxProduct(int* nums, int numsSize)
{
    qsort(nums, numsSize, sizeof(int), cmp);
    int first = nums[numsSize-1]-1;
    int second = nums[numsSize-2]-1;
    return first*second;
}

 485. 最大连续 1 的个数

题目:给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

力扣

法一:线性扫描

解题思路:

①用length记录最大连续1的个数,初值为0;用count记录当前连续1的个数,初值为0

②扫描数组,遇到1,count加一;遇到0,用count的值与length比较,若count>length,将count值赋给length,count赋0;否则,只把count赋0;

③扫描数组,得到length的值即为最大连续1的个数。

int findMaxConsecutiveOnes(int* nums, int numsSize){
    if(numsSize == 0)
    {
        return 0;
    }
    int length = 0; //记录最大来连续1的个数
    int count = 0;  //记录当前连续1的个数
    int i = 0;
    for(i=0; i<numsSize; i++)
    {
        if(nums[i] == 1)
        {
            count++;
        }
        else
        {
            length = fmax(length,count);
            count = 0;
        }  
    }
    length = fmax(length, count);  //选出两数中的最大的一数赋给length 
    return length;
} 

 153. 寻找旋转排序数组中的最小值

 题目:已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为?O(log n) 的算法解决此问题。

力扣

法一:线性扫描

 解题思路:巧妙利用leetcode测试的bug,此法虽然不满足题目要求,但可以测试通过。

遍历数组,将最小值赋给min.

int findMin(int* nums, int numsSize)
{
    int min = INT_MAX;   //将最大整型数赋给min
    for(int i=0; i<numsSize; i++)
    {
        if(nums[i] <= min)
        {
            min = nums[i];
        }
    }
    return min;
}

法二:二分法

 解题思路:由题目可以知道,旋转后的数组会出现这样的情况,在最小值的左边的元素和在最小值右边的元素都单调递增,且右边的元素恒小于左边的元素。

①用二分法取得中间的数值nums[middle]

②若nums[middle] < nums[right],则最小值在中间值的左边,这时可以舍去右边区域的数值

③若nums[middle] > nums[right], 最小值在中间值的右边,这时可以舍去左边区域的数值

④二分得到最后的数即为最小值

int findMin(int* nums, int numsSize)
{
    int left = 0;
    int right = numsSize-1;
    while(left < right)
    {
        int middle = left + (right-left)/2;
        if(nums[middle] < nums[right])
        {
            right = middle;
        } 
        else 
        {
            left = middle+1;
        }
    }
    return nums[left];
}

 154. 寻找旋转排序数组中的最小值 II

题目:已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,4]
若旋转 7 次,则可以得到 [0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个可能存在 重复 元素值的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须尽可能减少整个过程的操作步骤。

力扣

法一:线性扫描

解题思路:与上题思路一致

int findMin(int* nums, int numsSize)
{
    int min = INT_MAX;   //将最大整型数赋给min
    for(int i=0; i<numsSize; i++)
    {
        if(nums[i] <= min)
        {
            min = nums[i];
        }
    }
    return min;
}

​

法二: 二分法

解题思路:本题与上一题的区别在于数组中可以有重复的元素,则会出现以下特殊情况,其余与上一题一致,就不做重复说明。

若nums[middle]=nums[right]由于重复元素的存在,我们并不能确定nums[middle] 
究竟在最小值的左侧还是右侧,因此我们不能莽撞地忽略某一部分的元素。
我们唯一可以知道的是,由于它们的值相同,所以无论 nums[right]是不是最小值,都有一个它的替代品nums[middle],因此我们可以忽略二分查找区间的右端点

int findMin(int* nums, int numsSize){
    int left = 0;
    int right = numsSize-1;
    while(left < right)
    {
        int middle = left + (right-left)/2;
        if(nums[middle] > nums[right])
        {
            left = middle + 1;
        }
        else if(nums[middle] < nums[right])
        {
            right = middle;
        }
        else right--;
    }
    return nums[left];
}

 414. 第三大的数

题目:给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。

力扣

 法一:线性扫描

解题思路:与同时找出最大值和第二大值的算法思路一样。

这里需要注意的是,为避免边界值溢出,我们采用了long型变量记录最值。

另外,需要需要考虑第三大的值不存在的情况。

int thirdMax(int* nums, int numsSize)
{
    //使用长整型是为了避免边界测试用例溢出,如:[1,2,-2147483648]
    long first = LONG_MIN;
    long second = LONG_MIN;
    long third  = LONG_MIN;
    for(int i=0; i<numsSize; i++)
    {
        if(nums[i] > first)
        {
            third = second;
            second = first;
            first = nums[i];
        }
        else if(nums[i] < first && nums[i] > second)
        {
            third = second;
            second = nums[i];
        }
        else if(nums[i] > third && nums[i] < second)
        {
            third = nums[i];
        }
    }
    return (third == LONG_MIN)? first : third;
}

法二:排序

 解题思路:按降序排序,需要注意的在cmp中,本题使用大于号,避免当数据处于边界时数据越界。

用differ记录排序后相邻值的不同次数,当不同次数为2时,有第三大的数,否则,返回最大值。

int cmp(const void* _a, const void* _b)
{
    int* a = (int*)_a;
    int* b = (int*)_b;
    //如此操作是为了防止边界值的溢出,在本题中,若不采用此法,会有测试用例溢出出错
    return *b > *a;  //降序排列
}
int thirdMax(int* nums, int numsSize)
{
    qsort(nums, numsSize, sizeof(int), cmp);
    int differ = 0;  //记录前后两个元素不同的次数
    for(int i=1; i<numsSize; i++)
    {
        if(nums[i] != nums[i-1])
        {
            differ++;
        }
        if(differ == 2)  //有两次不同,就输出当前元素
        {
            return nums[i]; 
        }
    }
    return nums[0]; //没有第三大的元素,输出最大的元素
}

628. 三个数的最大乘积

 题目:给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。

力扣

法一:线性扫描

解题思路:由于本题的整数可正可负,则三数乘积的最大值会出现两种情况。

①最大的三个数乘积为三数乘积的最大值

②最小数的绝对值大于最大数时,最小的数、第二小的数和最大的数乘积为最大值

取这两种情况下的最大值,即为三数乘积的最大值。

int maximumProduct(int* nums, int numsSize) {
    // 最小的和第二小的,其初值赋为最大整型
    int min1 = INT_MAX;
    int min2 = INT_MAX;
    // 最大的、第二大的和第三大的,其初值赋为最小整型
    int max1 = INT_MIN;
    int max2 = INT_MIN;
    int max3 = INT_MIN;

    for (int i = 0; i < numsSize; i++) {
        if (nums[i] < min1) {
            min2 = min1;
            min1 = nums[i];
        } else if (nums[i] < min2) {
            min2 = nums[i];
        }

        if (nums[i] > max1) {
            max3 = max2;
            max2 = max1;
            max1 = nums[i];
        } else if (nums[i] > max2) {
            max3 = max2;
            max2 = nums[i];
        } else if (nums[i] > max3) {
            max3 = nums[i];
        }
    }
    return fmax(min1 * min2 * max1, max1 * max2 * max3);
}

法二: 排序

解题思路:排序后确定元素的大小顺序,其余思路与法一一致。

int maximumProduct(int* nums, int numsSize)
{
    qsort(nums, numsSize, sizeof(int), cmp);
    //最大的三个数相乘
    long max1 = nums[numsSize-1] * nums[numsSize-2] * nums[numsSize-3];
    //最小的两个数乘最大的一个数
    long max2 = nums[0] * nums[1] * nums[numsSize-1];
    return fmax(max1, max2);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值