文章目录
🌈你好呀!我是 山顶风景独好
🎈欢迎踏入我的博客世界,能与您在此邂逅,真是缘分使然!😊
🌸愿您在此停留的每一刻,都沐浴在轻松愉悦的氛围中。
📖这里不仅有丰富的知识和趣味横生的内容等您来探索,更是一个自由交流的平台,期待您留下独特的思考与见解。🌟
🚀让我们一起踏上这段探索与成长的旅程,携手挖掘更多可能,共同进步!💪✨
第一题:赎金信
题目描述:
给定一个字符串S,通过将字符串中的每个字母转变成大写或小写,我们可以获得一个新的字符串。返回所有可能得到的字符串列表。返回的列表中的字符串可以按任意顺序排列。
示例输入与输出:
输入: “a1b2”
输出: [“a1b2”, “a1B2”, “A1b2”, “A1B2”]
输入: “3z4”
输出: [“3z4”, “3Z4”]
题目分析:
这是一道典型的回溯问题。我们需要遍历字符串中的每个字符,当遇到字母时,分别尝试将其转换为大写和小写,然后继续处理下一个字符。这样,我们就能生成所有可能的字符串组合。
解题思路:
初始化一个结果列表来存储所有可能的字符串。
使用递归函数来处理字符串的每个字符。
如果字符是数字,则直接添加到当前处理的字符串中。
如果字符是字母,则分别生成两个新字符串,一个将字母转为大写,一个转为小写,然后递归处理剩余的字符串。
当处理完所有字符时,将当前处理的字符串添加到结果列表中。
示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 定义结果列表的最大长度,根据输入字符串的长度动态调整
#define MAX_RESULTS 1024
// 存储结果的字符串数组
char** results;
int result_count;
// 辅助函数,用于将当前处理的字符串添加到结果列表中
void add_result(char* str) {
results[result_count] = strdup(str); // 使用strdup复制字符串,需要free
result_count++;
}
// 递归函数,生成所有可能的字符串组合
void generate_permutations(char* s, char* current, int index) {
if (s[index] == '\0') {
// 处理完所有字符,添加当前字符串到结果列表中
add_result(current);
return;
}
// 添加当前字符到当前处理的字符串中
current[index] = s[index];
if (isalpha(s[index])) {
// 如果是字母,分别生成大写和小写的情况
generate_permutations(s, current, index + 1); // 保持当前字符不变
current[index] = toupper(s[index]); // 转为大写
generate_permutations(s, current, index + 1); // 递归处理
current[index] = tolower(s[index]); // 恢复为小写,为下一次递归做准备
} else {
// 如果是数字,直接递归处理下一个字符
generate_permutations(s, current, index + 1);
}
}
char** letterCasePermutation(char* S, int* returnSize) {
int len = strlen(S);
results = (char**)malloc(MAX_RESULTS * sizeof(char*)); // 分配存储空间
result_count = 0;
// 为当前处理的字符串分配空间,并初始化为空字符串
char* current = (char*)calloc(len + 1, sizeof(char));
generate_permutations(S, current, 0); // 生成所有可能的字符串组合
*returnSize = result_count; // 返回结果数量
free(current); // 释放临时字符串的空间
return results;
}
// 示例测试代码
int main() {
char* S = "a1b2";
int returnSize;
char** result = letterCasePermutation(S, &returnSize);
for (int i = 0; i < returnSize; i++) {
printf("%s\n", result[i]);
free(result[i]); // 释放结果字符串的空间
}
free(result); // 释放结果数组的空间
return 0;
}
深入剖析:
递归与回溯:本题使用了递归与回溯的思想。递归函数通过不断地处理字符串中的每个字符,生成所有可能的组合。当遇到字母时,通过递归生成两个新的分支,一个将字母转为大写,一个保持小写。
内存管理:在C语言中,内存管理是一个重要的考虑因素。我们使用malloc和calloc来动态分配存储空间,并在使用完毕后使用free来释放空间,以避免内存泄漏。
字符串处理:C语言中的字符串处理需要特别注意字符串的结束符\0。在生成新字符串时,我们需要确保正确地添加结束符,以避免字符串溢出或未正确结束的问题。
第二题:实现Trie(前缀树)
题目描述
实现一个Trie(前缀树),包含insert、search和startsWith这三个操作。
Trie的基本概念
Trie是一种树形数据结构,专门用于处理字符串的前缀匹配问题。Trie的每个节点代表一个字符,从根节点到某个节点的路径代表一个字符串的前缀。
解题思路
定义Trie节点的结构体:
包含指向子节点的指针数组(大小为字母表的大小)。
包含一个布尔值,表示节点是否是一个完整字符串的结束。
实现Trie树的结构体:
包含根节点的指针。
创建Trie树:
分配内存给Trie树和根节点。
初始化根节点的子节点指针为NULL。
设置根节点的结束标志为false。
插入字符串:
从根节点开始,遍历字符串的每个字符。
计算字符在字母表中的索引。
如果当前字符的子节点为空,则创建新节点。
移动到下一个字符,继续处理。
在字符串的最后一个字符处,标记节点为字符串结束。
搜索字符串:
从根节点开始,遍历字符串的每个字符。
计算字符在字母表中的索引。
如果当前字符的子节点为空,则字符串不存在。
移动到下一个字符,继续处理。
在字符串的最后一个字符处,检查节点是否标记为字符串结束。
检查前缀:
与搜索字符串类似,但不检查最后一个字符的结束标志。
释放Trie树的内存:
递归释放每个节点的内存。
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define ALPHABET_SIZE 26
// 定义Trie节点的结构体
typedef struct TrieNode {
struct TrieNode* children[ALPHABET_SIZE];
bool isEndOfWord;
} TrieNode;
// 定义Trie树的结构体
typedef struct {
TrieNode* root;
} Trie;
// 创建Trie树节点
TrieNode* createTrieNode() {
TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode));
memset(node->children, 0, sizeof(node->children));
node->isEndOfWord = false;
return node;
}
// 创建Trie树
Trie* trieCreate() {
Trie* obj = (Trie*)malloc(sizeof(Trie));
obj->root = createTrieNode();
return obj;
}
// 插入字符串到Trie树中
void trieInsert(Trie* obj, char* word) {
TrieNode* node = obj->root;
while (*word) {
int index = *word - 'a';
if (node->children[index] == NULL) {
node->children[index] = createTrieNode();
}
node = node->children[index];
word++;
}
node->isEndOfWord = true;
}
// 搜索Trie树中的字符串
bool trieSearch(Trie* obj, char* word) {
TrieNode* node = obj->root;
while (*word) {
int index = *word - 'a';
if (node->children[index] == NULL) {
return false;
}
node = node->children[index];
word++;
}
return node->isEndOfWord;
}
// 检查Trie树中是否包含以某个前缀开头的字符串
bool trieStartsWith(Trie* obj, char* prefix) {
TrieNode* node = obj->root;
while (*prefix) {
int index = *prefix - 'a';
if (node->children[index] == NULL) {
return false;
}
node = node->children[index];
prefix++;
}
return true;
}
// 递归释放Trie树的内存
void trieFreeHelper(TrieNode* node) {
for (int i = 0; i < ALPHABET_SIZE; i++) {
if (node->children[i] != NULL) {
trieFreeHelper(node->children[i]);
}
}
free(node);
}
// 释放Trie树的内存
void trieFree(Trie* obj) {
if (obj != NULL && obj->root != NULL) {
trieFreeHelper(obj->root);
}
free(obj);
}
// 示例测试代码
int main() {
Trie* obj = trieCreate();
trieInsert(obj, "apple");
printf("Search for 'apple': %s\n", trieSearch(obj, "apple") ? "true" : "false");
printf("Search for 'app': %s\n", trieSearch(obj, "app") ? "true" : "false");
printf("Starts with 'app': %s\n", trieStartsWith(obj, "app") ? "true" : "false");
trieFree(obj);
obj = trieCreate();
trieInsert(obj, "banana");
printf("Search for 'banana': %s\n", trieSearch(obj, "banana") ? "true" : "false");
printf("Search for 'ban': %s\n", trieSearch(obj, "ban") ? "true" : "false");
printf("Starts with 'ban': %s\n", trieStartsWith(obj, "ban") ? "true" : "false");
trieFree(obj);
return 0;
}
代码解释
- 结构体定义:
TrieNode结构体包含26个子节点指针和一个布尔值。
Trie结构体包含一个根节点指针。- 函数实现:
createTrieNode函数用于创建并初始化一个新的Trie节点。
trieCreate函数用于创建并初始化一个新的Trie树。
trieInsert函数用于插入一个新的字符串到Trie树中。
trieSearch函数用于搜索一个完整的字符串是否在Trie树中。
trieStartsWith函数用于检查是否存在以某个前缀开头的字符串。
trieFreeHelper函数是递归函数,用于释放Trie树的内存。
trieFree函数释放Trie树的根节点和Trie树本身的内存。- 测试代码:
创建Trie树并插入字符串。
测试搜索和前缀匹配功能。
释放Trie树的内存。
第三题:买卖股票的最佳时机 II
题目描述:
你可以无限次地买卖一只股票,但必须在再次购买前卖出之前的股票。你的目标是最大化总收益。设计一个算法来计算可以获得的最大利润。
示例输入与输出:
输入:[7, 1, 5, 3, 6, 4]
输出:7
解释:在第 2 天(股票价格 = 1)买入,在第 3 天(股票价格 = 5)卖出,交易收益为 5-1 = 4。然后在第 4 天(股票价格 = 3)买入,在第 5 天(股票价格 = 6)卖出,交易收益为 6-3 = 3。总收益为 4 + 3 = 7。
输入:[1, 2, 3, 4, 5]
输出:4
解释:在第 1 天(股票价格 = 1)买入,在第 5 天(股票价格 = 5)卖出,交易收益为 5-1 = 4。总收益为 4。
解题思路:
这道题的关键在于找到所有股价上升的区间,并将这些区间的差值累加起来。具体实现时,我们可以遍历价格数组,如果当前价格比前一天高,就将差值加入总收益。
代码实现:
#include <stdio.h>
int maxProfit(int* prices, int pricesSize) {
int maxProfit = 0;
for (int i = 1; i < pricesSize; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
int main() {
int prices1[] = {7, 1, 5, 3, 6, 4};
int size1 = sizeof(prices1) / sizeof(prices1[0]);
printf("Max Profit: %d\n", maxProfit(prices1, size1)); // Output: 7
int prices2[] = {1, 2, 3, 4, 5};
int size2 = sizeof(prices2) / sizeof(prices2[0]);
printf("Max Profit: %d\n", maxProfit(prices2, size2)); // Output: 4
return 0;
}
深入剖析:
这种贪心算法的时间复杂度为O(n),因为我们只需要遍历一次价格数组。这种方法简单且高效,适用于处理大规模数据。
✨ 这就是今天要分享给大家的全部内容了,我们下期再见!😊
🏠 我在CSDN等你哦!我的主页😍