《算法零基础100讲》(第17讲) 线性枚举(一) - 最值算法

一.概念定义

两个数的最小值

  两个数的最小值可以用if语句判断,也可以用C语言的三元运算符。

//if语句判断
int max(int a, int b){
    
    
	if(a < b)
		return b;
	return a;
}

//三元运算符
int max(int a, int b){
    
    
	return a > b ? a : b;//a > b ? 意思是a 是否大于b
	                    //a : b表示如果前面条件成立就返回a,否则返回b
}

n个数的最小值

  当有n个数时,我们可以先比较出俩个数的最大值,然后再用这个值与第三个值进行比较,一次类推,得出n个数的最大值。

int max(int* nums, int numsSize){
    
    
	int max = INT_MIN;
	for(int i = 0; i < numsSize; i++){
    
    
		if(max < nums[i]){
    
    
			max = nums[i];	
		}
	}
	return max;
}

课后习题

485. 最大连续 1 的个数

485. 最大连续 1 的个数
在这里插入图片描述
分析:
  这道题时让我们统计一个数组中1的连续最长个数,并且用0来做间隔,这时我们则需要两个变量,max和cout,max用来记录最长连续个数,cout用来计算每个0间隔前的1的连续个数,每当读取到0时,对cout和max进行比较。
代码如下:

int findMaxConsecutiveOnes(int* nums, int numsSize){
    
    
    int max = 0;
    int cout = 0;
    for(int i = 0; i < numsSize; i++){
    
    
        if(nums[i] == 1){
    
    
            cout++;
        }
        if(nums[i] == 0){
    
    
            cout = 0;
        }
        if(max < cout){
    
    
            max = cout;
        }
    }
    return max;
}

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

1464. 数组中两元素的最大乘积
在这里插入图片描述
在这里插入图片描述
因为nums[i]的区间为[1,1000],所以我们只需找出最大的两个值即可,因为数据量很小,所以我们可以直接遍历两遍,第一遍找出最大值,并记录下该数的下表,第二遍遍历就能找出第二大的值。
代码如下:

int maxProduct(int* nums, int numsSize){
    
    
    int max1 = 0, x1;
    int max2 = 0;

    for(int i = 0; i < numsSize; i++){
    
    
        if(max1 < nums[i]){
    
    
            max1 = nums[i];
            x1 = i;
        }
    }
    for(int i = 0; i < numsSize; i++){
    
    
        if(max2 < nums[i] && i != x1){
    
    
            max2 = nums[i];
        }
    }
    return (max1 - 1) * (max2 - 1);
}

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

153. 寻找旋转排序数组中的最小值
在这里插入图片描述
在这里插入图片描述
这道题的有两种解法:
第一种:简单暴力,直接遍历

int findMin(int* nums, int numsSize){
    
    
    int min = INT_MAX;
    for(int i = 0; i < numsSize; i++){
    
    
        if(min > nums[i]){
    
    
            min = nums[i];
        }
    }
    return min;
}

第二种:利用二分查找
  因为所给定的数组nums是由一个递增序列经过1至n次的向右移动得到的,并且nums不存在相同的数,我们假设最小值为x,除去原来的递增情况,x左边的数必然大于x右边的数。
如图:
在这里插入图片描述
这时我们可以设定两个指针,分别指向最低位下标和最高位下标,再定义一个mid来存放中间下标,通过结合上面的分析我们可以发现会出现两种情况:
第一种:当numd[mid] > nums[high]时,假设最小值下标为x,mid是[mid,x) 这个区间内的最小值,这时我们可以除去左半部分。
在这里插入图片描述
第二种情况:当nums[mid] < nums[high]时,说明最小值再mid的左边,我们可以舍去右半部分。
在这里插入图片描述
代码如下:

int findMin(int* nums, int numsSize){
    
    
    int low = 0;
    int hight = numsSize - 1;
    while(low < hight){
    
    
        int mid = (low + hight) / 2;
        if(nums[mid] < nums[hight]){
    
    
            hight = mid;
        }
        else{
    
    
            low = mid + 1;
        }
    }

    return nums[low];
}

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

154. 寻找旋转排序数组中的最小值 II
在这里插入图片描述
在这里插入图片描述
这道题与上面那道的区别是有相同的数据了,所以需要考虑一些特殊的情况,例如[1,3,3],[3,3,1,3]
前面两种情况和上题一样,新增的第三种情况是:nums[mid] == nums[high]
在这里插入图片描述
这种情况下,最小值一定是在high的左边,所以我们只需high-1即可。
代码如下:

int findMin(int* nums, int numsSize){
    
    
    int low = 0;
    int hight = numsSize - 1;
    while(low < hight){
    
    
        int mid = (low + hight) / 2;
        if(nums[mid] < nums[hight]){
    
    
            hight = mid;
        }
        else if(nums[mid] > nums[hight]){
    
    
            low = mid + 1;
        }
        else{
    
    
            hight--;
        }
    }

    return nums[low];
}

414. 第三大的数

414. 第三大的数
在这里插入图片描述
在这里插入图片描述
这道题有两种解法:
方法一:排序
太过简单就不分析了"( ̄▽ ̄)"
代码:

int thirdMax(int* nums, int numsSize){
    
    
    for(int i = 0; i < numsSize; i++){
    
    
        for(int j = 0; j < numsSize - 1 - i; j++){
    
    
            if(nums[j] < nums[j + 1]){
    
    
                int tmp = nums[j + 1];
                nums[j + 1] = nums[j];
                nums[j] = tmp;
            }
        }
    }
    for (int i = 1, diff = 1; i < numsSize; ++i) {
    
    
            if (nums[i] != nums[i - 1] && ++diff == 3) {
    
     // 此时 nums[i] 就是第三大的数
                return nums[i];
            }
        }
    return nums[0];
}

方法二:一次遍历
我们可以设置三个变量max1,max2,max3分别存储前三个的最大值,由于数据范围在整型的最大边界,所以这三个变量的初始条件为LONG_MIN。
代码如下:

int thirdMax(int* nums, int numsSize){
    
    
    long max[3];
    max[0] = LONG_MIN;
    max[1] = LONG_MIN;
    max[2] = LONG_MIN;

    for(int i = 0; i < numsSize; i++){
    
    
    	//先将最大的值给max[0]
        if(max[0] < nums[i]){
    
    
            max[2] = max[1];
            max[1] = max[0];
            max[0] = nums[i];
        }
        else if(max[0] > nums[i] && max[1] < nums[i]){
    
    
            max[2] = max[1];
            max[1] = nums[i];
        }
        else if(max[1] > nums[i] && max[2] < nums[i]){
    
    
            max[2] = nums[i];
        }
    }
    if(max[2] == LONG_MIN)
        return max[0];
    return max[2]; 
}

628. 三个数的最大乘积

628. 三个数的最大乘积
在这里插入图片描述
在这里插入图片描述
这道题也有两种解题方式
方法一:排序
由于nums[i]的取值范围在[-1000,1000],所以会有两种情况,最大的三个数相乘为最大值,或者最大的数与最小的两个数相乘为最大值。
代码如下:

int* cmp(const int* x1, const int* x2){
    
    
    return *x1 - *x2;
}

int maximumProduct(int* nums, int numsSize){
    
    
    qsort(nums, numsSize, sizeof(int), cmp);
    return fmax(nums[0] * nums[1] * nums[numsSize - 1], nums[numsSize - 3] * nums[numsSize - 2] * nums[numsSize - 1]);
}

方法二:线性扫描
线性扫描我们需找出最大的三个数和最小的两个数。
代码如下:

int maximumProduct(int* nums, int numsSize){
    
    
    int max[3];
    max[0] = INT_MIN;
    max[1] = INT_MIN;
    max[2] = INT_MIN;
    int min1 = INT_MAX;
    int min2 = INT_MAX;
    for(int i = 0; i < numsSize; i++){
    
    
        if(max[0] < nums[i]){
    
    
            max[2] = max[1];
            max[1] = max[0];
            max[0] = nums[i];
        }
        else if(max[1] < nums[i]){
    
    
            max[2] = max[1];
            max[1] = nums[i];
        }
        else if(max[2] < nums[i]){
    
    
            max[2] = nums[i];
        }

        if(min1 > nums[i]){
    
    
            min2 = min1;
            min1 = nums[i];
        }
        else if(min2 > nums[i]){
    
    
            min2 = nums[i];
        }
    }
    return fmax(max[0] * max[1] * max[2], min1 * min2 * max[0]);

}

おすすめ

転載: blog.csdn.net/qq_53060585/article/details/121183672