力扣上记录

c中 const int a; int shuzu[a];不行

当你尝试使用 int shuzu[a]; 这样的声明,并且 a 是一个变量(即使是 const int),

编译器仍然无法在编译时确定 a 的值。尽管 const int 在C语言中是一个常量表达式,

C语言标准并没有规定编译器必须能够在编译时知道所有 const 变量的值

特别是,如果 const int a 是在函数内部声明的,那么它的值通常是在运行时才确定的,因为函数参数和局部变量是在函数调用时初始化的

有几种方法可以绕过这个限制:

  1. 使用宏定义(Macro)
    在C语言中,宏定义是在预处理阶段处理的,因此它们的值在编译时就已经确定了。

    #define SIZE 10
    int shuzu[SIZE];
  2. 使用动态内存分配:(不是在编译之前,所以只能动态创建内存)
    如果你需要在运行时确定数组的大小,可以使用 malloc 或 calloc 等函数动态地分配内存。

    const int a = 10;
    int *shuzu = (int *)malloc(a * sizeof(int));
    // 使用shuzu...
    free(shuzu);
  3. 使用变长数组(Variable Length Arrays, VLA)(C99及以后):
    如果你的编译器支持C99或更高版本,你可以使用变长数组(VLA),它允许在栈上分配大小在运行时确定的数组。(这也解释了力扣为什么有时候可以有时候有不可以

    const int a = 10; // 注意:a的值最好在函数外部定义,以确保在编译时已知
    int shuzu[a]; // 这在C99及以后的标准中是合法的,但a的值最好是在编译时已知的

    然而,即使这样,如果 a 是在函数内部定义的 const int,它仍然可能不是编译时常量,这取决于编译器的具体实现和编译时的上下文。

  4. const int a = 10;
    int shuzu[a]; // 如果a是全局或静态的,这通常是合法的

c++中的vector<int>& a

在C++中,vector<int>& a 是一个引用声明,它表示 a 是对某个已存在的 vector<int> 类型对象的引用。这里有几个关键点需要理解:

  1. vector<int>:这是C++标准模板库(STL)中的一个序列容器,用于存储整数(int)的动态数组。它可以自动调整其大小以存储新元素,并提供了方便的成员函数来访问和操作容器中的元素。

  2. 引用(&:在C++中,引用是变量的别名。当你声明一个引用时,它必须被初始化为另一个同类型的对象。引用始终引用同一对象,不能重新绑定。引用的主要用途是作为函数参数(可以避免复制大型对象),以及作为函数返回值(返回对局部变量的引用时需要谨慎,因为局部变量在函数返回后可能不再存在)。

  3. vector<int>& a:这个声明表示 a 是一个引用,它引用了某个已经存在的 vector<int> 类型的对象。这意味着,通过 a 对 vector 进行的任何非const操作(如添加、删除元素或修改元素的值)都会反映到被引用的原始 vector 对象上。

#include <vector>  
#include <iostream>  
  
void printVector(const std::vector<int>& vec) { // 使用const引用以避免修改原始vector  
    for (int num : vec) {  
        std::cout << num << " ";  
    }  
    std::cout << std::endl;  
}  
  
void modifyVector(std::vector<int>& vec) { // 移除第一个元素  
    if (!vec.empty()) {  
        vec.erase(vec.begin());  
    }  
}  
  
int main() {  
    std::vector<int> myVector = {1, 2, 3, 4, 5};  
    printVector(myVector); // 输出: 1 2 3 4 5  
    modifyVector(myVector);  
    printVector(myVector); // 输出: 2 3 4 5  
    return 0;  
}

Vector声明

在C++中,vector是标准模板库(STL)中的一个序列容器,它能够存储可变数量的同类型元素,就像一个动态数组。vector提供了比数组更多的功能,比如自动管理内存、动态扩容等。

基本声明

要声明一个vector,你需要包含头文件<vector>,并使用std::vector(或者如果你使用了using namespace std;,则只需vector)加上你想要存储的元素类型以及(可选的)一个初始大小或初始值。

示例
  1. 声明一个空的vector

    #include <vector>
    std::vector<int> myVector; // 空的int类型的vector
  2. 声明一个具有特定初始大小的vector

    std::vector<int> myVector(10); // 包含10个int的vector,每个元素初始化为0
  3. 声明一个并用特定值初始化所有元素的vector

    std::vector<int> myVector(10, 42); // 包含10个int的vector,每个元素初始化为42
  4. 使用初始化列表声明并初始化vector

    std::vector<int> myVector = {1, 2, 3, 4, 5}; // 使用初始化列表
  5. 使用auto关键字和std::vector的构造函数(C++11及以后)

    auto myVector = std::vector<int>{1, 2, 3, 4, 5}; // C++11及以后的特性

注意事项

  • vector的大小增加时,它可能会重新分配其内部存储空间以容纳更多元素,这可能会导致现有元素的迭代器、指针和引用失效。
  • 访问vector的元素时,应使用at()成员函数(它会进行范围检查)或使用[]操作符(不会进行范围检查,速度更快但使用时需要小心)。
  • vector的大小是动态的,可以通过push_back()成员函数添加元素,或者通过pop_back()移除最后一个元素。
  • 可以通过size()成员函数获取vector中当前元素的数量,通过empty()成员函数检查vector是否为空。
  • 可以使用reserve()成员函数预留一定的存储空间,以减少因动态扩容导致的性能开销。
  • 可以通过clear()成员函数移除vector中的所有元素,但保留其容量。如果需要同时减少容量,可以先调用swap()与一个空的vector进行交换,或者简单地重新声明一个vector

c#中的数组传入函数,会修改原来的数组吗

在C#中,当你将一个数组作为参数传递给一个函数时,你实际上传递的是对数组引用的副本,而不是数组本身的副本。函数内部对数组所做的任何非结构性修改(如修改数组元素的值)都会反映到原始数组上。

传的类也是一样

这是因为C#中的引用类型变量存储的是对对象实例的引用(即内存地址),而不是对象本身。当你将引用类型变量作为参数传递给函数时,你实际上传递的是这个引用的副本,它仍然指向同一个对象实例。

public class MyClass  
{  
    public int Value { get; set; }  
}  
  
public class Program  
{  
    public static void ModifyClass(MyClass myClass)  
    {  
        // 修改对象的属性  
        myClass.Value = 100;  
    }  
  
    public static void Main()  
    {  
        MyClass myObject = new MyClass();  
        myObject.Value = 10;  
  
        // 传递对象引用给函数  
        ModifyClass(myObject);  
  
        // 输出修改后的值  
        Console.WriteLine(myObject.Value); // 输出 100,说明原始对象的属性被修改了  
    }  
}

fmax()函数

fmax()函数是C语言(C99及以后版本)中用于比较两个浮点数并返回较大值的一个库函数。这个函数定义在<math.h>头文件中,是标准数学库的一部分。fmax()函数特别适用于处理浮点数类型(如float和double)的比较,确保了在比较过程中能够正确处理浮点数特有的精度问题。

qsort函数参数

  1. void base*
    • 类型void*(空指针)
    • 描述:这个参数是指向待排序数组第一个元素的指针。由于qsort是一个通用的排序函数,可以处理任意类型的数组,因此它使用void*类型来确保可以指向任何类型的数据。
    • 用途:通过这个指针,qsort可以访问数组中的所有元素。
  2. size_t num
    • 类型size_t(无符号整数类型,通常是unsigned int的别名)
    • 描述:这个参数表示数组中元素的个数。
    • 用途qsort函数需要知道数组中有多少个元素来进行排序。
  3. size_t size
    • 类型size_t
    • 描述:这个参数表示数组中每个元素的大小(以字节为单位)。
    • 用途:由于qsort函数处理的是void*类型的指针,它无法直接知道元素的具体大小,因此需要用户显式提供。
  4. int(compar)(const void, const void*)
    • 类型:函数指针,指向一个比较函数
    • 描述:这个参数是一个指向比较函数的指针。比较函数需要接受两个const void*类型的参数(即指向要比较的两个元素的指针),并返回一个整型值来表示这两个元素的大小关系。
    • 用途qsort函数通过调用这个比较函数来确定数组元素的排序顺序。比较函数的返回值决定了元素的排序方式:如果返回值小于0,则第一个元素会被排在第二个元素之前;如果返回值等于0,则两个元素的顺序不变;如果返回值大于0,则第一个元素会被排在第二个元素之后

递归会算进空间复杂度吗

问:每次执行都会定义int a,b,c,d;,然后在继续往里执行,如果一直往里,第10次定义完这四个变量就不再递归,那空间复杂度怎么算,是4*10个int吗

如果递归在达到第10次定义这四个变量后停止,那么调用栈上最多会有10个函数帧(包括初始调用和9次递归调用)。但是,每个函数帧中不仅仅包含这四个整数变量,还包括其他必要的信息(如返回地址、可能的保存寄存器值等),这些额外的信息也会占用空间。然而,为了简化分析,我们通常只关注与递归深度直接相关的部分。

时间不可逆转,但是空间可以,递归的最大深度决定调用栈在递归过程中可能达到的最大大小

是的,递归会算进空间复杂度的计算中,特别是在考虑空间复杂度时,递归调用栈(call stack)的大小是一个重要的因素。

空间复杂度是指算法在运行过程中临时占用存储空间的大小。对于递归算法来说,每次递归调用都会占用一定的栈空间来保存调用信息如局部变量、参数值、返回地址等),直到递归结束。因此,递归的深度(即递归调用的层数)会直接影响空间复杂度。

哈希

在C语言中,使用UT_hash可以方便地实现哈希表(Hash Table)的功能。UT_hash是一个流行的、开源的、用于C语言的哈希表实现,它提供了灵活的API来操作哈希表中的键值对。在你给出的struct hashTable定义中,包含了三个成员:keyvalhh。下面是对这些成员的详细解释:

  1. int key;:这是哈希表中的键(Key)。在哈希表中,每个元素都通过唯一的键来标识,以便能够快速查找、插入或删除元素。在你的例子中,键被定义为一个整型(int),但根据实际需要,键可以是任何类型,只要它在你的应用场景中是唯一的。

  2. int val;:这是与键相关联的值(Value)。在哈希表中,每个键都对应一个值。在你的例子中,值也被定义为一个整型(int),但同样地,这个值可以是任何类型,取决于你的应用需求。

  3. UT_hash_handle hh;:这是UT_hash库特有的成员,用于在哈希表内部管理这个结构体实例你不需要直接操作这个成员,UT_hash库提供的API会负责处理它。这个成员使得结构体实例能够被添加到哈希表中,并在哈希表内部进行管理和组织。

使用UT_hash库时,你通常会定义类似struct hashTable这样的结构体来代表哈希表中的元素。然后,你可以使用UT_hash提供的API函数来创建哈希表、向哈希表中添加元素、从哈希表中查找元素、删除哈希表中的元素等

例如,向哈希表中添加元素时,你可能会使用HASH_ADD,如下所示:

struct hashTable *table = NULL; // 初始化哈希表为空
struct hashTable *s;
s = (struct hashTable*)malloc(sizeof(struct hashTable));
s->key = 1;
s->val = 100;
HASH_ADD_INT(table, hh, s->key, s); // 将s添加到哈希表table中,以s->key作为键

这里,HASH_ADD_INT宏用于向哈希表中添加元素

其中table是哈希表的指针,

hh是结构体中用于哈希表管理的成员名(在这个例子中就是hh),

s->key是键,

s是指向要添加的结构体实例的指针。

注意,键的类型(在这个例子中是intHASH_ADD宏的变体(HASH_ADD_INT相匹配,UT_hash为不同类型的键提供了不同的宏(如HASH_ADD_STR用于字符串键)

总的来说,UT_hash库为C语言开发者提供了一种方便、高效的方式来实现和使用哈希表。

疑解:

struct hashTable* find(int ikey) {

    struct hashTable* tmp;

    HASH_FIND_INT(hashtable, &ikey, tmp);//Find_后面加查找的键类型

    return tmp;

}

 tmp是临时变量,返回时是可以传出里面的值的,这和c#中的闭包并不相同,即在外部还能使用

c中只能返回这个值,如果还想操控这个变量就必须malloc

int trys() {
    int a = 10;
    return a;
}
int main() {
    int* a = trys();
    printf("%d",*a);
}

int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
    hashtable = NULL;
    for (int i = 0; i < numsSize; i++) {
        struct hashTable* it = find(target - nums[i]);
        if (it != NULL) {
            int* ret = malloc(sizeof(int) * 2);
            ret[0] = it->val, ret[1] = i;
            *returnSize = 2;
            return ret;

        }
        insert(nums[i], i);
    }
    *returnSize = 0;
    return NULL;
}

补:API通常由一组函数、协议、数据格式和工具组成,它们被设计为易于使用且能够跨不同的编程语言、操作系统或硬件平台工作。通过API,软件开发者可以重用现有的功能和服务,而无需从头开始编写所有的代码,这大大提高了开发效率和软件的可维护性。 该哈希函数即为API。

贪心算法

最优子结构:规模较大的问题的解由规模较小的子问题的解组成,规模较大的问题的解只由其中一个规模较小的子问题的解决定;
无后效性:后面阶段的求解不会修改前面阶段已经计算好的结果;
贪心选择性质:从局部最优解可以得到全局最优解。

动态规划

动态规划(Dynamic Programming, DP)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。其核心思想是将大问题分解成小问题,通过解决小问题来逐步解决大问题,同时存储已解决的小问题的答案,以避免重复计算,从而提高效率。

简单来说,动态规划就是“记住”已经解决过的子问题的答案,从而避免重复计算,通过“分而治之”和“记忆化”来优化算法性能。这种方法广泛应用于求解最优化问题,如最短路径问题、背包问题、最长公共子序列问题、项目安排问题等。

 sprintf函数

C标准库中用于格式化输出的函数之一,它可以将格式化的数据写入字符串中。这是将数字转换为字符串的一个非常直接的方法。

#include <stdio.h>  
  
int main() {  
    int number = 12345;  
    char str[20]; // 确保有足够的空间来存储转换后的字符串和终止符'\0'  
  
    // 使用sprintf将整数转换为字符串  
    sprintf(str, "%d", number);  
  
    // 输出转换后的字符串  
    printf("The number is: %s\n", str);  
  
    return 0;  
}

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值