LeetCode数组高频题目整理

刷题是应届生找工作不可缺少的部分,一种公认的刷题策略是按类别刷题,可是每个类别也有许多题,在有限的时间里到底该刷哪些题呢?个人根据LeetCode官方给出的每个题目的出现频率,整理并收录了每个类别里高频出现的题目,对于官方统计频率太低的题目,不予收录。

文章目录

1.两数之和(0.479)

题目描述

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]

解题思路

  暴力解法,遍历两次,注意题中要求,一个数只能用一次,所以,第二次遍历的时候注意j!=i。
哈希也可以,但是有点复杂

代码

int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    
    

    if(numsSize < 2) return NULL;
    int *sum = (int *)malloc(sizeof(int) * 2);
    int i,j = 0;
    for(i = 0;i<numsSize;i++)
    {
    
    
        //注意题中要求 
        for(j = 0;(j < numsSize)&&(j!=i);j++)
        {
    
    
            if(nums[i]+nums[j] == target)
            {
    
    
                sum[0] = j;
                sum[1] = i;
                *returnSize = 2;
                 return sum;
            }

        }
    }
    *returnSize =2;
    return NULL;
}

167. 两数之和 II - 输入有序数组(0.531)

题目描述

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1 和 index2)不是从零开始的。 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。 示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2] 解释: 2 与 7 之和等于目标数 9。因此 index1 = 1, index2 = 2 。

解题思路

  1、双指针初始化:左指针第0个元素,右指针第numsSize-1个元素
  2、循环:如果相等存取index,返回;如果>target,右指针前移;如果<target,左指针后移
  3、结果返回

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    
    
 int* ret = (int*)malloc(sizeof(int) * 2);
    *returnSize = 2;
    
    // i 起始从0开始
    int i = 0;
    // j 起始从最后一个开始
    int j = numbersSize-1;
    //因为数组元素从0开始,所以总长度-1 = 数组最后一个元素的位置;

    //开始循环
    while(i<j){
    
    
        //计算出(i,j)位置的总和
        int sum = numbers[i] + numbers[j];
        //如果相等,那么好了,结果就是这个 ,然后需要返回元素,所以都+1;
        if(sum == target){
    
    
            ret[0] = i+1; 
            ret[1] = j+1;
            return ret;
        }
        //不满足条件的,当总和小于(i,j)位置,i++是向下走,否则是j--向左走
        if(sum<=target){
    
    
            i++;
        }else{
    
    
            j--;
        }

    }


    ret[0] = -1, ret[1] = -1;
    return ret;

}

53. 最大子序和(0.499)

题目描述

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

解题思路

  暴力解法,遍历两次,记下max的值,在内层循环和累加值比较。
  方法二:贪心
  方法三:分治
https://leetcode-cn.com/problems/maximum-subarray/solution/chun-csan-chong-fang-fa-bao-li-po-jie-tan-xin-suan/

代码


int maxSubArray(int* nums, int numsSize){
    
    

    int i = 0;
    int j = 0;
    int Max = nums[0];
    int Tmp = 0;

    for (i = 0; i < numsSize; i++)
    {
    
    
        Tmp = 0;
        for (j = i; j < numsSize; j++)
        {
    
    
            Tmp += nums[j];
            if (Tmp > Max)
            {
    
    
                Max = Tmp;
            }
        }
    }
    return Max;
}

双指针复写数组

26. 删除排序数组中的重复项(0.493)

题目描述

  给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

  不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素。 示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。 for (int
i = 0; i < len; i++) {
print(nums[i]); }

解题思路

  方法一:双指针,一个用来遍历数组,另一个用来指向新数组的下标。首先注意数组是有序的,那么重复的元素一定会相邻。

  要求删除重复元素,实际上就是将不重复的元素移到数组的左侧。
  考虑用 2 个指针,一个在前记作 p,一个在后记作 q,算法流程如下:
  比较 p 和 q 位置的元素是否相等。
  如果相等,q 后移 1 位
  如果不相等,将 q 位置的元素复制到 p+1 位置上,p 后移一位,q 后移 1 位
  重复上述过程,直到 q 等于数组长度。
  返回 p + 1,即为新数组长度。
在这里插入图片描述
  方法二:暴力循环

代码

/*双指针*/
int removeDuplicates(int* nums, int numsSize){
    
    
     if(numsSize < 2)
        return numsSize;
    int a = 0, b = 1;
    while(b < numsSize)
        if(nums[b++] > nums[a])
            nums[++a] = nums[b - 1];
    return (a + 1);
}
/*暴力循环*/
int removeDuplicates(int* nums, int numsSize){
    
    
 for(int i = 0; i < numsSize - 1; i++)
    {
    
    
        for(int j = i + 1; j < numsSize; j++)
        {
    
    
            if(nums[i] < nums[j])
            {
    
    
                nums[i + 1] = nums[j];
                break;
            }
            else if(j == numsSize -1)
            {
    
    
                return i + 1;
            }
        }
    }
    return numsSize;//如果输入数组长度为1,上面循环不执行,直接返回1
    }

27. 移除元素(0.578)

题目描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。 示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,

函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

注意这五个元素可为任意顺序。

你不需要考虑数组中超出新长度后面的元素。

解题思路

  此题和“删除排序数组中的重复项”思想一样,都采用双指针来解决。
  一个(慢)num指针为数组本身进行复写,一个(快)p指针不断移动并判断是否等于val的值。一旦快指针所指向的值不等于val,则进行复写,将快指针所指向的值复写慢指针所指向的值,且计数器加一。(注意顺序:先*nums=p[i],后nums++;因为慢指针的最后一项总是待复写的位置,所以若快指针所指向的值不等于val时,先将快指针的值复写慢指针的值,再将慢指针往后挪一位)

代码


int removeElement(int* nums, int numsSize, int val){
    
    

    int count=0;
    int *p=nums;
    for(int i=0;i<numsSize;i++){
    
    
        if(p[i]!=val){
    
    
            *nums=p[i];
            nums++;
            count++;
        }
    }
    return count;
}

283. 移动零(0.601)

题目描述

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。 尽量减少操作次数。

解题思路

  方法一:和删除数组元素的题目很像,主要思想就是利用双指针复写数组。将不等于零的元素写进数组,最后将剩下的位置填充零即可。
  方法二:优化版双指针。当i==j时,不做任何操作。当i!=j时,将nums[j]=nums[i]的并且将nums[i]填充0,遍历一次即可完成。

代码

void moveZeroes(int* nums, int numsSize){
    
    

    int i,j=0;
    //双指针复写数组
    for(i = 0;i<numsSize;i++)
    {
    
    
        if(nums[i]!=0)
        {
    
    
            nums[j++] = nums[i];
        }
        else
        {
    
    
            continue;
        }
    }
    //将数组剩下的位置填充零
    for(int i = j;i<numsSize;i++)
    {
    
    
        nums[i] = 0;
    }    
}
void moveZeroes(int* nums, int numsSize){
    
    

    int i,j=0;
    for(i = 0;i<numsSize;i++)
    {
    
    
        if(nums[i] != 0)
        {
    
    
            if(i != j)
            {
    
    
                nums[j] = nums[i];
                nums[i] = 0;
            }
            j++;
        }
    }
}

717. 1比特与2比特字符(0.471)

题目描述

有两种特殊字符。第一种字符可以用一比特0来表示。第二种字符可以用两比特(10 或 11)来表示。
现给一个由若干比特组成的字符串。问最后一个字符是否必定为一个一比特字符。给定的字符串总是由0结束。

示例 1:

输入: bits = [1, 0, 0]
输出: True
解释:
唯一的编码方式是一个两比特字符和一个一比特字符。所以最后一个字符是一比特字符。 示例 2:

输入: bits = [1, 1, 1, 0]
输出: False
解释:
唯一的编码方式是两比特字符和两比特字符。所以最后一个字符不是一比特字符。 注意:

1 <= len(bits) <= 1000. bits[i] 总是0 或 1.

解题思路

  不需要遍历所有
  分类讨论如下:
  1.如果倒数第二位为0,则必然正确;0可以是单独1比特,也可以和1组成2比特。所以,如果倒数第二位为0,则一定不会和最后一位组合。
  2.如果倒数第二位为1,则连续1的个数必须为偶数;如果连续1的个数为奇数,那么倒数第二位的1一定会和最后一位组合成2比特。
由于第1种情况(0个1)也算偶数,所以只要判断从倒数第二位开始是否有连续偶数个1即可。

代码

bool isOneBitCharacter(int* bits, int bitsSize){
    
    

    if(bitsSize == 1) return true;
        int i = bitsSize-2;
        int count = 0;
        while(i >= 0 && bits[i] == 1){
    
    
            i--;
            count++;
        }
        if(count % 2 == 0) return true;
        return false;
}

121. 买卖股票的最佳时机(0.538)

题目描述

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。 示
例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路

  方法一:暴力:其实就是将数组每个元素和他的后面的元素依次做差,找出差最大的
  方法二:在方法一的基础上优化下,遍历一遍也可以,每次都记录差值进行比较。
  1、以当前点卖出的最大利润 = 当前点价格 - 当前点前的最小值
  2、遍历一次,一方面记录当前点前的最小值,一方面记录当前点的利润,更新最大值

代码

int maxProfit(int* prices, int pricesSize){
    
    
    //其实就是将数组每个元素和他的后面的元素依次做差,找出差最大的
    int MaxDiffer = 0;
    for(int i = 0;i<pricesSize-1;i++)
    {
    
    
        for(int j = i+1;j<pricesSize;j++)
        {
    
    
            int Difference = prices[j]-prices[i];
            if(Difference > MaxDiffer) MaxDiffer = Difference;
        }
    }
    return  MaxDiffer;

}
int maxProfit(int* prices, int pricesSize){
    
    
    int i;
    int min;
    int diff;
    int max_diff = 0;

    if (pricesSize == 0 || pricesSize == 1) 
    {
    
    
        return 0;
    }

    min = prices[0];
    for (i = 1; i < pricesSize; i++) 
    {
    
    
        if (prices[i - 1] < min)
        {
    
    
            min = prices[i - 1];
        }
        
        diff = prices[i] - min;
        if (diff > max_diff) 
        {
    
    
            max_diff = diff;
        }
    }

    return max_diff;
}

122. 买卖股票的最佳时机 II(0.584)

题目描述

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:

1 <= prices.length <= 3 * 10 ^ 4 0 <= prices[i] <= 10 ^ 4

解题思路

  本质是求正的公共子数组的总和。直接计算每天差价,若因为分区间计算就是升序序列直接相邻天差价的和。直接遍历一次数组,如果相邻两天差价为正,就加到利润中

代码

int maxProfit(int* prices, int pricesSize){
    
    
   if(pricesSize==0) return 0;
    int profit=0;
    for(int i=1;i<pricesSize;i++)
       if(prices[i]>prices[i-1])
          profit += prices[i]-prices[i-1];
    return profit;
}

88. 合并两个有序数组(0.471)

题目描述

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 你可以假设 nums1 有足够的空间(空间大小大于或等于 m +n)来保存 nums2 中的元素。

示例:

输入: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3

输出: [1,2,2,3,5,6]

解题思路

  方法一:分别遍历两个数组nums1和nums2,申请nums3的空间存放二者排序的结果
  方法二 :其实也可以不用申请额外空间,从后往前遍历两个数组,统一合并到nums1中。

代码

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    
    
  int *nums3 = (int *)malloc(sizeof(int) * (m + n));
  int i = 0,j = 0,k = 0;
  //遍历两个数组,只要有一个遍历完成时就退出
    while(i < m && j < n)
    {
    
    
        if(nums1[i] <= nums2[j])
        {
    
    
            nums3[k] = nums1[i];
            i++;
        }
        else
        {
    
    
            nums3[k] = nums2[j];
            j++;
        }
        k++;
    }
    //当nums1遍历完成,nums2还有元素时,直接把nums2的放在nums3后面
    if(i == m && j < n)
    {
    
    
        while(j < n)
        {
    
    
            nums3[k] = nums2[j];
            j++;
            k++;
        }
    }
     //当nums2遍历完成,nums1还有元素时,直接把nums1的放在nums3后面
    if(j == n && i < m)
    {
    
    
        while(i < m)
        {
    
    
            nums3[k] = nums1[i];
            i++;
            k++;
        }
    }
    //把nums3的元素赋值给nums1
    for(i = 0;i < nums1Size;i++)
    {
    
    
        nums1[i] = nums3[i];
    }
}
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    
    
    int M = m - 1;
    int N = n - 1;
    int cur = m + n - 1;
    // 从后往前处理,nums2都加到num1上去
    while (M >= 0 && N >= 0)
    {
    
    
        if (nums1[M] >= nums2[N])
        {
    
    
            nums1[cur--] = nums1[M--];
        }
        else if (nums1[M] < nums2[N])
        {
    
    
            nums1[cur--] = nums2[N--];
        }   
    }
    // 要是nums2有剩余的,处理一下
    while (N >= 0)
    {
    
    
        nums1[cur--] = nums2[N--];
    }
}

杨辉三角

118. 杨辉三角(0.66)

题目描述

给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 5
输出:
[
[1],
[1,1],
[1,2,1],
[1,3,3,1]
[1,4,6,4,1]
]

解题思路

  注意对题目中returnSize和returnColumnSizes的理解。

代码

 //returnSize是指向一个整数的指针,表示返回杨辉三角的行数 = 输入numRows
   *returnSize = numRows;
   //returnColumnSizes是指向一个数组的指针,数组元素为对应行的元素个数
    *returnColumnSizes = (int*)malloc(numRows * sizeof(int));
    //res是一个指针,它指向的是由指针构成的数组,每个指针都指向对应的三角的一行数;res也是二维数组
    int **res = (int**)malloc((*returnSize) * sizeof(int*));
    int i = 0;
    //每一行开头和结尾的数都是固定值
    for(; i < *returnSize; i++){
    
    
        //需要告诉程序 每一行开辟的大小
        (*returnColumnSizes)[i] = i + 1;
        res[i] = (int*)malloc((*returnColumnSizes)[i] * sizeof(int));
        res[i][0] = 1;
        res[i][i] = 1; 
    }
    //第三行以后的中间的数值是上一行对角的值
    for(i = 2; i <= numRows - 1; i++){
    
    
        for(int j = 1; j < i; j++){
    
    
            res[i][j] = res[i - 1][j - 1] + res[i - 1][j];
        }
    }
    return res;

119. 杨辉三角 II(0.605)

题目描述

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
在杨辉三角中,每个数是它左上方和右上方的数的和。

示例:

输入: 3 输出: [1,3,3,1]
进阶:
你可以优化你的算法到 O(k) 空间复杂度吗?

解题思路

动态规划,每一次数组从后往前更新,该位置的数组的值为该位置i加上位置i-1的值,第0个和最后一个元素为1

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* getRow(int rowIndex, int* returnSize){
    
    
    int *res = (int *)calloc(10000, sizeof(int));
    if(rowIndex == 0)
    {
    
    
        *returnSize = 1;
        res[0] = 1;
        return res;
    }
    int size = rowIndex;
    *returnSize = size;
    int *lastRow = getRow(rowIndex - 1, returnSize);
    int i;
    
    for(i = 0;i <= rowIndex;i++)
    {
    
    
        if(i == 0 || i == rowIndex)
        {
    
    
            res[i] = 1;
        }
        else
        {
    
    
            res[i] = lastRow[i - 1] + lastRow[i];
        }
    }
    *returnSize = i;
    return res;
    
}


561. 数组拆分 I(0.7)

题目描述

给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。

示例 1:

输入: [1,4,3,2]

输出: 4 解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4). 提示:

n 是正整数,范围在 [1, 10000]. 数组中的元素范围在 [-10000, 10000].

解题思路

  一直在想怎样满足题目中的:“将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。”这个要求。然后就发现示例中的中的做法正好满足,先排序,然后从头开始取出每对中最小的数再相加就是和最大的时候。如果删除掉红框中的提示,这道题还要多花时间想一想。
在这里插入图片描述

代码

int mymin(int a,int b){
    
    
    return (a>b?b:a);
}
int comp(const void*a,const void*b){
    
    
    return *(int*)a-*(int*)b;
}

int arrayPairSum(int* nums, int numsSize){
    
    
    int i=0,sum=0;
    qsort(nums,numsSize,sizeof(int),comp);
    for(i=0;i<numsSize;i=i+2)
    {
    
    
        sum=sum+mymin(nums[i],nums[i+1]);
    }
    return sum;

}

169. 多数元素(0.628)

题目描述

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [3,2,3]
输出: 3 示例 2:

输入: [2,2,1,1,1,2,2]
输出: 2

解题思路

  方法一:快排,将数组排序,返回下标为nums + numsSize / 2的数
  方法二:摩尔投票法

代码

int compare(const void* a, const void* b)
{
    
    
    return (*(int*)a - *(int*)b);
}

int majorityElement(int* nums, int numsSize) {
    
    
    //使用qsort函数快速排序
    qsort(nums, numsSize, sizeof(int), compare);
    //将数组排序,返回下标为nums + numsSize / 2的数
    return *(nums + numsSize / 2);
}
int majorityElement(int* nums, int numsSize){
    
    
    int s = 1;
	int maj = nums[0];
	for (int i = 1; i < numsSize; i++) {
    
    
		if (maj == nums[i]){
    
    
			s++;
		}
		else {
    
    
			s--;
		}
		if (s == 0) {
    
    
		//抵消完了,换下一个元素
			maj = nums[i + 1];
		}
	}
	return maj;

}

摩尔投票法

作者:喝七喜
链接:https://www.zhihu.com/question/49973163/answer/235921864
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  为了理解一个代码量很短的算法,先通过更复杂的数据结构和空间复杂度了解会更为具体,然后去掉冗余就得到了简短的算法。
重点:

首先请考虑最基本的摩尔投票问题,找出一组数字序列中出现次数大于总数1/2的数字(并且假设这个数字一定存在)。显然这个数字只可能有一个。摩尔投票算法是基于这个事实:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。请首先认同这个事实,这里不证明了~如果你已经了解摩尔投票法的代码,只是无法理解其中变量的实际意义是什么,你可以根据以上这个事实自己再理一遍。

  所以我们的目标就是:删除,删除,删除。删到不能删除为止。

  实现的算法从第一个数开始扫描整个数组,有两个变量(参考第一答题者的变量名)major和count。其实这两个变量想表达的是一个“隐形的数组”array,array存储的是“当前暂时无法删除的数字”,我们先不要管major和count,只考虑这个array,同时再维护一个结果数组result,result里面存储的是每次删除一对元素之后的当前结果。为了方便理解举一个示例

  输入:{1,2,1,3,1,1,2,1,5}从第一个数字1开始,我们想要把它和一个不是1的数字一起从数组里抵消掉,但是目前我们只扫描了一个1,所以暂时无法抵消它,把它加入到array,array变成了{1},result由于没有抵消任何元素所以还是原数组{1,2,1,3,1,1,2,1,5}。

  然后继续扫描第二个数,是2,我们可以把它和一个不是2的数字抵消掉了,因为我们之前扫描到一个1,所以array变成了{},result变成了{1,3,1,1,2,1,5}继续扫描第三个数1,无法抵消,于是array变成了{1},result还是{1,3,1,1,2,1,5};

  接下来扫描到3,可以将3和array数组里面的1抵消,于是array变成了{},result变成了{1,1,2,1,5}

  接下来扫描到1,此时array为空,所以无法抵消这个1,array变成了{1},result还是{1,1,2,1,5}

  接下来扫描到1,此时虽然array不为空,但是array里也是1,所以还是无法抵消,把它也加入这个array,于是array变成了{1,1}(其实到这我们可以发现,array里面只可能同时存在一种数,因为只有array为空或当前扫描到的数和array里的数字相同时才把这个数字放入array),result还是{1,1,2,1,5}

  接下来扫描到2,把它和一个1抵消掉,至于抵消哪一个1,无所谓,array变成了{1},result是{1,1,5}

  接下来扫描到1,不能抵消,array变成了{1,1},result{1,1,5}

  接下来扫描到5,可以将5和一个1抵消,array变成了{1},result变成了{1}至此扫描完成了数组里的所有数,result里剩了1,所以1就是大于一半的数组。

  再回顾一下这个过程,其实就是删除(抵消)了(1,2),(1,3),(1,5)剩下了一个1。

  除去冗余关系:实际代码中没有array,也没有result,因为我们不需要。由于前面提到array里只可能同时存储一种数字,所以我们用major来表示当前array里存储的数,count表示array的长度,即目前暂时无法删除的元素个数,最后扫描完所有的数字,array和result变成一样了,都表示“最后还是无法删除的数字”。

  我们再根据只有两个变量的实际代码理一遍:
  major 初始化随便一个数
  count 初始化为0
  输入:{1,2,1,3,1,1,2,1,5}

  扫描到1,count是0(没有元素可以和当前的1抵消),于是major = 1,count = 1(此时有1个1无法被抵消)

  扫描到2,它不等于major,于是可以抵消掉一个major => count -= 1,此时count = 0,其实可以理解为扫到的元素都抵消完了,这里可以暂时不改变major的值

  扫描到1,它等于major,于是count += 1 => count = 1

  扫描到3,它不等于major,可以抵消一个major => count -= 1 => count = 0,此时又抵消完了(实际的直觉告诉我们,扫描完前四个数,1和2抵消了,1和3抵消了)

  扫描到1,它等于major,于是count += 1 => count = 1

  扫描到1,他等于major,无法抵消 => count += 1 => count = 2 (扫描完前六个数,剩两个1无法抵消)

  扫描到2,它不等于major,可以抵消一个major => count -= 1 => count = 1,此时还剩1个1没有被抵消

  扫描到1,它等于major,无法抵消 => count += 1 => count = 2

  扫描到5,它不等于major,可以抵消一个major => count -= 1 => count = 1至此扫描完成,还剩1个1没有被抵消掉,它就是我们要找的数。

  最开始假设了“一定存在一个出现次数大于总数一半的数",还有一些问题比如“找出出现次数大于1/3的所有数,并且不一定存在”,以后有时间了再总结吧 ~

941. 有效的山脉数组(0.351)

题目描述

.给定一个整数数组 A,如果它是有效的山脉数组就返回 true,否则返回 false。

让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:

A.length >= 3
在 0 < i < A.length - 1 条件下,存在 i 使得:
A[0] < A[1] < …A[i-1] < A[i]
A[i] > A[i+1] > … > A[A.length - 1]
示例 1:

输入:[2,1] 输出:false 示例 2:

输入:[3,5,5] 输出:false 示例 3:

输入:[0,3,2,1] 输出:true

提示:

0 <= A.length <= 10000 0 <= A[i] <= 10000

解题思路

  方法一:先编写函数找出数组中最大的数的下标,这个下标之前的数应该是严格递增的,这个下标之后的数应该是严格递减的。
  方法二:上山步数和下山步数相加等于总长度-1;

代码

int max(int* A, int ASize)//找出数组中最大的数的下标
{
    
    
    int i=0,index=0,max=A[0];
    for(i=0;i<ASize;i++)
    {
    
    
        if(A[i]>max)
        {
    
    
            max=A[i];
            index=i;
        }
    }
    return index;
}
bool validMountainArray(int* A, int ASize){
    
    
    if(ASize<3)//数组长度小于3为假
    {
    
    
        return 0;
    }
    int i=0,re=0,ans=1;
    re=max(A,ASize);
    if(re==0||re==ASize-1)//这两种情况,整个数组都是递增或递减的,为假
    {
    
    
        return 0;
    }
    for(i=0;i<re;i++)//数组从零直到最大元素都应该是递增的
    {
    
    
        if(A[i]>=A[i+1])
        {
    
    
            ans=0;
            break;
        }
    }
    for(i=re;i<ASize-1;i++)//数组从最大元素直到结尾都应该是递减的
    {
    
    
        if(A[i]<=A[i+1])
        {
    
    
            ans=0;
            break;
        }
    }
    return ans;
}
bool validMountainArray(int* A, int ASize){
    
    

   if (ASize < 3)
    {
    
    
        return false;
    }
    int inc = 0;
    int dec = 0;
    for (int i = 0; i < ASize - 1; i++) 
    {
    
    
        if (A[i] < A[i + 1] && dec == 0) 
        {
    
    
            inc++;
        } else if (A[i] > A[i + 1] && inc > 0)
        {
    
    
            dec++;
        }
    }
    if ((inc + dec == ASize - 1) && (inc > 0 && inc < ASize - 1) && (dec > 0 && dec < ASize - 1))
    {
    
    
        return true;
    }
    return false;
}

747. 至少是其他数字两倍的最大数(0.39)

题目描述

在一个给定的数组nums中,总是存在一个最大元素 。
查找数组中的最大元素是否至少是数组中每个其他数字的两倍。
如果是,则返回最大元素的索引,否则返回-1。

示例 1:

输入: nums = [3, 6, 1, 0]
输出: 1
解释: 6是最大的整数, 对于数组中的其他整数,
6大于数组中其他元素的两倍。6的索引是1, 所以我们返回1.

示例 2:

输入: nums = [1, 2, 3, 4]
输出: -1 解释: 4没有超过3的两倍大, 所以我们返回 -1.

提示:

nums 的长度范围在[1, 50]. 每个 nums[i] 的整数范围在 [0, 100].

解题思路

  方法一:首先遍历一遍数组,找出最大值max和其下标index。接着再次遍历数组和最大值做除法,当商大于等于二,cnt++,记录下符合要求的数组元素的个数。当cnt == numsSize-1,返回index。
  方法二:直接遍历数组寻找secmax,max,最后做除法。

代码

int dominantIndex(int* nums, int numsSize){
    
    

    int max = nums[0];
    int cnt = 0;
    int index = 0;
    for(int i = 1;i< numsSize;i++)
    {
    
    
        if(nums[i]>max)
        {
    
    
            max = nums[i];
            index = i;
        }

    }
    for(int j = 0;j < numsSize;j++)
    {
    
    
        if(nums[j] == 0)
        {
    
    
            cnt++;
            continue;
        }
        else
        {
    
    
            double x  = max/nums[j];
             printf("x:%lf\r\n",x);
            if(x>=2)
            {
    
    
                 
                 cnt++;
                 printf("x>2:%d",cnt);
            }
        }
    }
    if(cnt == numsSize-1)  return index;
    return -1;
}

int dominantIndex(int* nums, int numsSize)
{
    
    
if(numsSize==1) return 0;
    int max=0,secmax=0,maxindex=0;
    int i;
    for(i=0;i<numsSize;i++){
    
    
        if(nums[i]>max){
    
    //更新最大
            secmax=max;
            maxindex=i;
            max=nums[i];
        }else if(nums[i]>secmax){
    
    //更新次大
            secmax=nums[i];
        }
    }
    if(max<2*secmax)
        return -1;
    return maxindex;

}


448. 找到所有数组中消失的数字(0.572)

题目描述

给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

示例:

输入: [4,3,2,7,8,2,3,1]

输出: [5,6]

解题思路

  方法一:哈希。遍历nums数组,以nums[i]为下标存放进hash中,最后遍历hash,hash[i]为0的即为没有出现的元素

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){
    
    

    //初始化时数组大小必须时确定的 这种方法不对:int *hash[numsSize+1] = {0};
    int *hash=(int *)calloc((numsSize+1),sizeof(int));
    int  *result=(int *)malloc(sizeof(int)* numsSize);
    int k = 0;
    //把所有的数按照大小存放进hash中,最后遍历hash即可
    for(int i = 0;i < numsSize;i++)
    {
    
    
        hash[nums[i]]++;
    }
    //hash[]从下标1开始到numsSize,存放nums[i],num[i]的值从1开始
    for(int j = 1;j < numsSize+1;j++)
    {
    
    
        //如果出现的次数为0,即缺少nums[j],即缺少j。
        if(hash[j] == 0)
        {
    
    
            result[k++] = j; 
        }
    }
    *returnSize=k;
    return result;
}

628. 三个数的最大乘积(0.494)

题目描述

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

示例 1:

输入: [1,2,3]
输出: 6
示例 2:

输入: [1,2,3,4]
输出: 24
注意:
给定的整型数组长度范围是[3,104],数组中所有的元素范围是[-1000, 1000]。
输入的数组中任意三个数的乘积不会超出32位有符号整数的范围。

解题思路

  方法一:排序,分类讨论.全是非负数,全是非正数,正数负数都有
  方法二:
  1、如果结果3个正数,则取3个最大数
  2、如果结果2个正数、1个负数,结果为负数,则取3个最大数
  3、如果结果1个正数、2个负数,结果为正数,则取1个最大和2个最小。
  4、如果结果3个负数,结果为负数,则取3个最大数
  所以找到最大的三个数,最小的两个数,将最大的三个数相乘,最小的两个数与最大的一个数相乘,两个乘积最大的就是结果,根据这个思路,通过排序即可实现,按照这个想法做相应的判断与操作就可以得出所需要的结果

代码

int compare(void *a,void*b)
{
    
    
    return (*(int*)a-*(int*)b);
}
int maximumProduct(int* nums, int numsSize){
    
    

    qsort(nums,numsSize,sizeof(int),compare);
    for(int i = 0;i < numsSize;i++)
    printf("%d ",nums[i]);
    int max;
    //三个数全是负数或者全是正数
    if(nums[numsSize-1] <= 0 || nums[0] >= 0)
        max = nums[numsSize-1] * nums[numsSize - 2] * nums[numsSize - 3];
   //数组有正有负
    if(nums[1] < 0 && nums[numsSize-1] >= 0)
         //两正一负  
        if(nums[numsSize-2] * nums[numsSize-3] >= nums[0] * nums[1])
            max = nums[numsSize-3] * nums[numsSize-2] * nums[numsSize - 1]; 
        else
        // 两负一正
            max = nums[0] * nums[1] * nums[numsSize - 1]; 
    return max;
}
int compare(void *a,void*b)
{
    
    
    return (*(int*)a-*(int*)b);
}
int maximumProduct(int* nums, int numsSize){
    
    

    qsort(nums,numsSize,sizeof(int),compare);
    int max1 = nums[numsSize - 3] * nums[numsSize - 2] * nums[numsSize - 1];
    int max2 = nums[0] * nums[1] * nums[numsSize - 1];
    
    if(max1 > max2)
        return max1;
    return max2;


}

66. 加一

题目描述

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

解题思路

  从后向前(从个位)开始+1,逢九进一,不为九时结束,全为九时数组长度加一
解法如下: 特别要注意的是returnSize,这个代表返回指针的长度,要不上层代码不知道要取多少个

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* plusOne(int* digits, int digitsSize, int* returnSize){
    
    

  for (int i = digitsSize - 1; i >= 0; --i) {
    
    
        if (digits[i] == 9) {
    
    
            digits[i] = 0;
        } else {
    
    
            digits[i]++;
            *returnSize = digitsSize;
            return digits;
        }
    }
    //如果之前没有返回,说明有进位产生了,则重新分配digitsSize + 1的空间,将第一位赋值为1,后面都为0
    int *result = (int *) malloc(sizeof(int) * (digitsSize + 1));
    memset(result, 0, (digitsSize + 1) * sizeof(int));
    result[0] = 1;
    *returnSize = digitsSize + 1;
    return result;
}

697. 数组的度(0.521)

题目描述

给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

示例 1:

输入: [1, 2, 2, 3, 1]
输出: 2
解释: 输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示: [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1,2, 2], [2, 2, 3], [2, 2] 最短连续子数组[2, 2]的长度为2,所以返回2. 示例 2:

输入: [1,2,2,3,1,4,2]
输出: 6
注意:
nums.length 在1到50,000区间范围内。 nums[i] 是一个在0到49,999范围内的整数。

解题思路

  首先找频数最大的元素,再计算出该元素第一次出现与最后一次出现的角标做差加一就是运算的结果,如果有多个满足频数最大的元素,则取运算的结果最小的,按照这个思路做相应的判断与操作就可以得出所需要的结果

代码

int findShortestSubArray(int* nums, int numsSize){
    
    

    int a[50000]={
    
    0};
    int i=0,max=-1,val=0,start=-1,end=numsSize-1,res=numsSize,j=0;
    //遍历数组,找出数组的度max
    for(i=0;i<numsSize;i++)
    {
    
    
        a[nums[i]]++;
        if(max<a[nums[i]]){
    
    
            max=a[nums[i]];
        }
    }
    //遍历数组a找出度对应元素的第一次出现的位置和最后一次出现的位置
    for(i=0;i<50000;i++){
    
    
        //当两者相等,记录下a元素的下标i,下标即为nums数组的数值
        if(max==a[i]){
    
    
            end=-1;
            start=-1;
            //同样遍历数组nums,找出nums数组中和i相等的位置,记录nums的下标
            //start初值为-1,如果是第一次出现,就直接赋值给start,后面如果再有相等的,则更新赋值给end
            for(j=0;j<numsSize;j++){
    
    
                if(nums[j]==i){
    
    
                    end=j;
                    if(start==-1){
    
    
                        start=j;
                    }
                }
            }
            //start和end做差+1即可得到长度,每次选取res最小的返回
            if(end-start+1<res){
    
    
                res=end-start+1;
            }
        }
    }
    return res;
}

268. 缺失数字(0.548)(相同数字异或)

题目描述

给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。

示例 1:

输入: [3,0,1]
输出: 2
示例 2:

输入: [9,6,4,2,3,5,7,0,1]
输出: 8

说明: 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?

解题思路

  方法一:哈希数组
  方法二:异或。好像和以前的一道题(只出现一次的数字)有异曲同工之处。看了大家的题解,异或操作(^)是一种很好的方式,不用考虑sum越界问题。
举个例子:

0 ^ 4 = 4
4 ^ 4 = 0

  那么,就可以不用求和,直接使用异或运算^进行 抵消,剩下的数字就是缺失的了。
再举个例子:

1^1^2^2^3 = 3

  方法三:最简单的方法应该是求和,然后和1到n项的数列和对比,相差的数就是缺的这个数

代码

int missingNumber(int* nums, int numsSize){
    
    

    int hash[50000] = {
    
    0};
    for(int i = 0;i < numsSize;i++)
    {
    
    
        hash[nums[i]]++;
    }
    for(int i = 0;i < 50000;i++)
    {
    
    
        if(hash[i]==0)
        {
    
    
            return i;
        }
    }
    return  -1;

}
int missingNumber(int* nums, int numsSize){
    
    

     int res = numsSize;
    for (int i = 0; i < numsSize; ++i){
    
    
        res ^= nums[i];
        res ^= i;
    }
    return res;

}
int missingNumber(int* nums, int numsSize){
    
    

    int sum = 0;
    for(int i = 0; i < numsSize; i++){
    
    
            sum += nums[i];
    }
        return numsSize * (numsSize + 1) / 2 - sum;

}

35. 搜索插入位置(0.453)

题目描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5 输出: 2 示例 2:

输入: [1,3,5,6], 2 输出: 1 示例 3:

输入: [1,3,5,6], 7 输出: 4 示例 4:

输入: [1,3,5,6], 0 输出: 0

解题思路

  方法一:直接遍历。如果遍历完,最后跳出,说明插到数组末尾。
  方法二:二分查找

代码

  方法一:

int searchInsert(int* nums, int numsSize, int target){
    
    

    for(int i = 0;i < numsSize;i++)
    {
    
    
        if(nums[i] == target) return i;
        else if (nums[i]>target)    return i;
    }
    return numsSize;

}

  方法二:

int searchInsert(int* nums, int numsSize, int target){
    
    

    if(nums==NULL || numsSize==0) return 0;

    // binary Search
    int start = 0, end = numsSize -1;
    int mid = 0;
    
    while(start<=end) {
    
      
        mid = start + (end-start)/2;
        if(nums[mid]==target) return mid;  // 找到了
        else if(nums[mid] < target) start = mid + 1;
        else    end = mid - 1;
    }
    // 没有找到时 返回的是靠前的元素的位置
    return start;

}

189. 旋转数组(0.409)

题目描述

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释: 向右旋转 1 步: [7,1,2,3,4,5,6] 向右旋转 2 步: [6,7,1,2,3,4,5] 向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释: 向右旋转 1 步: [99,-1,-100,3] 向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 要求使用空间复杂度为 O(1) 的 原地 算法。

解题思路

  方法一:使用额外空间
  方法二:使用反转数组的方法,例如k为3时(次优,但是好理解):
  原始数组 : 1 2 3 4 5 6 7
  反转所有数字后 : 7 6 5 4 3 2 1
  反转前 k 个数字后 : 5 6 7 4 3 2 1
  反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果

代码

  方法一:

void rotate(int* nums, int numsSize, int k){
    
    

    int *arr = (int*)malloc(sizeof(int)*(numsSize));
    int j = 0;
    for(int i = 0;i < numsSize;i++)
    {
    
    
        arr[(i+k)%numsSize] = nums[i];
    }
    for(int i = 0;i < numsSize;i++)
    {
    
    
        nums[i] = arr[i];
    }
}

  方法二:

void reverse(int* arr,int start,int end)
{
    
    
    while(start < end)
    {
    
    
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}

void rotate(int* nums, int numsSize, int k){
    
    
    //考虑当k>numsSize时 求出相对于自身位置的真实移动距离 计算出nums[0]移动后的最后位置,即(0+k)%numSize
    //numsSize = 3,k = 5,每个元素实际移动距离 5%3 = 2;
    //numsSize = 3,k = 1,每个元素实际移动距离 1%3 = 1;
    k %= numsSize; 
    //反转整个数组
    reverse(nums,0,numsSize-1);
    //反转前k个位置的数组
    reverse(nums,0,k-1);
    //反转k -- numsSize的数组
    reverse(nums,k,numsSize-1);

}

1217. 玩筹码(0.678)

题目描述

数轴上放置了一些筹码,每个筹码的位置存在数组 chips 当中。
你可以对 任何筹码 执行下面两种操作之一(不限操作次数,0 次也可以):
将第 i 个筹码向左或者右移动 2 个单位,代价为 0。
将第 i 个筹码向左或者右移动 1 个单位,代价为 1。
最开始的时候,同一位置上也可能放着两个或者更多的筹码。
返回将所有筹码移动到同一位置(任意位置)上所需要的最小代价。

示例 1:

输入:chips = [1,2,3]
输出:1
解释:第二个筹码移动到位置三的代价是 1,第一个筹码移动到位置三的代价是 0,总代价为 1。
示例 2:
输入:chips = [2,2,2,3,3]
输出:2
解释:第四和第五个筹码移动到位置二的代价都是 1,所以最小总代价为 2。

提示:
1 <= chips.length <= 100 1 <= chips[i] <= 10^9

解题思路

  难点在于理解题目意思。chips = [1, 2, 2, 2, 3, 15, 23] 意味着位置1有1个筹码,位置2有3个筹码,同样位置3、15、23各有1个筹码。把所有筹码移动到一个位置,移动两步代价为0,那其实我就可以把所有“奇数位置”筹码移动到一个点A(奇数位置),且不必花费任何代价;同理,所有“偶数位置”的筹码移动到点B(偶数位置)也不需要任何代价。最终就是看A,B两点谁的筹码数多,把少的筹码移动到多的筹码的位置,每一个筹码的代价都是1。

  因为移动2个位置不需要代价,那么奇数位置移到奇数位置不用代价,偶数位置移到偶数位置不用代价,那就分别统计奇数位置和偶数位置的个数,相当于把所有奇数放一起,所有偶数的放一起,然后比较奇数的少还是偶数的少,将少的个数移到多的个数位置上去就可以了

代码



int minCostToMoveChips(int* chips, int chipsSize){
    
    
    int odd =0, even = 0;
    for(int i = 0; i < chipsSize; i++)
    {
    
    
        if(chips[i]%2)
            odd++;
        else
            even++;
    }
    return (odd <= even)? odd: even;
}

665. 非递减数列(0.221)未做

题目描述

给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。

示例 1:

输入: nums = [4,2,3]
输出: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。 示例 2:

输入: nums = [4,2,1]
输出: false
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

说明:

1 <= n <= 10 ^ 4
10 ^ 5 <= nums[i] <= 10 ^ 5

解题思路

代码

217. 存在重复元素(0.518)

题目描述

给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。

示例 1:

输入: [1,2,3,1]
输出: true
示例 2:

输入: [1,2,3,4] 输出: false 示例 3:

输入: [1,1,1,3,3,4,3,2,4,2] 输出: true

解题思路

  方法一:快排+判断相邻元素是否重复
  方法二:哈希

代码

  方法一:

int cmp(const void*a, const void*b) {
    
    
    return *(int*)a - *(int*)b;
}

bool containsDuplicate(int* a, int n)
{
    
    
    if (a == NULL || n <= 0) {
    
    
        return false;
    }
    qsort(a, n, sizeof(int), cmp);
    for (int i = 1; i < n; i++) {
    
    
        if (a[i] == a[i - 1]) {
    
    
            return true;
        }
    }
    return false;
}

  方法二:


int find_hash(int n, int * hash, int size)
{
    
    
    int location = (n + size / 2) % size;
    if (location < 0) {
    
    
        location = -location;
    }

    while (hash[location] != n && hash[location] != -234567) {
    
    
        location++;
        if (location >= size) {
    
    
            location = 0;
        }
    }

    if (hash[location] == n) {
    
    
        return 1;
    }
    else {
    
    
        return 0;
    }
}

void insert_hash(int n, int * hash, int size)
{
    
    
    int location = (n + size / 2) % size;
    if (location < 0) {
    
    
        location = -location;
    }

    while (hash[location] != -234567) {
    
    
        location++;
        if (location >= size) {
    
    
            location = 0;
        }
    }

    hash[location] = n;

    return;
}

void init_hash(int * hash, int size)
{
    
    
    int i;

    for (i = 0; i < size; i++) {
    
    
        hash[i] = -234567;
    }

    return;
}

bool containsDuplicate(int* nums, int numsSize){
    
    

    if (numsSize == 0) {
    
    
        return false;
    }

    int hash_size = 5 * numsSize;

    int hash[hash_size];
    int i;

    init_hash(hash, hash_size);

    for (i = 0; i < numsSize; i++) {
    
    
        if (find_hash(nums[i], hash, hash_size)) {
    
    
            return true;
        }
        else {
    
    
            insert_hash(nums[i], hash, hash_size);
        }
    }
    return false;
}


219. 存在重复元素 II(0.379)

题目描述

给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:

输入: nums = [1,2,3,1], k = 3 输出: true
示例 2:

输入: nums = [1,0,1,1], k = 1 输出: true
示例 3:

输入: nums = [1,2,3,1,2,3], k = 2 输出: false

解题思路

  方法一:结构体数组排序再遍历
  方法二:C有uthash.h头文件,再也不用担心C被哈希虐了。本题就是一直找,再遇到两数相等且|j-i|<=k即可马上返回

代码

typedef struct node_t{
    
    
    int value;
    int index;
} node;

int cmp(void *a, void *b) {
    
    
	if ((((node*)a)->value<((node*)b)->value)) return -1;
	else if ((((node*)a)->value==((node*)b)->value)) return 0;
	else return 1;
   
}


bool containsNearbyDuplicate(int* nums, int numsSize, int k){
    
    
    node array[100000];
    int i,j;
    for(i=0;i<numsSize;i++){
    
    
        array[i].value = nums[i];
        array[i].index = i;
    }
    qsort(array, numsSize, sizeof(node), cmp);
    for(i=0;i<numsSize; i++){
    
    
        for(j=i+1;j<numsSize;j++){
    
    
            if(array[i].value == array[j].value){
    
    
                if(array[j].index-array[i].index <= k){
    
    
                    return true;
                }
            }
            else{
    
    
                break;
            } 
        }
    }
    return false;
}

  方法二:

typedef struct hash{
    
    
    int key;  // 键
    int index;  // 索引值
    UT_hash_handle hh; // 让结构体哈希柄
} *hash_ptr;

bool containsNearbyDuplicate(int* nums, int numsSize, int k){
    
    
    hash_ptr p=NULL, tables=NULL;
    for(int i=0;i<numsSize;i++){
    
    
        if(tables) HASH_FIND_INT(tables, &(nums[i]), p);
         //如果哈希表中已经存在这个元素,判断当前正在放入的和已经放入的索引值的差值
        if(p&&(i-p->index)<=k) return true;
        p=(hash_ptr)malloc(sizeof(*p));
        p->key=nums[i];
        p->index=i;
        HASH_ADD_INT(tables, key, p);
    }
    return false;
}

581. 最短无序连续子数组(0.343)

题目描述

给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

你找到的子数组应是最短的,请输出它的长度。

示例 1:

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9]进行升序排序,那么整个表都会变为升序排序。 说明 : 输入的数组长度范围在 [1, 10,000]。 输入的数组可能包含重复元素 ,所以升序的意思是<=。

解题思路

方法一:
  确定无序子数组即找出子数组的边界,边界的特点是破坏了数组的递增趋势。顺序遍历,找出所有破坏递增趋势的数,并记录最小值,该最小值非数组最小值,而是子区间的最小值,即为正常升序数组中排在非变动数字右边的第一个数;逆序遍历同理,找出子区间最大值。

  从左到右循环,记录最大值为 max,若 nums[i] < max, 则表明位置 i 需要调整, 循环结束,记录需要调整的最大位置 i 为 high; 同理,从右到左循环,记录最小值为 min, 若 nums[i] > min, 则表明位置 i 需要调整,循环结束,记录需要调整的最小位置 i 为 low.
方法二:
1、先进行排序,让数组升序;
2、从前向后,寻找第一个与之前序列不同的元素所在位置begin
3、从后向前遍历 , ,end
4、相减返回begin-end+1。如果已经有序就返回0

代码

int findUnsortedSubarray(int* nums, int numsSize){
    
    
        if(numsSize <= 1) {
    
    
            return 0;
        }
        int high = 0,low = numsSize-1,CurMax =nums[0],CurMin=nums[numsSize-1];
        for(int i = 0;i<numsSize;i++){
    
    
            if(nums[i] >= CurMax){
    
    
                CurMax = nums[i];
            } else {
    
    
                high = i;
            }
            if(nums[numsSize-i-1] <= CurMin){
    
    
                CurMin = nums[numsSize-i-1];
            } else {
    
    
                low = numsSize - i - 1;
            }
        }
        return high > low ? high -low + 1 : 0;
    }

int compare(const void *a,const void *b){
    
    
    return *(int*)a-*(int*)b;
}
int findUnsortedSubarray(int* nums, int numsSize){
    
    
    int begin=0;int end=0;
    int *numscpy=(int*)malloc(numsSize*sizeof(int));
    memcpy(numscpy,nums,sizeof(int)*numsSize);
    qsort(numscpy,numsSize,sizeof(int),compare);
    for(int i=0;i<numsSize;i++){
    
    
        if(numscpy[i]!=nums[i]){
    
    
            begin=i;
            break;
        }
    }
    for(int i=numsSize-1;i>=0;i--){
    
    
        if(numscpy[i]!=nums[i]){
    
    
            end=i;
            break;
        }
    }
    if(begin==end){
    
    
        return 0;
    }
    return end-begin+1;
}

905. 按奇偶排序数组(0.684)

题目描述

给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。
你可以返回满足此条件的任何数组作为答案。

示例:

输入:[3,1,2,4]
输出:[2,4,3,1]
输出 [4,2,3,1],[2,4,1,3] 和 [4,2,1,3] 也会被接受。

提示:

1 <= A.length <= 5000 0 <= A[i] <= 5000

解题思路

  定义额外数组和两个头尾下标,遍历原数组,将偶数从前往后放,奇数从后往前放。

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* sortArrayByParity(int* A, int ASize, int* returnSize){
    
    

    *returnSize = ASize;
    if (ASize < 2) 
        return A;

    int *arr = (int *)malloc(sizeof(int)*ASize);
    int head = 0,tail = ASize-1;
    for(int i = 0;i<ASize;i++)
    {
    
    
        if(A[i]%2==0)
            arr[head++] = A[i];
        else
            arr[tail--] = A[i];
    }
    
    return arr;

}

1013. 将数组分成和相等的三个部分(0.408)

题目描述

给你一个整数数组 A,只有可以将其划分为三个和相等的非空部分时才返回 true,否则返回 false。
形式上,如果可以找出索引 i+1 < j 且满足 A[0] + A[1] + … + A[i] == A[i+1] + A[i+2] + … + A[j-1] == A[j] + A[j-1] + … + A[A.length - 1] 就可以将数组三等分。

示例 1:

输入:[0,2,1,-6,6,-7,9,1,2,0,1]
输出:true
解释:0 + 2 + 1 = -6 + 6 - 7 + 9 + 1 = 2 + 0 + 1 示例 2:

输入:[0,2,1,-6,6,7,9,-1,2,0,1]
输出:false
示例 3:

输入:[3,3,6,5,-2,2,5,1,-9,4] 输出:true 解释:3 + 3 = 6 = 5 - 2 + 2 + 5 + 1 -
9 + 4

提示:

3 <= A.length <= 50000
-10^4 <= A[i] <= 10^4

解题思路

  首先计算数组 AA 中所有数字总和 sum
  遍历数组 AA 查找和为 sum / 3的子数组个数
  如果找到了三个这样的子数组则返回 true
  找不到三个就返回 false

  疑问一:

  评论区好多人对 count == 3 有疑问, count == 3 之后还没有遍历完数组,怎么就能判断为 true 了?

  解答:

  首先 count == 3 就说明找到了三个和为 sum / 3 的子数组,那剩下元素的和就只能为 0 了,把它们合并到最后一个子数组就 OK 啦。

  疑问二:

  那 count == 2 不就可以返回 true 了吗? 因为已经找到了两个和为 sum / 3 的子数组,那剩下的不就是第三个了嘛!

  解答:

  其实不是这样的,因为找到了两个子数组后可能恰好到达了数组末尾,此时就不符合要求了,例如: [1, -1, 1, -1] .

代码

bool canThreePartsEqualSum(int* A, int ASize){
    
    

   int sum = 0;
    for (int i = 0; i < ASize; ++i) sum += A[i];
    int avg;
    if (sum % 3) return false;//不能3整除
    avg = sum / 3;

    int tmp = 0;
    int num = 0;
    for (int i = 0; i < ASize; ++i) {
    
    
        tmp += A[i];
        if (tmp == avg) {
    
    //记录等于avg的次数
            ++num;
            tmp = 0;
        }
     if (num == 3)
           return true;
           
    }
    return  false;
}

414. 第三大的数(0.347)

题目描述

给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。

示例 1:

输入: [3, 2, 1]

输出: 1

解释: 第三大的数是 1. 示例 2:

输入: [1, 2]

输出: 2

解释: 第三大的数不存在, 所以返回最大的数 2 . 示例 3:

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

输出: 1

解释: 注意,要求返回第三大的数,是指第三大且唯一出现的数。 存在两个值为2的数,它们都排第二。

解题思路

先用一个循环 找出最小值
然后再用一个循环 依次找出第一大 第二大 第三大的数

代码



int thirdMax(int* nums, int numsSize){
    
    
    int first,second,third;
    int i;
    int min=nums[0];
    for(i=1;i<numsSize;i++){
    
    //找最小,也可用min=INT_MIN来代替
        if(nums[i]<min){
    
    
            min=nums[i];
        }
    }
    first=second=third=min;
    for(i=0;i<numsSize;i++){
    
    //分类讨论,细节要周全
        if(nums[i]>first){
    
    
            third=second;
            second=first;
            first=nums[i];
        }
        else if(nums[i]>second&&nums[i]!=first){
    
    
            third=second;
            second=nums[i];
        }
        else if(nums[i]>third&&nums[i]!=first&&nums[i]!=second){
    
    //这里很关键
            third=nums[i];
        }
    }
    if(third==second||second==first){
    
    当三个层次的最值里面有两个值相等时   说明没有第三大 
        return first;
    }
    return third;
}

485. 最大连续1的个数(0.561)

题目描述

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

示例 1:

输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3. 注意:

输入的数组只包含 0 和1。 输入数组的长度是正整数,且不超过 10,000。

解题思路

  定义两个变量max和cnt,cnt记录连续1的个数,只要不是1就清零,从新计算,同时和max比较,更新下max的值。

代码

int findMaxConsecutiveOnes(int* nums, int numsSize){
    
    

    int max = 0,cnt = 0;
    for(int i = 0;i < numsSize;i++)
    {
    
    
        if(nums[i]==1)
        {
    
    
            cnt++;
            if(cnt>max)
                max = cnt;
        }
        else
        {
    
    
            cnt = 0;
        }
            
    }
    return max;
}

566. 重塑矩阵(0.643)难懂参数的意思

题目描述

在MATLAB中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。
给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。
如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

示例 1:

输入: nums = [[1,2], [3,4]] r = 1, c = 4
输出: [[1,2,3,4]]
解释:
行遍历nums的结果是 [1,2,3,4]。新的矩阵是 1 * 4 矩阵, 用之前的元素值一行一行填充新矩阵。
示例 2:

输入: nums = [[1,2], [3,4]] r = 2, c = 4
输出: [[1,2], [3,4]]
解释:
没有办法将 2 * 2 矩阵转化为 2 * 4 矩阵。 所以输出原矩阵。 注意:

给定矩阵的宽和高范围在 [1, 100]。 给定的 r 和 c 都是正数。

解题思路

   C做这种题比较吃亏,得先弄懂数组指针和指针数组,还要明白最后两个参数什么意思, int* returnSize代表返回矩阵的行数,int** returnColumnSizes代表返回矩阵每行的列数

代码

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int** matrixReshape(int** nums, int numsSize, int* numsColSize, int r, int c, int* returnSize, int** returnColumnSizes){
    
    
    //元素数量不匹配
    if(r*c != numsSize*(*numsColSize)) 
    {
    
    
		*returnSize = numsSize;
		*returnColumnSizes = numsColSize;
		return nums;
	}	
    //生成新矩阵
	*returnColumnSizes = (int*)malloc(sizeof(int) * r);
	int **res = (int**)malloc(sizeof(int*)*r), x = 0, y = 0;
	for (int i = 0; i < r; i++)
    {
    
    
		res[i] = (int*)malloc(sizeof(int)*c);
		(*returnColumnSizes)[i] = c;
	}
    *returnSize = r;
  //转移
	for (int i = 0; i < numsSize; i++)
		for (int j = 0; j < *numsColSize; j++)
        {
    
    
		    if (y == c)
            {
    
    
			    y = 0;
                x++;
		    }
		    res[x][y++] = nums[i][j];
		}

	return res;


}

643. 子数组最大平均数 I(0.381)滑动窗口

题目描述

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。

示例 1:

输入: [1,12,-5,-6,50,3], k = 4
输出: 12.75
解释: 最大平均数 (12-5-6+50)/4 = 51/4= 12.75

注意:

1 <= k <= n <= 30,000。 所给数据范围 [-10,000,10,000]。

解题思路

  如果每次都去求子数组的和会超时,所以使用滑动窗口,每次减去前一组的最后一个值,再加上最后一个值。最后返回时作除法

代码

double findMaxAverage(int* nums, int numsSize, int k)
{
    
    
    int maxsum, sum = 0;
    int i;
    for(i = 0; i < k; i++)
        sum += nums[i];
    maxsum = sum;
    for(i = 1; i+k-1 < numsSize; i++)
    {
    
      
        sum = sum + nums[i+k-1] - nums[i-1];
        maxsum = sum > maxsum?sum:maxsum;
    }
    return (double)maxsum/k;
}


605. 种花问题*(0.315)

题目描述

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:

输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
示例 2:

输入: flowerbed = [1,0,0,0,1], n = 2
输出: False
注意:

数组内已种好的花不会违反种植规则。 输入的数组长度范围为 [1, 20000]。 n 是非负整数,且不会超过输入数组的大小。

解题思路

  我们从左到右扫描数组 flowerbed,如果数组中有一个 0,并且这个 0 的左右两侧都是 0,那么我们就可以在这个位置种花,即将这个位置的 0 修改成 1,并将计数器 count 增加 1。对于数组的第一个和最后一个位置,我们只需要考虑一侧是否为 0。
  在扫描结束之后,我们将 count 与 n 进行比较。如果 count >= n,那么返回 True,否则返回 False。

  基本思路:要确定存在三个连续的坑才能在中间插入。但是需要注意左右两边的边界问题和单元素的极端情况。
  对于前者细致分为三种情况:
  1.左边界,只需要考虑相邻右元素是否存在坑
  2.右边界,只需要考虑相邻左元素是否存在坑
  3.中间,考虑左右两边元素是否存在坑

代码

bool canPlaceFlowers(int* flowerbed, int flowerbedSize, int n){
    
    
    int count=0,i;//计数器
    if(flowerbedSize==1&&flowerbed[0]==0)
        count++;
    else
    {
    
    
        for(i=0;i<flowerbedSize;i++)
        {
    
    
            if(flowerbed[i]==0)
            {
    
    
                if(i==0)//首元素,处理左边界
                {
    
    
                    if(flowerbed[1]==0)
                    {
    
    
                        count++;
                        flowerbed[i]=1;
                    }
                }
                else if(i==flowerbedSize-1)//处理右边界
                {
    
    
                    if(flowerbed[i-1]==0)
                        count++;
                        flowerbed[i]=1;
                }
                else if(flowerbed[i-1]==0&&flowerbed[i+1]==0)//处理中间元素
                {
    
    
                    count++;
                    flowerbed[i]=1;
                }
            }
        }
	}
    if(count>=n)
    	return true;
    return false;
}


674. 最长连续递增序列( 0.444)

题目描述

  给定一个未经排序的整数数组,找到最长且连续的的递增序列,并返回该序列的长度。

示例 1:

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。 尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。
示例 2:
输入: [2,2,2,2,2]
输出: 1
解释: 最长连续递增序列是 [2], 长度为1。
注意:数组长度不会超过10000。

解题思路

  首先列出可能出现的数组类型,大致可以分为以下五种:

1.[]
2.[1]
3.[1,1,1]
4.[1,2,3,4,5]
5.[7,8,9,1,2,3,4]

  对于情况1和2,可以单独列出来做一个判断,并返回对应数组长度作为结果。
  对于情况3,4,5,进行for循环,并判断后一项是否大于前一项,若是,则计数+1,若不是,计数重置为1。
  在for循环中每进行一次if判断都取ans和j中的最大值,可以保证循环结束时ans的值是数组中最长的连续递增序列的长度。

方法二:滑动窗口

代码

int findLengthOfLCIS(int* nums, int numsSize){
    
    
    if(numsSize == 0)
        return 0;
    if(numsSize == 1)
        return 1;
    int length = 1;
    int max = 1;
    for(int i = 0; i < numsSize - 1; i++){
    
    
        if(nums[i] < nums[i+1])
            length++;
        else
            length = 1;
        if(length > max)
            max = length;
    }
    return max;
}


#define MAX(a, b)   ((a)>(b)?(a):(b))

int findLengthOfLCIS(int* nums, int numsSize)
{
    
    
    int start, end;
    int max_len;

    if (nums == NULL || numsSize == 0)
        return 0;

    start = 0;
    end = 1;
    max_len = 1;
    while (end < numsSize) {
    
    
        if (nums[end-1] >= nums[end]) {
    
    
            start = end;
        }
        max_len = MAX(max_len, end-start+1);
        end++;
    }

    return max_len;
}

724. 寻找数组的中心索引(0.34)滑动窗口

题目描述

给定一个整数类型的数组 nums,请编写一个能够返回数组 “中心索引” 的方法。
我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。
如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。

示例 1:

输入: nums = [1, 7, 3, 6, 5, 6]
输出:3
解释: 索引 3 (nums[3] = 6) 的左侧数之和 (1 + 7 + 3 = 11),与右侧数之和 (5 + 6 = 11) 相等。 同时, 3 也是第一个符合要求的中心索引。
示例 2:
输入: nums = [1, 2, 3]
输出:-1
解释: 数组中不存在满足此条件的中心索引。
说明:

nums 的长度范围为 [0, 10000]。 任何一个 nums[i] 将会是一个范围在 [-1000, 1000]的整数。

解题思路

  方法一:S 是数组的和,当索引 i 是中心索引时,位于 i 左边数组元素的和 leftsum 满足 S - nums[i] - leftsum。我们只需要判断当前索引 i 是否满足 leftsum==S-nums[i]-leftsum 并动态计算 leftsum 的值。

  方法二:滑动窗口,先设定一个初始位置0(坑啊,题目出的有问题,应该从1开始才对,没办法,题目认为0左边没元素也符合。。)已其为中心分别计算左、右的和,然后比较不相等就右移,更新左、右和即可,代码简单易懂。

代码

方法一

int pivotIndex(int* nums, int numsSize){
    
    

    if (numsSize < 3) 
    {
    
    
        return -1;
    }
    int sum = 0,right = 0,left = 0;
    for(int i = 0;i <numsSize;i++)
    {
    
    
        sum+=nums[i];
    }
    for(int i = 0;i < numsSize;i++)
    {
    
    
        //计算右边的和
        right = sum-nums[i]-left; 
        //左右相等则返回
        if(left == right)
            return i;
        //左边求和
        left += nums[i];      
    }
    return -1;
}

方法二

/*
*  1. 从位置0开始右移寻找,先计算left和right,然后每右移一位判断是否符合
*/

int pivotIndex(int* nums, int numsSize)
{
    
    
    if (numsSize < 3) {
    
    
        return -1;
    }
    int left = 0;
    int right = 0;
    for (int i = 1; i < numsSize; ++i) {
    
    
        right += nums[i];
    }

    int pos = 0;
    while (left != right) {
    
    
        if (pos + 1 == numsSize) {
    
    
            return -1;
        }
        left += nums[pos];
        right -= nums[pos + 1];
        pos++;
    }
    return pos;
}

1287. 有序数组中出现次数超过25%的元素(0.607)

题目描述

给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。
请你找到并返回这个整数

示例:

输入:arr = [1,2,2,6,6,6,6,7,10]
输出:6

提示:

1 <= arr.length <= 10^4 0 <= arr[i] <= 10^5

解题思路

  方法一:将每个元素放进哈希表,遍历哈希表求次数相等的元素。
  方法二:数组有序,且某元素出现次数超过25%。那么对于此元素第1次出现位置,加25%数组长度,必定仍为它自身

代码

方法一

int findSpecialInteger(int* arr, int arrSize){
    
    

    if (arrSize < 1) return -1;
    int flag[100000] = {
    
    0};
    for(int i = 0;i < arrSize;i++)
    {
    
    
        flag[arr[i]]++;
    }
    int cnt = arrSize*0.25;
    for(int i = 0;i < 100000;i++)
    {
    
    
        if(cnt < flag[i])
            return i;
    }
    return -1;

}

方法二

int findSpecialInteger(int* arr, int arrSize){
    
    

 for (int *p = arr, *q = arr + arrSize / 4; ; p++, q++)
        if (*p == *q) return *p;
    return 0;  
}

1232. 缀点成线(0.508)

题目描述

在一个 XY 坐标系中有一些点,我们用数组 coordinates 来分别记录它们的坐标,其中 coordinates[i] = [x, y] 表示横坐标为 x、纵坐标为 y 的点。
请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回 true,否则请返回 false。

示例 1:
在这里插入图片描述
输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]
输出:true
示例 2:
在这里插入图片描述
输入:coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]]
输出:false

提示:

2 <= coordinates.length <= 1000 coordinates[i].length == 2
-10^4 <= coordinates[i][0], coordinates[i][1] <= 10^4 coordinates 中不含重复的点

解题思路

  直线的两点式方程(y-y2)/(y1-y2) = (x-x2)/(x1-x2)。取前两个点组成直线,判断后面的点是否在直线上

代码

bool checkStraightLine(int** coordinates, int coordinatesSize, int* coordinatesColSize){
    
    
    if(coordinatesSize==2) return true;
    int i=0;
    for(i=2;i<coordinatesSize;i++)
    {
    
    
        if(
            (coordinates[i][0]-coordinates[1][0])*(coordinates[0][1]-coordinates[1][1])!=(coordinates[i][1]-coordinates[1][1])*(coordinates[0][0]-coordinates[1][0])
        )
        {
    
    
            return false;
        }
    }
    return true;
}

1002. 查找常用字符*(二维哈希)(0.667)

题目描述

给定仅有小写字母组成的字符串数组 A,返回列表中的每个字符串中都显示的全部字符(包括重复字符)组成的列表。例如,如果一个字符在每个字符串中出现 3 次,但不是 4 次,则需要在最终答案中包含该字符 3 次。

你可以按任意顺序返回答案。

示例 1:

输入:[“bella”,“label”,“roller”]
输出:[“e”,“l”,“l”]
示例 2:

输入:[“cool”,“lock”,“cook”]
输出:[“c”,“o”]

提示:

1 <= A.length <= 100 1 <= A[i].length <= 100 A[i][j] 是小写字母

解题思路

  用一个二维数组存储每个单词出现的a-z的个数,然后再按照列遍历,找到每个字符在所有单词中出现的最小个数,该个数就是结果中需要添加的个数;

代码

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
char ** commonChars(char ** A, int ASize, int* returnSize){
    
    
    if (A == NULL || ASize == 0) {
    
    
        (*returnSize) = 0;
        return NULL;
    }
    int res[100][26] = {
    
    0}; // 每个单词占一行
    char **rslt = malloc(sizeof(char*) * 100);

    int i, j;
    for (i = 0; i < 100; i++) {
    
    
        rslt[i] = malloc(sizeof(char) * 2);
    }

    for (i = 0; i < ASize; i++) {
    
    
        for (j = 0; j < strlen(A[i]); j++) {
    
    
            res[i][A[i][j] - 'a']++;
            //  printf("i = %d, %d\n", i, res[i][A[i][j] - 'a']);
        }
        
    }

    int idx = 0;
    //依次比较每个单词中j字符出现的次数
    for (i = 0; i < 26; i++) {
    
    
        int min = res[0][i];
        for (j = 1; j < ASize; j++) {
    
    
            if (res[j][i] < min) {
    
    
                min = res[j][i];
            }
        } // 找到最小的公共的
        while (min > 0) {
    
    
            rslt[idx][0] = i + 'a'; // 存储重复的字母
            rslt[idx][1] = '\0';
            idx++;
            min--;
        }
    }

    (*returnSize) = idx;
    return rslt;
}


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
char ** commonChars(char ** A, int ASize, int* returnSize){
    
    
    if (A == NULL || ASize == 0) {
    
    
        (*returnSize) = 0;
        return NULL;
    }
    int res[100][26] = {
    
    0}; // 每个单词占一行
    char **rslt = malloc(sizeof(char*) * 100);

    int i, j;
    for (i = 0; i < 100; i++) {
    
    
        rslt[i] = malloc(sizeof(char) * 2);
    }

    for (i = 0; i < ASize; i++) {
    
    
        for (j = 0; j < strlen(A[i]); j++) {
    
    
            res[i][A[i][j] - 'a']++;
            //  printf("i = %d, %d\n", i, res[i][A[i][j] - 'a']);
        }
        
    }

    int idx = 0;
    for (i = 0; i < 26; i++) {
    
    
        int min = res[0][i];
        for (j = 1; j < ASize; j++) {
    
    
            if (res[j][i] < min) {
    
    
                min = res[j][i];
            }
        } // 找到最小的公共的
        while (min > 0) {
    
    
            rslt[idx][0] = i + 'a'; // 存储重复的字母
            rslt[idx][1] = '\0';
            idx++;
            min--;
        }
    }

    (*returnSize) = idx;
    return rslt;
}


猜你喜欢

转载自blog.csdn.net/qq_16933601/article/details/107671119