leetcode-数组-简单-C-第一部分

文章目录

  • 序号1
  • 序号26
  • 序号27
  • 序号35
  • 序号53
  • 序号66
  • 序号88
  • 序号118
  • 序号119
  • 序号121
  • 序号122
  • 序号167
  • 序号169
  • 序号189
  • 序号217
  • 序号219
  • 序号268
  • 序号283
  • 序号414
  • 序号448
  • 序号485
  • 序号532

序号1

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

解析:采用暴力法
有负数 不好hash 且要记录出现和下标

int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    int* ans = (int*)malloc(sizeof(int) * 2);
    *returnSize = 0;

    for (int i = 0; i < numsSize; i++)
    {
        for (int j = i + 1; j < numsSize; j++)
        {
            if (nums[j] == target - nums[i])
            {
                ans[0] = i;
                ans[1] = j;
                *returnSize = 2;
                return ans;
            }
        }
    }
    return ans;
}

序号26

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

解析:删除元素->重写数组

  1. 使用快慢指针
    慢指针指向重写的下标 快指针指向遍历的下标
int removeDuplicates(int* nums, int numsSize){
    if (numsSize <= 1)
        return numsSize;
    int i = 0;

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

序号27

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

解析:删除元素->重写数组

  1. 使用快慢指针
    慢指针指向重写的下标 快指针指向遍历的下标
  2. 26号题是排序,此题无序找特定元素,判定条件不同而已
int removeElement(int* nums, int numsSize, int val){
    if (numsSize == 0 )
        return 0;
    int i = 0;

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

序号35

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

解析

  1. 查找元素
    排序数组->二分法 最后的left就是该插入的位置
int searchInsert(int* nums, int numsSize, int target){
    int left = 0;
    int right = numsSize - 1;
    int mid;

    while (left <= right)
    {        
    	mid = (left + right) / 2;
        if (nums[mid] == target)
            return mid;
        else if (nums[mid] > target)
            right = mid - 1;
        else
            left = mid + 1;
    }
    return left;
}

序号53

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

解析

  1. 子数组可以用滑动窗口法
  2. 这里使用动态规划
  3. 单变量的动态规划也就是在线处理
    sum 记录当前的和,比 0 小,重新计和,比 0 大,就继续加当前值
    用一个 ans 记录最大值
int maxSubArray(int* nums, int numsSize){
    int sum;
    int ans;
   
    sum = nums[0];
    ans = nums[0];
    for (int i = 1; i < numsSize; i++)
    {
        //sum = Max(sum, 0) + nums[i];  下面的if..else..等同意思
        if (sum > 0)
            sum += nums[i];
        else
            sum = nums[i];
            
        if (sum > ans)
            ans = sum;
    }
    return ans;
}

序号66

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

解析

  1. 进位
    检测当前位置,没有进位直接退出,进位也只进1
    如果首位还进位,如 99->100,重新开辟空间,首位置1
int* plusOne(int* digits, int digitsSize, int* returnSize){
    for (int i = digitsSize - 1; i >= 0; i--)
    {
       digits[i] += 1;
       digits[i] %= 10;
       if (digits[i] != 0)
       {
           *returnSize = digitsSize;
           return digits;
       }
    }
    
    int* ans = (int*)calloc(digitsSize + 1, sizeof(int));
    *returnSize = digitsSize + 1;
    ans[0] = 1;
    return ans;
}

扩展:如果不是加 1,可用 carry 记录进位数,初始为加数。
最后用 malloc 申请后,再用 memcpy 赋值到 ans+1,ans 首位置1

序号88

题目:给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

解析

  1. 写 nums1 数组
    因为 nums1 有足够空间,所以从尾开始写,大的放后面
    这样不用动 nums1 开始的元素
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    int pm = m-1;
    int pn = n-1;
    int p = nums1Size-1;  //nums1尾部下标

    while(pm >=0 && pn >= 0)
    {
        nums1[p] = nums1[pm] > nums2[pn] ? nums1[pm--] : nums2[pn--];  //可用if..else..
        p--;
    }
    if (pn >= 0)  //nums2赋值完应该是-1
        memcpy(nums1,nums2, sizeof(int) * (pn+1));
}

序号118

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

解析

  1. 参数说明
    传指针,有时是改变数组本身,有时是改变指针所指向的变量
    *returnSize传进来为了改变returnSize
    **returnColumnSizes传进来为了改变*returnColumnSizes
  2. 模拟填数组
    每行第一个写 1,末位写 1,中间的ans[i][j] = ans[i-1][j-1] + ans[i-1][j];
int** generate(int numRows, int* returnSize, int** returnColumnSizes) {
    int i, index;

    *returnColumnSizes = malloc(numRows * sizeof(int));  //*returnColumnSizes存放每行的列数
    for (i = 0; i < numRows; i++)
        (*returnColumnSizes)[i] = i + 1;

    int** res = malloc(numRows * sizeof(int*));
    for (i = 0; i < numRows; i++) {
        res[i] = malloc((*returnColumnSizes)[i] * sizeof(int));
        res[i][0] = 1;
        res[i][(*returnColumnSizes)[i] - 1] = 1;
        if (i > 1)
        {
            for (index = 1; index < (*returnColumnSizes)[i] - 1; index++)
                res[i][index] = res[i - 1][index - 1] + res[i - 1][index];
        }
    }
    *returnSize = numRows;
    return res;
}

序号119

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

解析

  1. 第一种方法就是 118 号题做法,不过很浪费空间
  2. 第二种运用数学知识,第 k 行的数为 C k 0 C k 1 C k 2 . . . C k k C^0_k \quad C^1_k \quad C^2_k \quad...\quad C^k_k Ck0Ck1Ck2...Ckk
    C n k = n ! / ( k ! ⋅ ( n − k ) ! ) = n ⋅ ( n − 1 ) ⋯ ( n − k + 1 ) / k ! C^k_n = n! / (k! \cdot (n-k)!) = n \cdot (n-1) \cdots (n-k+1)/k! Cnk=n!/(k!(nk)!)=n(n1)(nk+1)/k!
    进一步
    C n k = C n k − 1 ⋅ ( n − k + 1 ) / k C^k_n = C^{k-1}_n \cdot (n-k+1)/k Cnk=Cnk1(nk+1)/k
  3. 在计算组合数时,pre 进行乘法时可能会越界,所以定义为long long
    乘法计算的汇编为:
    d = a ∗ b / c d = a * b / c d=ab/c
    mov         eax,dword ptr [a]  
    imul        eax,dword ptr [b]  
    cdq  
    idiv        eax,dword ptr [c]  
    mov         dword ptr [d],eax 
    
int* getRow(int rowIndex, int* returnSize){
    int len = rowIndex + 1;
    int* ans = (int*)malloc(sizeof(int) * len);
    
    ans[0] = 1;
    long long pre = 1;
    for (int i = 1; i < len; i++)
    {
        pre = pre * (len - i) / i ;
        ans[i] = (int)pre;
    }
    
    *returnSize = len;
    return ans;
}

:源自于题解

序号121

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

解析

  1. 第一种方法,记录买入的最小价格,卖出的最大价格,输出利润
  2. 第二种方法运用一点动态规划思路,记录买入的最小价格,更新最大利润。
int maxProfit(int* prices, int pricesSize){
    if (pricesSize == 0)
        return 0;
    
    int min = prices[0];
    int ans = 0;

    for (int i = 1; i < pricesSize; i++)
    {
        if (prices[i] < min)
            min = prices[i];
        else if (prices[i] - min > ans)
            ans = prices[i] - min;
    }
    return ans;
}

序号122

题目:给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

解析:因为尽可能完成更多交易。

  1. 保证得到所有的正利润
int maxProfit(int* prices, int pricesSize){
    int ans = 0;

    for (int i = 1; i < pricesSize; i++)
    {
        if (prices[i] > prices[i-1])
            ans += prices[i] - prices[i-1];
    }
    return ans;
}

序号167

题目:给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

解析:加数如果大了,就减小;如果小了,就增大。

  1. 双指针法
    left 指向最小值,right 指向最大值,如果大了right--,如果小了left++
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    int left = 0;
    int right = numbersSize - 1;
    int* ans = (int*)malloc(sizeof(int) * 2);
    int sum;
    *returnSize = 0;

    while (left < right)
    {
        sum = numbers[left] + numbers[right];
        if (sum == target)
        {
            ans[0] = left + 1;
            ans[1] = right + 1;
            *returnSize = 2;
            break;
        }
        else if (sum < target)
            left++;
        else
            right--;
    }
    return ans;
}

序号169

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

解析

  1. 第一种:排序,统计次数
  2. 第二种,hash 表
  3. 第三种:投票算法
    非众数对众数一换一的抵消,最后还是会剩下众数
int majorityElement(int* nums, int numsSize){
    int ans;
    int cnt = 0;

    for (int i = 0; i < numsSize; i++)
    {
        if (cnt == 0)
            ans = nums[i];
        cnt += (nums[i] == ans) ? 1 : -1;
    }
    return ans;
}

序号189

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

解析

  1. 反转区间代替移动
    反转 [0, len - 1 - k],[len - k, len - 1],[0, len - 1]
  2. 保证 k 的范围小于 numsSize
    k = k % numsSize;
void Reverse(int* arr, int left, int right)
{
    while (left < right)
    {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left++;
        right--;
    }
}

void rotate(int* nums, int numsSize, int k){
    k = k % numsSize;

    Reverse(nums,0,numsSize-k-1);
    Reverse(nums,numsSize-k,numsSize-1);
    Reverse(nums,0,numsSize-1);
}

序号217

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

解析

  1. 第一种 hash 表
    考虑到可能会有负数,不好用数组
  2. 第二种排序
    排序后,重复的元素挨在一起的
    采用堆排序,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
void DownAdjust(int* arr, int len, int pos)
{
    int temp = arr[pos];
    int child = pos * 2 + 1;

    while (child < len)
    { 
        if (child + 1 < len && arr[child] < arr[child + 1])
            child++;
        if (arr[child] > temp)
        {
            arr[pos] = arr[child];
            pos = child;
        }
        else
            break;
        child = pos * 2 + 1;
    }
    arr[pos] = temp;
}

void HeapSort(int* arr, int len)
{
    for (int i = len/2 - 1; i >= 0; i--)
        DownAdjust(arr,len,i);

    for (int i = len-1; i > 0; i--)
    {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;

        DownAdjust(arr, i, 0);
    }
}

bool containsDuplicate(int* nums, int numsSize){
    if (numsSize < 2)
        return false;

    HeapSort(nums,numsSize);
    for (int i = 1; i < numsSize; i++)
    {
        if (nums[i] == nums[i-1])
            return true;
    }
    return false;
}

序号219

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

解析

  1. 第一种可以使用 hash ,不过可能存在负数,用数组不方便
  2. 第二种使用排序
    构造结构体,存储下标和值。
    对值进行排序
    遍历,值相同的判断下标差
typedef struct node
{
    int index;
    int data;
}node;

void DownAdjust(node* arr, int len, int pos)
{
    node temp = arr[pos];
    int child = pos * 2 + 1;

    while (child < len)
    {
        if (child + 1 < len && arr[child].data < arr[child+1].data)
            child++;
        if (arr[child].data > temp.data)
        {
            arr[pos] = arr[child];
            pos = child;
        }
        else
            break;
        child = pos * 2 + 1;
    }
    arr[pos] = temp;
}

void HeapSort(node* arr, int len)
{
    for (int i = len/2 - 1; i >= 0; i--)
        DownAdjust(arr, len, i);
    
    for (int i = len - 1; i > 0; i--)
    {
        node temp = arr[i];
        arr[i] = arr[0];
        arr[0] = temp;

        DownAdjust(arr, i, 0);
    }
}

bool containsNearbyDuplicate(int* nums, int numsSize, int k){
    if (numsSize < 2)
        return false;

    node* newNums = (node*)malloc(sizeof(node) * numsSize);
    for (int i = 0; i < numsSize; i++)
    {
        newNums[i].index = i;
        newNums[i].data = nums[i];
    }

    HeapSort(newNums, numsSize);
    for (int i = 1; i < numsSize; i++)
    {C
        if (newNums[i].data == newNums[i-1].data)
        {
            if (abs(newNums[i].index - newNums[i-1].index) <= k)
                return true;
        }
    }
    free(newNums);  //需要手动释放
    return false;
}

序号268

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

解析

  1. 第一种方法 hash
    遍历一遍序列,记录出现。再遍历 hash 数组
  2. 第二种使用排序
    排完序,检查下标和数是否对应
int missingNumber(int* nums, int numsSize){
    int* hash = (int*)calloc(numsSize + 1, sizeof(int));

    for (int i = 0; i < numsSize; i++)
        hash[nums[i]] = 1;

    for (int i = 0; i <= numsSize; i++)
    {
        if (hash[i] == 0)
            return i;
    }
    return -1;  //为了oj能过

序号283

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

解析:一开始想到把 0 全部后移,这要移动很多次非 0 元素

  1. 重写数组
    遍历数组,把非 0 元素从头重新写入,最后写 0
  2. 交换元素
    遍历数组,把非 0 元素与最前面的 0 交换

如果 0 少,第一种更快;0 多,第二种更快
这里采用第二种

void moveZeroes(int* nums, int numsSize){
    int pos = 0;

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

:源自于题解

序号414

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

解析:因为要求为 O ( n ) O(n) O(n),所以不能排序

  1. 遍历数组,更新前三大的数
    初值设为最小,前三大的数不能重复
int thirdMax(int* nums, int numsSize){
    long long ans[3];
    for (int i = 0; i < 3; i++)
        ans[i] = LLONG_MIN;

    for (int i = 0; i < numsSize; i++)
    {
        if (nums[i] == ans[0] || nums[i] == ans[1] || nums[i] == ans[2])
            continue;
        if (nums[i] > ans[0])
        {
            ans[2] = ans[1];
            ans[1] = ans[0];
            ans[0] = nums[i];
        }
        else if (nums[i] > ans[1])
        {
            ans[2] = ans[1];
            ans[1] = nums[i];
        }
        else if (nums[i] > ans[2])
            ans[2] = nums[i];
    }

    if (ans[2] != LLONG_MIN)
        return (int)ans[2];
    else
        return (int)ans[0];
}

序号448

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

解析:先想到 hash,用不同数字记录出现否

  1. 符号记录出现否
    遍历数组,每个数对应的下标,下标的数取负。即用符号标记出现
    用乘法标记为负
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){
    int* ans = (int*)malloc(sizeof(int) * numsSize);
    int len = 0;

    for (int i = 0; i < numsSize; i++)
    {
        int pos = abs(nums[i]) - 1;  //因为从 1 开始,减 1 位
        if (nums[pos] > 0)
            nums[pos] *= -1;
    }
    for (int i = 0; i < numsSize; i++)
    {
        if (nums[i] > 0)
        {
            ans[len] = i + 1;
            len++;
        }
    }
    *returnSize = len;
    return ans;
}

:源自于题解

序号485

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

解析:

  1. 模拟连续
    遇到 1,就累计。遇到 0,就重置
int findMaxConsecutiveOnes(int* nums, int numsSize){
    int sum = 0;
    int max = 0;

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

:如果数据 1 比较多,可以在等于 0 时再更新最大值,返回前更新一次

序号532

题目:给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对。这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k.

解析

  1. 第一种用 hash
    记录元素后,遍历数组找到符合条件的整数对
  2. 第二种用排序
    排完序,k == 0,找重复的;否则,遍历找符合条件的
void DownAdjust(int* arr, int len, int pos)
{
    int temp = arr[pos];
    int child = pos * 2 + 1;

    while (child < len)
    {
        if (child + 1 < len && arr[child] < arr[child + 1])
            child++;
        if (arr[child] > temp)
        {
            arr[pos] = arr[child];
            pos = child;
        }
        else
            break;
        child = pos * 2 + 1;
    }
    arr[pos] = temp;
}

void HeapSort(int* arr, int len)
{
    for (int i = len/2 - 1; i >= 0; i--)
        DownAdjust(arr, len, i);
    for (int i = len - 1; i > 0; i--)
    {
        int temp = arr[i];
        arr[i] = arr[0];
        arr[0] = temp;
        DownAdjust(arr, i, 0);
    }
}

int findPairs(int* nums, int numsSize, int k){
    if (k < 0 || numsSize < 2)
        return 0;
    int cnt = 0;
    HeapSort(nums, numsSize);

    for (int i = 0; i < numsSize - 1; i++)
    {
        while (i < numsSize - 1 && nums[i] == nums[i+1])
            i++;
        if (k == 0)
        {
            if (i > 0 && nums[i] == nums[i-1])
                ++cnt;
        }
        else
        {
            for (int j = i + 1; j < numsSize; j++)
            {
                if (nums[j] - nums[i] == k)
                {
                    cnt++;
                    break;
                }
            }
        } 
    }
    return cnt;
}

你可能感兴趣的:(c语言)