C语言Leetcode每日记录

学习链接:https://github.com/CyC2018/CS-Notes

1: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 【21】

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

typedef struct ListNode {
    int value;
    ListNode* next;
}ListNode;

ListNode *mergeTwoLists(ListNode *l1, ListNode *l2)
{
    ListNode head;
    ListNode* cur = &head;
    while (l1 && l2) {
        if (l1.value <= l2.value) {
            cur->next = l1;
            l1 = l1.next;
        } else {
            cur->next = l2;
            l2 = l2.next;
        }
        cur = cur->next;
    }

    cur->next = (l1 == NULL ? l2 : l1);
    return head.next;
}

2:  数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

 

示例:

输入:n = 3
输出:[
       "((()))",
       "(()())",
       "(())()",
       "()(())",
       "()()()"
     ]

/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_SIZE 1430
void backTrack(int left, int right, int n, char *combination, int index, char **result, int *returnSize)
{
    if (left == n && right == n) {
        result[(*returnSize)] = (char*)calloc((2 * n + 1), sizeof(char));
        strcpy(result[*returnSize], combination);
        (*returnSize)++;
        return;
    }
    if (left < n) {
        combination[index] = '(';
        backTrack(left + 1, right, n, combination, index + 1, result, returnSize);
    }
    if (right < left && right < n) {
        combination[index] = ')';
        backTrack(left, right + 1, n, combination, index + 1, result, returnSize);
    }
}

char** generateParenthesis(int n, int* returnSize)
{    

    char* combination = (char*)calloc((2 * n + 1), sizeof(char));
    char** result = (char**)malloc(sizeof(char*) * MAX_SIZE);
    *returnSize = 0;
    backTrack(0, 0, n, combination, 0, result, returnSize);
    return result;
}

int main()
{
    int n = 3;
    int size = 0;
    char** r = generateParenthesis(n, &size);
    for (int j = 0; j < size; j++) {
        printf("The result is %s\n", r[j]);
    }
    return 0;
}

3: 下个排列

#输入:nums = [1,2,3]
#输出:[1,3,2]
#思路 1: 先找num[i]: 从后往前升序,遇到降序的第一个元素 num[j]: 从后往前遇到的第一个比num[i]大的 两者交换 保证下一个队列大的在前面
#    2: 之后left = i + 1; right = len - 1; [left, right]这个降序序列做双指针反转
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

void cmP(int* nums, int i, int j)
{
    int temp = 0;
    temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
    return;
}

void nextPermutation(int* nums, int numsSize)
{
    int i = numsSize - 2;
    while (i >= 0 &&  nums[i] >= nums[i+ 1]) {
        i--;
    }
    if (i >= 0) {
        int j = numsSize - 1;
        while (j >= 0 && nums[i] >= nums[j]) {
            j--;
        }
        cmP(nums, i, j);
    }

    int left = i + 1;
    int right = numsSize - 1;
    while (left < right) {
        cmP(nums, left, right);
        left++;
        right--;
    }
    return;
}

int main()
{
    int nums[3] = {3, 2, 1};
    int len = sizeof(nums) / sizeof(int);
    nextPermutation(nums, len);
    for (int i = 0; i < len; i++) {
        printf("the nums[%d] is %d\n", i, nums[i]);
    }
}

4: 搜索旋转排序数组

#序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。
#请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
#示例 1:

# 输入:nums = [4,5,6,7,0,1,2], target = 0
# 输出:4
#思路:由于要求复杂度为log(n),因此需要用到二分法。二分法前提是排序数组。对于旋转之后额数组,各一半的排序,因此也可以用。
# mid = right + len //2    nums[0] 和 nums[mid]进行比较确定一定升序的范围 之后 target做比较 移动左右指针
/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

int search(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;
        }
        if (nums[0] <= nums[mid]) {
            if (nums[0] <= target && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        } else {
            if (nums[mid] < target && target <= nums[numsSize - 1]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

int main()
{
    int nums[7] = {4, 5, 6, 7, 0, 1, 2};
    int numsSize = sizeof(nums) / sizeof(int);
    int target = 0;
    int result = search(nums, numsSize, target);
    printf("the result is %d\n", result);
}

5:数组总合

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

/*server.c*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_NUM 1432
int size = 0;

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

int numsSum(int* nums, const int numsLen)
{
    int result = 0;
    for (int i = 0; i < numsLen; i++) {
        result += nums[i];
    }
    return result;
}

void trackback(int** result, int* candidates, int* combination, int Sum, int index, int target, int left, int* returnSize, int** returnColumnSizes)
{
    int curSum = Sum;

    if (curSum > target) {
        return;
    }

    if (curSum == target) {
        result[*returnSize] = (int*)calloc(index, sizeof(int));
        memcpy(result[*returnSize], combination, sizeof(int) * index );
        (*returnColumnSizes)[*returnSize] = index;
        (*returnSize)++;
        return;
    }

    for (int i = left; i < size; i++) {
        if (i != left && candidates[i - 1] == candidates[i]) {
            continue;
        } 
        left = i;
        combination[index] = candidates[i];
        curSum = numsSum(combination, index + 1);
        trackback(result, candidates, combination, curSum, index + 1, target, left, returnSize, returnColumnSizes);
    }
    return;
}

int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes)
{
    int** result = NULL;
    result = (int **)malloc(sizeof(int*) * MAX_NUM);
    *returnColumnSizes = (int*)malloc(MAX_NUM * sizeof(int)); 
    size = candidatesSize;
    qsort(candidates, candidatesSize, sizeof(int), cmp);
    int* combination = (int*)calloc(MAX_NUM, sizeof(int));
    trackback(result, candidates, combination, 0, 0, target, 0, returnSize, returnColumnSizes);
    return result;
}

int main()
{
    int nums[4] = {2, 3, 6, 7};
    int len = sizeof(nums) / sizeof(int);
    int target = 7;
    int returnSize = 0;
    int* returnColumnSizes = NULL;
    int** result = combinationSum(nums, len, target, &returnSize, &returnColumnSizes);
    for (int i = 0; i < returnSize; i++) {
        for (int j = 0; j < returnColumnSizes[i]; j++) {
            printf("%d", result[i][j]);
        }
        printf("\n");
    }
}

6: 下一个更大元素 

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

思路: 单调栈,数组构造栈top, 两次循环,

#define MAX_NUM 14321
int* nextGreaterElements(int* nums, int numsSize, int* returnSize)
{
    int* result = (int*)malloc(sizeof(int) * numsSize);
    memset(result, -1, sizeof(int) * numsSize);
    int* stack = (int*)calloc(MAX_NUM, sizeof(int));
    int top = -1; // stack top ptr
    int cur = 0;

    for(int i = 0; i < numsSize; i++) {
        while (top > -1 && nums[i] > nums[stack[top]]) {
            cur = stack[top]; // pop
            top--;
            result[cur] = nums[i];
        }
        top++;
        stack[top] = i; // push
    }

    for(int i = 0; i < numsSize; i++) {
        while (top > -1 && nums[i] > nums[stack[top]]) {
            cur = stack[top]; // pop
            top--;
            result[cur] = nums[i];
        }
    }

    *returnSize = numsSize;
    return result;
}

7:

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

思路: 单调栈,数组构造栈top

int* dailyTemperatures(int* T, int TSize, int* returnSize)
{
    int* stack = (int*)calloc(TSize, sizeof(int));
    int* result = (int*)malloc(sizeof(int) * TSize);
    memset(result, 0, sizeof(int) * TSize);
    int top = -1;
    int pre_idx = 0;

    for(int i = 0; i < TSize; i++) {
        while (top > -1 && T[i] > T[stack[top]]) {
            pre_idx = stack[top];
            top--;
            result[pre_idx] = i - pre_idx;
        }
        top++;
        stack[top] = i;
    }

    *returnSize = TSize;
    return result;
}

8: 

给你两个长度相同的字符串,s 和 t

将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0

 

示例 1:

输入:s = "abcd", t = "bcdf", cost = 3
输出:3
解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。
#define MAX(x, y) ((x) > (y)) ? (x) : (y)

int equalSubstring(char * s, char * t, int maxCost)
{
    int len_s = strlen(s);
    int* record = (int*)calloc(len_s, sizeof(int));
    for (int i = 0; i < len_s; i++) {
        record[i] = abs(s[i] - t[i]);
    }

    int start = 0;
    int end = 0;
    int windowSum = 0;
    int res = 0;
    for (int end = 0; end < len_s; end++) {
        windowSum += record[end];
        while (windowSum > maxCost) {
            windowSum -= record[start];
            start += 1;
        }
        res = MAX(res, end - start + 1);
    }
    return res;
}

9: 买股票的最佳时机2

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

思路: 贪心算法

#define MAX(x, y) ((x) > (y)) ? (x) : (y)

int maxProfit(int* prices, int pricesSize)
{   
    int cur_max = 0;
    int res = 0;
    for(int i = 1; i < pricesSize; i++) {
        cur_max = MAX(0 ,prices[i] - prices[i - 1]);
        res += cur_max;
    }
    return res;
}

10: 和可被 K 整除的子数组

输入:A = [4,5,0,-2,-3,1], K = 5
输出:7
解释:
有 7 个子数组满足其元素之和可被 K = 5 整除:
[4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]

思路: 前缀法

int subarraysDivByK(int* A, int ASize, int K){
    int* resMap = (int*)calloc(K, sizeof(int));
    resMap[0] = 1;
    int count = 0;
    int preSumMod= 0;
    for(int i = 0; i < ASize; i++) {
        preSumMod = (preSumMod + A[i]) % K;
        if (preSumMod < 0) {
            preSumMod += K;
        }
        if (!resMap[preSumMod]) {
            resMap[preSumMod] = 1;
        } else {
            count += resMap[preSumMod];
            resMap[preSumMod] ++;
        }
    }
    return count;
}

11: 和为K的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 :

输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

思路:前缀和 + hash

typedef struct Node{
    int key;
    int value;
    struct Node * next;
} HashNode;

int hash(int key, int numsSize) // 哈希函数;;哈希函数的构造其实是随便写的,仅仅是为了分类用的,分的特别粗逼近1,就是不分类,那就是暴力
                                // 解法,等于是不分类;分的特别细,类别特别多,但是可能浪费资源。
{
    return key & (numsSize - 1); // 最常用的方法,key为正数时选用 key %(numsSize - 1), 这样写能够防止int溢出
}

bool containKey(HashNode *hashtable, int key, int numsSize)
{
    int index;
    HashNode *head = NULL;
    HashNode *tail = NULL;

    index = hash(key, numsSize);
    head = &hashtable[index]; // 表头节点
    tail = head->next; // 第一个数据节点
    // printf("inter containKey\n");
    while(tail) {
        if(tail->key == key) {
            // printf("key %d\n", key);
            return true;
        }
        tail = tail->next;
    }
    // printf("false key %d\n", key);
    return false;
}

int getKey(HashNode * hashtable, int key, int numsSize)
{
    HashNode * head = &hashtable[hash(key, numsSize)];
    HashNode * tail = head->next;
    while(tail){
        if(tail->key == key) return tail->value;
        tail = tail->next;
    }
    return -1; 
}


void push(HashNode * hashtable, int key, int numsSize)
{
    HashNode *head = &hashtable[hash(key, numsSize)]; // 找头,找爸爸。
    HashNode *tail = head->next;                      // 找尾,爸爸本身不算,后面带领的小弟才算。所以摸一下爸爸的屁股。
                                                      // 备注:哈希的目的就是通过简单的哈希函数,
                                                      // 把数据划分成大类,每个大类又是一个链表,记录具体的每个值;
    printf("inter push\n");
    while(tail) { // 只要尾巴不为空,就顺着尾巴往后摸,找到key值绝对相等的节点。
        if(tail->key == key){
            tail->value++;   // 真的key值相等,就在节点本身的计数上累加就好
            // printf("value++ %d\n", tail->value);
            return;
        }
        tail = tail->next;
    }
    // 头插建表    实在找不到,就顺势在尾巴上从新分一个节点,然后初始化就行。
    HashNode * q = malloc(sizeof(HashNode));
    q->key = key;
    q->value = 1;
    q->next = head->next;
    head->next = q;
    // printf("init a node key %d\n", key);
}

int subarraySum(int* nums, int numsSize, int k)
{
    HashNode * hashtable = malloc(sizeof(HashNode) * numsSize);   // 这里注意哈希表的大小
    memset(hashtable, 0, sizeof(HashNode) * numsSize);

    push(hashtable, 0, numsSize);  //为啥push一个0进去?
    int pre = 0, cnt = 0;
    // printf("numsSize = %d\n", numsSize);
    for(int i = 0; i < numsSize; i++) {
        pre += nums[i];
        // printf("i = %d\n", i);
        if(containKey(hashtable, pre - k, numsSize)) { // 这句代码的关键是pre -k 作为key值。对应下面的原理。
            cnt += getKey(hashtable, pre - k, numsSize);  
            // printf("cnt = %d\n", cnt);
        }
        push(hashtable, pre, numsSize);
    }
    return cnt;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值