目录
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);
}