学习链接: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;
}