├── .gitignore ├── .gitmodules ├── cplusplus ├── basic │ ├── bit.cpp │ ├── constpoint.cpp │ ├── enum.cpp │ ├── for.cpp │ ├── function.cpp │ ├── function_overload.cpp │ ├── input.cpp │ ├── logic.cpp │ ├── mutable.cpp │ ├── new.cpp │ ├── rand.cpp │ ├── reference.cpp │ ├── size_t.cc │ ├── smart_point_shared.cpp │ ├── smart_point_unique.cpp │ └── smart_point_weak.cpp ├── class │ ├── class.cpp │ ├── explicit.cpp │ ├── friend.cpp │ ├── inherit.cpp │ ├── inherit.hpp │ ├── inherit.jpg │ ├── memberlocate.cpp │ ├── operatorOverload.cpp │ ├── staticfunc.cpp │ ├── thisPoint.cpp │ ├── typetransform.cpp │ └── virtualfun.cpp ├── design │ └── register │ │ ├── README.md │ │ ├── main │ │ ├── mk.sh │ │ └── src │ │ ├── Convolution.cpp │ │ ├── OpConverter.cpp │ │ ├── OpConverter.hpp │ │ ├── Pool.cpp │ │ └── main.cpp ├── extern_C │ └── c_call_cpp │ │ ├── cpp_class.cpp │ │ ├── cpp_class.h │ │ ├── cpp_class_wrapper.cpp │ │ ├── cpp_class_wrapper.h │ │ ├── main.c │ │ └── mk.sh └── stl │ ├── deque.cpp │ ├── hash_map.cpp │ ├── heap.cpp │ ├── iterator.cpp │ ├── pair.cpp │ ├── queue.cpp │ ├── stack.cpp │ ├── string.cpp │ └── vector.cpp ├── cuda └── transpose │ ├── main.cu │ ├── mk.sh │ └── run.sh ├── flatbuffers ├── mk.sh ├── monster_read.cpp ├── monster_write.cpp ├── net_read.cpp └── net_write.cpp ├── leetcode ├── search.cpp ├── sort.cpp ├── sword │ ├── 二叉树-二叉搜索树与双向链表.cpp │ ├── 二叉树-二叉搜索树的后序遍历序列.cpp │ ├── 二叉树-二叉搜索树的最近公共祖先.cpp │ ├── 二叉树-二叉搜索树的第k个结点.cpp │ ├── 二叉树-二叉树中和为某一值的路径.cpp │ ├── 二叉树-二叉树的下一个结点.cpp │ ├── 二叉树-二叉树的最近公共祖先.cpp │ ├── 二叉树-二叉树的深度.cpp │ ├── 二叉树-二叉树的镜像.cpp │ ├── 二叉树-二叉树遍历.cpp │ ├── 二叉树-从上往下打印二叉树.cpp │ ├── 二叉树-对称的二叉树.cpp │ ├── 二叉树-平衡二叉树.cpp │ ├── 二叉树-序列化二叉树.cpp │ ├── 二叉树-把二叉树打印成多行.cpp │ ├── 二叉树-按之字形顺序打印二叉树.cpp │ ├── 二叉树-树的子结构.cpp │ ├── 二叉树-重建二叉树.cpp │ ├── 位运算-不用加减乘除做加法.cpp │ ├── 位运算-二进制中1的个数.cpp │ ├── 堆栈-包含min函数的栈.cpp │ ├── 堆栈-栈的压入弹出序列.cpp │ ├── 堆栈-用两个栈实现队列.cpp │ ├── 堆栈-队列的最大值.cpp │ ├── 字符串-字符串的排列.cpp │ ├── 字符串-字符流中第一个不重复的字符.cpp │ ├── 字符串-左旋转字符串.cpp │ ├── 字符串-把字符串转换成整数.cpp │ ├── 字符串-把数字翻译成字符串.cpp │ ├── 字符串-替换空格.cpp │ ├── 字符串-最长不含重复字符的子字符串.cpp │ ├── 字符串-正则表达式匹配.cpp │ ├── 字符串-第一个只出现一次的字符.cpp │ ├── 字符串-翻转单词顺序列.cpp │ ├── 字符串-表示数值的字符串.cpp │ ├── 数组-0到n-1中缺失的数字.cpp │ ├── 数组-1到n整数中1出现的次数.cpp │ ├── 数组-n个骰子的点数.cpp │ ├── 数组-丑数.cpp │ ├── 数组-二维数组中的查找.cpp │ ├── 数组-二维数组中的查找.py │ ├── 数组-和为S的两个数字.cpp │ ├── 数组-和为S的连续正数序列.cpp │ ├── 数组-扑克牌顺子.cpp │ ├── 数组-打印从1到最大的n位数.cpp │ ├── 数组-把数组排成最小的数.cpp │ ├── 数组-数字在排序数组中出现的次数.cpp │ ├── 数组-数字序列中某一位的数字.cpp │ ├── 数组-数据流中的中位数.cpp │ ├── 数组-数组中出现次数超过一半的数字.cpp │ ├── 数组-数组中数字出现的次数I.cpp │ ├── 数组-数组中数字出现的次数II.cpp │ ├── 数组-数组中的逆序对.cpp │ ├── 数组-数组中重复的数字.cpp │ ├── 数组-斐波那契数列.cpp │ ├── 数组-旋转数组的最小数字.cpp │ ├── 数组-最小的K个数.cpp │ ├── 数组-最长递增子数组长度.cpp │ ├── 数组-构建乘积数组.cpp │ ├── 数组-滑动窗口的最大值.cpp │ ├── 数组-调整数组顺序使奇数在偶数前.cpp │ ├── 数组-连续子数组的最大和.cpp │ ├── 数组-青蛙跳台阶.cpp │ ├── 数组-顺时针打印矩阵.cpp │ ├── 杂-剪绳子I.cpp │ ├── 杂-剪绳子II.cpp │ ├── 杂-孩子们的游戏(圆圈中最后剩下的数).cpp │ ├── 杂-数值的整数次方.cpp │ ├── 杂-求1+2+3+...+n.cpp │ ├── 棋盘-机器人的运动范围.cpp │ ├── 棋盘-矩阵中的路径.cpp │ ├── 棋盘-礼物的最大价值.cpp │ ├── 链表-两个链表的第一个公共结点.cpp │ ├── 链表-从尾到头打印链表.cpp │ ├── 链表-删除链表中重复的结点.cpp │ ├── 链表-删除链表的节点.cpp │ ├── 链表-反转链表.cpp │ ├── 链表-合并链表.cpp │ ├── 链表-复杂链表的复制.cpp │ ├── 链表-链表中倒数第k个节点.cpp │ └── 链表-链表中环的入口节点.cpp ├── 二叉树-不同的二叉搜索树.cpp ├── 二叉树-二叉树展开为链表.cpp ├── 二叉树-二叉树的直径.cpp ├── 二叉树-合并二叉树.cpp ├── 二叉树-实现前缀树.cpp ├── 二叉树-打家劫舍III.cpp ├── 二叉树-把二叉搜索树转换为累加树.cpp ├── 二叉树-路径总和III.cpp ├── 二叉树-验证二叉搜索树.cpp ├── 位运算-比特位计数.cpp ├── 位运算-汉明距离.cpp ├── 图-课程表.cpp ├── 图-除法求值.cpp ├── 字符串-单词拆分.cpp ├── 字符串-回文子串.cpp ├── 字符串-字母异位词分组.cpp ├── 字符串-字符串解码.cpp ├── 字符串-找到字符串中所有字母异位词.cpp ├── 字符串-括号的生成.cpp ├── 字符串-无重复字符的最长子串.cpp ├── 字符串-最长回文子串.cpp ├── 字符串-有效的括号.cpp ├── 字符串-电话号码的字母组合.cpp ├── 数组-三数之和.cpp ├── 数组-下一个排列.cpp ├── 数组-两数之和.cpp ├── 数组-乘积最大子数组.cpp ├── 数组-买卖股票的最佳时机.cpp ├── 数组-任务调度器.cpp ├── 数组-全排列.cpp ├── 数组-分割等和子集.cpp ├── 数组-前K个高频元素.cpp ├── 数组-合并两个有序数组.cpp ├── 数组-合并区间.cpp ├── 数组-和为K的子数组.cpp ├── 数组-在排序数组中查找元素的第一个和最后一个位置.cpp ├── 数组-子集.cpp ├── 数组-完全平方数.cpp ├── 数组-寻找两个正序数组的中位数.cpp ├── 数组-寻找重复数.cpp ├── 数组-打家劫舍.md ├── 数组-找到数组中消失的数字.cpp ├── 数组-插入区间.cpp ├── 数组-搜索旋转排序数组.cpp ├── 数组-数组中的第K个最大元素.cpp ├── 数组-最佳买卖股票时机含冷冻期.cpp ├── 数组-最短无序连续子数组.cpp ├── 数组-最长上升子序列.cpp ├── 数组-最长连续序列.cpp ├── 数组-根据身高重建队列.cpp ├── 数组-每日温度.cpp ├── 数组-爬楼梯.cpp ├── 数组-盛最多水的容器.cpp ├── 数组-目标和.cpp ├── 数组-移动零.cpp ├── 数组-组合总和.cpp ├── 数组-跳跃游戏.cpp ├── 数组-除自身以外数组的乘积.cpp ├── 数组-零钱兑换.cpp ├── 数组-颜色分类.cpp ├── 棋盘-不同路径.cpp ├── 棋盘-单词搜索.cpp ├── 棋盘-岛屿周长.cpp ├── 棋盘-岛屿数量.cpp ├── 棋盘-岛屿的最大面积.cpp ├── 棋盘-旋转图像.cpp ├── 棋盘-最大人工岛.cpp ├── 棋盘-最大正方形.cpp ├── 棋盘-最小路径和.cpp ├── 链表-LRU机制.cpp ├── 链表-两数相加.cpp ├── 链表-删除链表的倒数第N个节点.cpp ├── 链表-反转链表II.cpp ├── 链表-回文链表.cpp ├── 链表-排序链表.cpp └── 链表-重排链表.cpp ├── nc4hw4 ├── gemm_c4_neon.c ├── gemm_neon.s ├── main.c └── mk.sh ├── thread_process ├── pthread │ ├── create.c │ ├── event.c │ ├── exit.c │ ├── join.c │ ├── mutex.c │ └── ringbuffer.c └── python │ ├── lock.py │ ├── manager.py │ ├── pipe.py │ ├── pool.py │ ├── process.py │ ├── queue.py │ ├── shared_memory.py │ ├── thread.py │ └── value.py └── winograd ├── README.md ├── mk.sh ├── run.sh └── src ├── common.c ├── common.h ├── conv0.c ├── conv0.h ├── generate.cpp ├── main.c ├── winograd1.c ├── winograd1.h ├── winograd1_sample.c ├── winograd2.c ├── winograd2.h ├── winograd3.c ├── winograd3.h ├── winograd4.c ├── winograd4.h ├── winograd4_sample.c ├── winograd5.c ├── winograd5.h └── winograd5_sample.c /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | Debug/ 3 | .DS_Store 4 | *pyc 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "flatbuffers/flatbuffers"] 2 | path = flatbuffers/flatbuffers 3 | url = https://github.com/google/flatbuffers.git 4 | -------------------------------------------------------------------------------- /cplusplus/basic/bit.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | using namespace std; 4 | 5 | int main() 6 | { 7 | unsigned int a=1; 8 | unsigned int b=2; 9 | unsigned int c=0; 10 | //按位与 11 | c = (a&b); 12 | //按位或 13 | c = (a|b); 14 | //按位异或 15 | c = (a^b); 16 | 17 | //左移,非循环 18 | a<<=1; 19 | 20 | //把一个数打印16进制表示 21 | int num1=-1; 22 | cout<(device2::cpu);//OK 31 | device2 col = static_cast(0);//OK 32 | -------------------------------------------------------------------------------- /cplusplus/basic/for.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // https://blog.csdn.net/soulwyb/article/details/100876414 7 | 8 | int main(int argc, char const* argv[]) { 9 | vector list(10, 0); 10 | 11 | // 1.普通的for循环 12 | for (int n = 0; n < 50; ++n) 13 | std::cout << n << '\n'; 14 | // 两个索引数 15 | for (int i = 0, j = 1; i < j; i++, j++) { 16 | i++; 17 | } 18 | // 2.用于容器的for循环 19 | for (auto it = list.begin(); it != list.end(); ++it) 20 | cout << *it << '\n'; 21 | 22 | // 3. 23 | 24 | // 4.升级版简易for循环 25 | for (auto item : list) 26 | cout << item << '\n'; 27 | 28 | // 5. 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /cplusplus/basic/function.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 1.有一个参数带默认值,则后面的所有参数都必须带默认值 5 | int add(int a, int b = 1, int c = 2) { 6 | return a + b + c; 7 | } 8 | 9 | // 2.声明和实现只能有一个地方设置默认值 10 | int add2(int a, int b = 1, int c = 2); 11 | int add2(int a, int b, int c) { 12 | return a + b + c; 13 | } 14 | 15 | // 3.只设置类型不设置形参即为占位参数,并可设置默认值 16 | int add3(int a, int, int = 2){ 17 | return a; 18 | } 19 | 20 | int main() { 21 | return 0; 22 | } -------------------------------------------------------------------------------- /cplusplus/basic/function_overload.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 一:函数重载的条件 5 | // 1.同一个作用域 6 | // 2.函数名相同 7 | // 3.参数类型、参数顺序、参数个数不同 8 | // 4.返回值不同不行 9 | int func(int a, float b) 10 | { 11 | std::cout<<"func(int a, float b)"< 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | // //一、以空格为间隔的数组 9 | // // input: 10 | // // 5 11 | // // 1 2 3 4 5 12 | // int n; 13 | // cin >> n; 14 | // int *a = new int[n]; 15 | // for (int i = 0; i < n; i++) 16 | // { 17 | // cin >> a[i]; 18 | // } 19 | 20 | // //二、不定长输入,以空格为间隔的数组 21 | // // input: 22 | // // 1 2 3 4 5 23 | // int number; 24 | // vector v; 25 | // while (1) 26 | // { 27 | // cin >> number; 28 | // v.push_back(number); 29 | // if (cin.get() == '\n') 30 | // break; 31 | // } 32 | 33 | // //三、不定长输入,带空格的,回车结束的字符串 34 | // // input: 35 | // // dfaj djfa. 36 | // string s; 37 | // getline(cin, s); 38 | 39 | //四、数字以逗号间隔 40 | string s; 41 | vector v; 42 | char *ptr; 43 | getline(cin, s); 44 | char *str = new char[s.size()]; //将整个string转成字符串 45 | strcpy(str, s.c_str()); 46 | ptr = strtok(str, ","); 47 | while (ptr != NULL) 48 | { 49 | v.push_back(atoi(ptr)); 50 | ptr = strtok(NULL, ","); 51 | } 52 | 53 | //套一个while t 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /cplusplus/basic/logic.cpp: -------------------------------------------------------------------------------- 1 | // 逻辑运算符的短路性质。 2 | // 以逻辑运算符 && 为例,对于 A && B 这个表达式,如果 A 表达式返回False ,那么 A && B 已经确定为False ,此时不会去执行表达式 B。 3 | // 同理,对于逻辑运算符 ||, 对于 A || B 这个表达式,如果 A 表达式返回 True ,那么 A || B 已经确定为 True ,此时不会去执行表达式 B。 4 | #include 5 | using namespace std; 6 | 7 | bool fun1(){ 8 | cout<<"hi from fun1"< 8 | 9 | class Person { 10 | public: 11 | Person(); 12 | ~Person(); 13 | 14 | int getAge() const; /*调用方法*/ 15 | int getCallingTimes() const; /*获取上面的getAge()方法被调用了多少次*/ 16 | private: 17 | int age; 18 | mutable int m_nums;/*用于统计getAge函数的被调用次数*/ 19 | int m_nums2; 20 | }; 21 | 22 | int Person::getAge() const 23 | { 24 | std::cout << "Calling the method" << std::endl; 25 | m_nums++;//只有mutable可以修改 26 | // m_nums2++;//这个非mutable是无法修改的 27 | return age; 28 | } -------------------------------------------------------------------------------- /cplusplus/basic/new.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main() { 5 | float* p1 = new float(); //申请了一个float,值为0 6 | float* p2 = new float(10); //申请了一个float,值为10 7 | float* p3(new float()); //同p1 8 | float* arr_p2 = new float[10]; //申请了10个float,值为随机 9 | float* arr_p3 = new float[10](); //申请了10个float,值为0 10 | delete p1; 11 | delete[] arr_p2; //删除数组要带[] 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /cplusplus/basic/rand.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // https://blog.csdn.net/cmm0401/article/details/54599083 4 | int main(int argc, char const* argv[]) { 5 | int a = 0; 6 | float b = 0.0; 7 | a = rand(); // 0-RAND_MAX 8 | a = rand() % 100; //[0, 99] 9 | a = rand() % 100 + 1; //[1, 100] 10 | a = (rand() + 5) % 15; //[5, 15] 11 | b = rand() / RAND_MAX; //[0,1] 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /cplusplus/basic/reference.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | // 引用是别名,指针是地址 4 | // 1.指针在运行时可以改变所指向的值,而引用一旦与某个对象绑定后就不再改变。 5 | // 2.内存上看,指针变量分配区域,而不为引用分配内存区域。 6 | // 3.因为引用生命时必须初始化,从而指向一个已经存在的对象,引用不能为空值。 7 | // 4.可以有指针的指针,但是没有引用的引用。效率上一样的 8 | 9 | //值传递 10 | void swap(float a, float b) { 11 | float temp = a; 12 | a = b; 13 | b = temp; 14 | } 15 | //指针做参数 16 | void swap2(float* a, float* b) { 17 | float temp = *a; 18 | *a = *b; 19 | *b = temp; 20 | } 21 | //引用,好处是代码结构与值传递一样 22 | void swap3(float& a, float& b) { 23 | float temp = a; 24 | a = b; 25 | b = temp; 26 | } 27 | 28 | //不要返回局部变量引用 29 | // float& test() 30 | // { 31 | // float a = 10; 32 | // return a; 33 | // } 34 | 35 | // 1.可以返回静态变量引用 36 | // 2.函数的返回是引用,则函数可以作为左值 37 | float& test2() { 38 | static float a = 10; 39 | return a; 40 | } 41 | 42 | void show(const float& a) {//常量引用作为参数,防止函数内误操作导致外面也变了 43 | // a = 1;//修改常量引用会报错 44 | std::cout << a << std::endl; 45 | } 46 | 47 | int main() { 48 | int a = 0; //创建被引用的对象 49 | int& b = a; //引用初始化后指向的对象不可更改 50 | // int &b;//定义时必须初始化 51 | int c = 1; 52 | b = c; //赋值操作,把b和a指向的内存赋值为c,不是更改b的指向 53 | 54 | // int& d = 10;//错误,非常量引用的初始化必须为左值 55 | const int& d = 10; //这样可以,编译器自动添加了一个temp 56 | 57 | float& ref = test2(); 58 | std::cout << ref << std::endl; //获取到静态变量的值 59 | test2() = 100; //函数的返回作为左值被100更改了值 60 | std::cout << ref << std::endl; //静态变量同样也被更改 61 | return 0; 62 | } -------------------------------------------------------------------------------- /cplusplus/basic/size_t.cc: -------------------------------------------------------------------------------- 1 | // 作用 2 | // 1. 用于malloc时设定空间. size_t的取值range是目标平台下最大可能的数组尺寸. 3 | // void *malloc(size_t __size). 最典型的,在x64下,int还是4,但size_t是8 4 | // 2. 用于数组索引. 5 | #include 6 | 7 | int main(int argc, char const *argv[]) 8 | { 9 | // 1. 10 | size_t size = 100; 11 | void* p = malloc(size); 12 | 13 | // 2. 14 | for (size_t i = 0; i < 5; i++) 15 | { 16 | ; 17 | } 18 | 19 | return 0; 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /cplusplus/class/class.cpp: -------------------------------------------------------------------------------- 1 | // 一:属性都设置为private 2 | // 1.将成员属性设置为private后用户自己可以通过开放对应public成员函数接口控制读写权限 3 | // 2.可以控制属性赋值的合法性 4 | 5 | // 二:class和struct的差别就在于默认权限 6 | 7 | // 三:构造和析构函数 8 | // 1.构造和析构都没有有返回值 9 | // 2.构造函数有参数,可以被重载 10 | // 3.析构函数没有参数,不能被重载 11 | 12 | // 四:构造函数分类和调用方法 13 | // 1.括号法调用构造函数 14 | // 2.显式法调用构造函数 15 | // 3.隐式转换法 16 | 17 | // 五:拷贝构造函数的使用场景 18 | // 1.手动调用拷贝构造函数,用一个实例去初始化另一个实例 19 | // 2.值传递方式调用函数 20 | // 3.以值传递方式返回 21 | 22 | // 六:构造函数 23 | // 1.系统默认给类提供默认构造函数、默认拷贝构造函数、默认析构函数 24 | // 2.用户提供有参构造函数后则系统不再提供默认构造函数,但是仍提供拷贝构造 25 | // 3.用户提供拷贝构造函数后则系统不再提供任何其他默认函数 26 | 27 | // 七:拷贝构造函数的深浅拷贝 28 | // 系统提供的默认拷贝构造函数为浅拷贝,即对于属性直接逐字节拷贝,如果是指针属性则当实例1释放之后 29 | // 实例2的指针属性会指向异常 30 | 31 | // 八:用初始化列表构造函数初始化属性 32 | 33 | // 九:一个类A实例作为另外一个类B的属性,先构造A,再构造B,先析构B,再析构A 34 | 35 | class Person { 36 | int m_A; // class成员的默认权限是private,struct成员的默认权限是public 37 | public: //类内类外(类和实例对象)都可以被访问 38 | Person(){ 39 | //无参构造函数,即默认构造函数 40 | 41 | }; 42 | Person(int a){ 43 | //有参普通构造函数 44 | 45 | }; 46 | Person(int b, int c) : m_B(b), m_C(c) { 47 | //初始化列表构造函数 48 | }; 49 | Person(const Person& person){ 50 | //有参拷贝构造函数 51 | 52 | }; 53 | ~Person(){ 54 | 55 | }; 56 | 57 | void set_B(int b) { //在public一般会设置关于属性m_B的读写接口 58 | if (b < 10) //还能控制属性设置的合法性 59 | m_B = b; 60 | } 61 | int get_B() { return m_B; } 62 | 63 | protected: //类内可以被访问,实例对象不可以访问 64 | private: //类内可以被访问,实例对象不可以访问 65 | int m_B; //属性一般都放在private里,实例不能直接访问 66 | int m_C; 67 | }; 68 | 69 | int main() { 70 | // 1.括号法调用构造函数 71 | Person p1; //默认构造函数 72 | Person p2(1); //有参普通构造函数 73 | Person p3(p1); //拷贝构造函数 74 | // 注意事项 75 | // 调用默认构造函数时不要加括号,否则会被认为是函数声明 76 | 77 | // 2.显式法调用构造函数 78 | Person p4; //默认构造函数 79 | Person p5 = Person(1); //有参普通构造函数 80 | Person p6 = Person(p4); //拷贝构造函数 81 | Person(1); //匿名对象,当前行运行结束即释放 82 | // 注意事项 83 | // 不要用拷贝构造函数初始化一个匿名对象,编译器会等价Person(p1)=Person 84 | // p1,出现重定义问题 Person(p1) 85 | 86 | // 3.隐式转换法 87 | Person p7 = 1; //相当于 Person p7 = Person(1); 88 | } -------------------------------------------------------------------------------- /cplusplus/class/explicit.cpp: -------------------------------------------------------------------------------- 1 | // explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的 2 | // 类构造函数默认情况下即声明为implicit(隐式) 3 | // explicit关键字只对有一个参数的类构造函数有效或者除了第一个参数以外的其他参数都有默认值 4 | 5 | // https://www.cnblogs.com/rednodel/p/9299251.html 6 | 7 | // [隐式构造可能存在的问题](https://www.shangmayuan.com/a/6b16d01233494022be9eaea3.html) 8 | 9 | // 构造函数分类和调用方法 10 | // 1.括号法调用构造函数 11 | // 2.显式法调用构造函数 12 | // 3.隐式转换法 13 | 14 | class Person { 15 | public: 16 | Person(int a){ 17 | //有参普通构造函数 18 | }; 19 | }; 20 | // 3.隐式转换法 21 | Person p7 = 1; //相当于 Person p7 = Person(1); 22 | 23 | class Person2 { 24 | public: 25 | explicit Person2(int a){ 26 | //有参普通构造函数 27 | }; 28 | }; 29 | // 3.隐式转换法 30 | // Person2 p8 = 1; //报错 -------------------------------------------------------------------------------- /cplusplus/class/friend.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | //友元的作用是为了让一个类的私有属性可以在类外被访问 5 | // 三种实现方式 6 | // 1.全局函数作友元 7 | // 2.类作友元 8 | // 3.成员函数作友元 9 | class goodgay2; 10 | 11 | class Person { 12 | friend void g_fun( 13 | Person& p); //在类里设置该全局函数的声明为友元,可访问该类的私有属性 14 | friend class goodgay; //在类里设置另一个类的声明为友元,可访问该类的私有属性 15 | friend void goodgay2::visit( 16 | Person& p); //在类里设置另一个类的成员函数声明为友元,可访问该类的私有属性 17 | public: 18 | Person() { m_age = 10; } 19 | 20 | private: 21 | int m_age; 22 | }; 23 | 24 | void g_fun(Person& p) { 25 | int a = p.m_age; //一般情况无法在实例中访问private属性 26 | } 27 | 28 | class goodgay { 29 | public: 30 | goodgay() { 31 | m_p = new Person; 32 | int a = m_p->m_age; //在另外一个类里一般无法访问类的私有属性 33 | } 34 | void visit() { 35 | int a = m_p->m_age; //在另外一个类里一般无法访问类的私有属性 36 | } 37 | void visit2(Person& p) { 38 | int a = p.m_age; //在另外一个类里一般无法访问类的私有属性 39 | } 40 | 41 | private: 42 | Person* m_p; 43 | }; 44 | 45 | class goodgay2 { 46 | public: 47 | void visit(Person& p) { 48 | int a = p.m_age; //在另外一个类的成员函数里一般无法访问类的私有属性 49 | } 50 | }; 51 | 52 | int main() { 53 | Person p; 54 | g_fun(p); 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /cplusplus/class/inherit.cpp: -------------------------------------------------------------------------------- 1 | #include "inherit.hpp" 2 | 3 | father::father(/* args */) {} 4 | 5 | father::~father() {} 6 | 7 | son::son(/* args */) {} 8 | 9 | son::~son() {} 10 | 11 | int main() { 12 | father father; 13 | // 类对象只能访问本身的public成员 14 | int a = father.father_public_param; 15 | int b = father.father_public_fun(); 16 | // int c=father.father_protected_param; 17 | // int c=father.father_private_param; 18 | son son1; 19 | //子类对象只能访问父类public成员,即权限和父类对象一样 20 | int c = son1.father_public_param; 21 | int d = son1.father_public_fun(); 22 | } 23 | -------------------------------------------------------------------------------- /cplusplus/class/inherit.hpp: -------------------------------------------------------------------------------- 1 | class father { 2 | public: 3 | father(/* args */); 4 | ~father(); 5 | int father_public_param; 6 | // 类内部成员函数可以访问任意成员 7 | int father_public_fun() { 8 | int a = father_public_param; 9 | int b = father_protected_param; 10 | int c = father_protected_fun(); 11 | int d = father_private_param; 12 | int e = father_private_fun(); 13 | return 0; 14 | } 15 | 16 | protected: 17 | int father_protected_param; 18 | int father_protected_fun() { return 0; } 19 | 20 | private: 21 | int father_private_param; 22 | int father_private_fun() { return 0; } 23 | }; 24 | 25 | class son : public father { 26 | public: 27 | son(/* args */); 28 | ~son(); 29 | // 子类任意类型的成员函数都可以访问负类的public和protected成员 30 | int son_public_fun() { 31 | int a = father_public_param; 32 | int b = father_public_fun(); 33 | int c = father_protected_param; 34 | int d = father_protected_fun(); 35 | return 0; 36 | } 37 | 38 | protected: 39 | // 子类任意类型的成员函数都可以访问负类的public和protected成员 40 | int son_protected_fun() { 41 | int a = father_public_param; 42 | int b = father_public_fun(); 43 | int c = father_protected_param; 44 | int d = father_protected_fun(); 45 | return 0; 46 | } 47 | 48 | private: 49 | /* data */ 50 | }; 51 | 52 | 53 | -------------------------------------------------------------------------------- /cplusplus/class/inherit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuangShiqing/LearnAndTry/ab49cb1944b3b7146795fe2028a7317a003d4c29/cplusplus/class/inherit.jpg -------------------------------------------------------------------------------- /cplusplus/class/memberlocate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | //成员变量和成员函数是分开存储的 4 | 5 | class Person 6 | { 7 | 8 | }; 9 | 10 | class Person2 11 | { 12 | int mA;//每个实例都会安排一个位置放非静态成员变量mA 13 | static int mB;//静态成员变量不占实例的空间 14 | void fun(){};//成员函数不占实例的空间,非静态的也不占 15 | static void fun2(){};//成员函数不占实例的空间 16 | }; 17 | 18 | void test() 19 | { 20 | Person p;//空对象 21 | std::cout<m_age+p1.m_age; 12 | return p3; 13 | }; 14 | //3.重载函数也可以发生函数重载 15 | Person operator+(int a){ 16 | Person p3; 17 | p3.m_age = this->m_age+a; 18 | return p3; 19 | }; 20 | int m_age; 21 | }; 22 | 23 | //2.全局函数实现操作符重载 24 | // Person operator+(Person& p1, Person& p2) 25 | // { 26 | // Person p3; 27 | // p3.m_age = p1.m_age+p2.m_age; 28 | // return p3; 29 | // } 30 | 31 | 32 | 33 | int main() 34 | { 35 | Person p1; 36 | p1.m_age = 10; 37 | Person p2; 38 | p2.m_age = 20; 39 | 40 | Person p3 = p1 + p2; 41 | return 0; 42 | } -------------------------------------------------------------------------------- /cplusplus/class/staticfunc.cpp: -------------------------------------------------------------------------------- 1 | // 一:静态成员变量 2 | // 1.所有实例享有同一份数据 3 | // 2.在编译阶段分配内存空间 4 | // 3.类内声明,类外初始化 5 | // 4.静态成员可以通过类名访问 6 | 7 | // 二:静态成员函数 8 | // 1.静态成员函数,所有实例享有同一份 9 | // 2.静态成员函数只能访问静态成员变量 10 | // 3.静态成员可以通过类名访问 11 | class Person 12 | { 13 | public: 14 | static int mA;// 15 | const static int mB = 1;//只有const static才能在类内初始化 16 | int mC; 17 | static void fun() 18 | { 19 | mA = 1; 20 | // mC = 1;//静态成员函数只能访问静态成员变量 21 | } 22 | void fun2() 23 | {} 24 | 25 | }; 26 | 27 | int main() 28 | { 29 | Person p; 30 | p.fun();//一般的访问方法 31 | Person::fun();//静态成员可以通过类名访问 32 | Person::mA = 1; 33 | // Person::fun2();非静态成员函数不行 34 | } -------------------------------------------------------------------------------- /cplusplus/class/thisPoint.cpp: -------------------------------------------------------------------------------- 1 | // this指针指向实例 2 | // this指针的作用: 3 | // 1.形参和成员变量同名时做区分 4 | // 2.返回实例本身 5 | 6 | #include 7 | using namespace std; 8 | 9 | class Person{ 10 | public: 11 | void fun(int age) 12 | { 13 | // age=age; 14 | this->age = age;//this指针指向实例,可用于区分同名形参和成员变量,本质上属性的访问 15 | //都是省略了this 16 | } 17 | 18 | Person& fun2(){ 19 | return *this;//返回实例本身 20 | } 21 | 22 | void fun3(){ 23 | std::cout<<"hello"<fun3();//指向空的类指针调用成员函数是可行的,前提是函数内没有隐含的this指针 32 | return 0; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /cplusplus/class/typetransform.cpp: -------------------------------------------------------------------------------- 1 | // 只有子类转换成父类,父类不可能转换成子类,因为子类是从父类继承而来, 2 | // 子类中包含父类中所有成员。而如果父类可以转换成子类,意味着将子类中将有一部分是未知的成员,会出现不合法的内存访问 3 | // 在子类转换成父类的时候,也需要注意,只能是指针或者引用,不能是对象,除非当子类B以public方式继承父类A后,或者有重写构造函数和赋值函数。 4 | 5 | class father { 6 | private: 7 | /* data */ 8 | public: 9 | father(/* args */); 10 | ~father(); 11 | }; 12 | 13 | father::father(/* args */) {} 14 | 15 | father::~father() {} 16 | 17 | class son : public father { 18 | private: 19 | /* data */ 20 | public: 21 | son(/* args */); 22 | ~son(); 23 | }; 24 | 25 | son::son(/* args */) {} 26 | 27 | son::~son() {} 28 | 29 | int main() { 30 | // 子类的对象可以隐含的转换为父类的对象 31 | // 子类对象可以初始化积累的引用 32 | // 子类的指针可以隐含的转换为父类的指针 33 | son son; 34 | father* p_father = &son; 35 | 36 | return 0; 37 | 38 | } -------------------------------------------------------------------------------- /cplusplus/class/virtualfun.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | // 一、虚函数 4 | // 目的: 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 5 | // 实现: 虚函数的实现是在类的对象空间中包含一个隐藏的虚函数表, 这个虚函数表的本质就是一个 6 | // 函数指针数组, 这个数组按序存放着这个类对象的多个虚函数实现的真实地址 7 | 8 | // 包含虚函数指针和虚函数表, 指针就是函数指针, 表就是数组. 虚函数表是每个类维护一份, 虚函数指针是每个对象维护一份. 9 | // 因为这个父类指针可以指向子类, 也可能指向父类, 这样只要修改虚函数指针指向父类的虚函数表即可 10 | class father { 11 | private: 12 | /* data */ 13 | public: 14 | father(/* args */); 15 | ~father(); 16 | //父类中定义虚函数 17 | virtual void father_virtual_fun() { std::cout << "father" << std::endl; } 18 | }; 19 | 20 | father::father(/* args */) {} 21 | 22 | father::~father() {} 23 | 24 | class son : public father { 25 | private: 26 | /* data */ 27 | public: 28 | son(/* args */); 29 | ~son(); 30 | //子类中重写父类的虚函数,virtual可不加,但是为了明显就加上 31 | virtual void father_virtual_fun() { std::cout << "son" << std::endl; } 32 | }; 33 | 34 | son::son(/* args */) {} 35 | 36 | son::~son() {} 37 | 38 | int main() { 39 | //用父类指针指向子类 40 | son son; 41 | father* p_father = &son; 42 | //打印的是son,证明调用的是子类的方法 43 | p_father->father_virtual_fun(); 44 | return 0; 45 | } 46 | 47 | // 二、纯虚函数 48 | // 目的: 定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数 49 | // 称带有纯虚函数的类为抽象类。不能创建类的实例,只能创建它的派生类的实例。 50 | class Animal { 51 | private: 52 | /* data */ 53 | public: 54 | // 表明派生类必须有一个参数为weigth的eat实现 55 | virtual void eat(int weight) = 0; 56 | }; -------------------------------------------------------------------------------- /cplusplus/design/register/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | ## Target 3 | 目的是实现一个管理大量Op的Conver(推理也同理)代码的注册机制. 这类代码的特点是一个Op的种类量大, 另外一个是不定期会增加Op 4 | 5 | ## Implementation 6 | 1. 首先实现一个基础OpConverter作为父类, 包含一个virtual函数run作为默认Convert处理. 每种具体的Op都继承于该父类实现一个子类, 比如PoolConverter, 重点是需要重新实现自己的run. 7 | 2. 然后再实现一个OpConverterSuit作为管理者, 包含一个静态成员变量OpConverterSuit* global, 以及一个静态成员函数get用于获得这个global. 另外还包含了一个成员变量std::map mTests用于记录Op的字符名和具体的OpConverter(这里用父类指针)的映射关系. 当然还包含操作这个map的insert和search方法 8 | 3. 每种具体的Op都需要调用insert. 这里利用一个OpConverterRegister类的构造函数和全局变量的自动初始化实现了一个不需要逐个手动显式调用insert的自动过程. 9 | 4. 使用时直接通过静态成员函数get获得global, 然后去search即可根据字符名获得对应的子类(都是用父类指针)方法run 10 | 11 | ## Quick Start 12 | ```bash 13 | bash mk.sh 14 | ./main 15 | ``` -------------------------------------------------------------------------------- /cplusplus/design/register/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuangShiqing/LearnAndTry/ab49cb1944b3b7146795fe2028a7317a003d4c29/cplusplus/design/register/main -------------------------------------------------------------------------------- /cplusplus/design/register/mk.sh: -------------------------------------------------------------------------------- 1 | DIR="./Debug/" 2 | SRC="./src/" 3 | if [ ! -d "${DIR}" ]; then 4 | mkdir "${DIR}" 5 | fi 6 | 7 | g++ "${SRC}"Convolution.cpp -c -o "${DIR}"Convolution.o 8 | g++ "${SRC}"Pool.cpp -c -o "${DIR}"Pool.o 9 | g++ "${SRC}"OpConverter.cpp -c -o "${DIR}"OpConverter.o 10 | g++ "${SRC}"main.cpp "${DIR}"Convolution.o "${DIR}"Pool.o "${DIR}"OpConverter.o -o main -------------------------------------------------------------------------------- /cplusplus/design/register/src/Convolution.cpp: -------------------------------------------------------------------------------- 1 | #include "OpConverter.hpp" 2 | using namespace std; 3 | 4 | class ConvolutionConverter : public OpConverter { 5 | public: 6 | // virtual void run(MNN::OpT* dstOp, const caffe::LayerParameter& parameters, const caffe::LayerParameter& weight) { 7 | // ...... 8 | // } 9 | ConvolutionConverter() { 10 | } 11 | virtual ~ConvolutionConverter() { 12 | } 13 | // virtual MNN::OpType opType() { 14 | // return MNN::OpType_Convolution; 15 | // } 16 | // virtual MNN::OpParameter type() { 17 | // return MNN::OpParameter_Convolution2D; 18 | // } 19 | }; 20 | 21 | static OpConverterRegister a("Convolution"); -------------------------------------------------------------------------------- /cplusplus/design/register/src/OpConverter.cpp: -------------------------------------------------------------------------------- 1 | #include "OpConverter.hpp" 2 | 3 | OpConverterSuit* OpConverterSuit::global = nullptr; 4 | OpConverter* OpConverterSuit::search(const std::string& name) { 5 | auto iter = mTests.find(name); 6 | if (iter == mTests.end()) { 7 | std::cout<<"can not find"<second; 15 | } 16 | 17 | OpConverterSuit* OpConverterSuit::get() { 18 | if (global == nullptr) 19 | global = new OpConverterSuit; 20 | return global; 21 | } 22 | 23 | OpConverterSuit::~OpConverterSuit() { 24 | for (auto& iter : mTests) { 25 | delete iter.second; 26 | } 27 | mTests.clear(); 28 | } 29 | 30 | void OpConverterSuit::insert(OpConverter* t, const char* name) { 31 | mTests.insert(std::make_pair(name, t)); 32 | } -------------------------------------------------------------------------------- /cplusplus/design/register/src/OpConverter.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | class OpConverter { 6 | friend class OpConverterSuit; 7 | 8 | public: 9 | // virtual void run(MNN::OpT* dstOp, const caffe::LayerParameter& parameters, const caffe::LayerParameter& weight) = 10 | // 0; virtual MNN::OpParameter type() = 0; virtual MNN::OpType opType() = 0; 11 | OpConverter() {} 12 | virtual ~OpConverter() {} 13 | 14 | private: 15 | }; 16 | 17 | class OpConverterSuit { 18 | public: 19 | static OpConverterSuit* get(); 20 | void insert(OpConverter* t, const char* name); 21 | 22 | OpConverter* search(const std::string& name); 23 | 24 | OpConverterSuit() {} 25 | ~OpConverterSuit(); 26 | 27 | private: 28 | static OpConverterSuit* global; 29 | std::map mTests; 30 | }; 31 | 32 | template 33 | class OpConverterRegister { 34 | public: 35 | OpConverterRegister(const char* claim) { 36 | T* test = new T; 37 | OpConverterSuit* ts = OpConverterSuit::get(); 38 | ts->insert(test, claim); 39 | std::cout< a("Pooling"); -------------------------------------------------------------------------------- /cplusplus/design/register/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "OpConverter.hpp" 2 | #include 3 | 4 | int main(int argc, char const *argv[]) 5 | { 6 | std::string target = "Pooling"; 7 | auto creator = OpConverterSuit::get()->search(target); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /cplusplus/extern_C/c_call_cpp/cpp_class.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cpp_class.h" 4 | 5 | CppClass::CppClass(){}; 6 | CppClass::~CppClass(){}; 7 | 8 | void CppClass::fun(){ 9 | std::cout<<"fun"<//CppClass这个类依赖的但是C又不能用的头文件内容都要在这个cpp里include 2 | #include "cpp_class_wrapper.h" 3 | #include "cpp_class.h" 4 | 5 | void* wrapper_fun_init(){ 6 | return new CppClass(); 7 | } 8 | 9 | void wrapper_fun_delete(void* p){ 10 | delete static_cast(p); 11 | } 12 | 13 | void wrapper_fun(void* p){ 14 | static_cast(p)->fun(); 15 | } 16 | void wrapper_fun_a(void* p){ 17 | static_cast(p)->fun(1); 18 | } -------------------------------------------------------------------------------- /cplusplus/extern_C/c_call_cpp/cpp_class_wrapper.h: -------------------------------------------------------------------------------- 1 | // #include //CppClass这个类依赖的但是C又不能用的头文件内容不能在这个h里include 2 | // 设计这个wrapper时函数参数不要用C不能用的内容, 比如vector 3 | #ifdef __cplusplus 4 | extern "C" 5 | { 6 | #endif 7 | 8 | void* wrapper_fun_init(); 9 | void wrapper_fun_delete(void* p); 10 | void wrapper_fun(void* p); 11 | void wrapper_fun_a(void* p); 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif -------------------------------------------------------------------------------- /cplusplus/extern_C/c_call_cpp/main.c: -------------------------------------------------------------------------------- 1 | // https://zhuanlan.zhihu.com/p/31295798 2 | // https://zhuanlan.zhihu.com/p/123269132 3 | // https://blog.csdn.net/fengfengdiandia/article/details/82704375 4 | // [C通过wrapper调用C++库](https://blog.csdn.net/shaosunrise/article/details/81176880) 5 | #include "cpp_class_wrapper.h" 6 | 7 | int main(int argc, char const *argv[]) { 8 | void* p = wrapper_fun_init(); 9 | wrapper_fun(p); 10 | wrapper_fun_a(p); 11 | wrapper_fun_delete(p); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /cplusplus/extern_C/c_call_cpp/mk.sh: -------------------------------------------------------------------------------- 1 | rm -rf build 2 | mkdir build 3 | 4 | CXX="g++" 5 | CXXFLAGS="-std=c++11 -fPIC" 6 | CC="gcc" 7 | CCFLAGS="" 8 | 9 | $CXX $CXXFLAGS cpp_class.cpp -c -o build/cpp_class.o 10 | $CXX $CXXFLAGS cpp_class_wrapper.cpp -c -o build/cpp_class_wrapper.o 11 | $CC $CCFLAGS main.c -c -o build/main.o 12 | 13 | $CXX -shared build/cpp_class.o build/cpp_class_wrapper.o -o build/libcxx.so 14 | $CC build/main.o -o build/main -Lbuild -lcxx -Wl,-rpath=build -------------------------------------------------------------------------------- /cplusplus/stl/deque.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // queue队列只能从尾插入从头弹出, 访问是可以两头访问的 6 | // deque双端队列可以任何操作都可以对头尾进行, 包括访问, 插入, 弹出 7 | int main() 8 | { 9 | //初始化空队列 10 | deque dq; 11 | 12 | //插入 13 | //从尾部插入 14 | dq.push_back(5); 15 | //从头部插入 16 | dq.push_front(1); 17 | 18 | //访问 19 | //访问头 20 | int num = dq.front(); 21 | //访问尾 22 | num = dq.back(); 23 | 24 | //删除 25 | //删除头 26 | dq.pop_front(); 27 | //删除尾 28 | dq.pop_back(); 29 | 30 | // 获得信息 31 | if(dq.empty()) 32 | return 0; 33 | num = dq.size(); 34 | 35 | return 0; 36 | 37 | } -------------------------------------------------------------------------------- /cplusplus/stl/hash_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | int main() { 6 | // 初始化 7 | unordered_map map; 8 | map["a"] = 1; 9 | unordered_map pairs = {{')', '('}, {']', '['}, {'}', '{'}}; 10 | 11 | // 查找 12 | if (map.find("a") == map.end()) 13 | cout << "not found" << endl; 14 | else 15 | cout << "found" << endl; 16 | if (map.count("a")) 17 | cout << "found" << endl; 18 | 19 | // 读取 20 | int num = map["a"]; 21 | 22 | // 遍历 23 | for (auto it = map.begin(); it != map.end(); it++) { 24 | cout<second; 25 | } 26 | 27 | // 根据key删除 28 | map.erase("a"); 29 | return 0; 30 | } -------------------------------------------------------------------------------- /cplusplus/stl/heap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | static bool cmp(pair& m, pair& n) { 9 | return m.second > n.second; 10 | } 11 | 12 | //大小堆的top操作时间复杂度是O(1), push和pop的时间复杂度是O(n) 13 | 14 | int main() 15 | { 16 | vector h_max{3,4,2,7,4,9,5,2,0,3}; 17 | vector h_min{3,4,2,7,4,9,5,2,0,3}; 18 | 19 | //用greater初始化最小堆,[0]是最小的数 20 | make_heap(h_min.begin(),h_min.end(),greater()); 21 | //在数组末尾添加一个元素后重新保证[0]是最小的数 22 | h_min.push_back(5); 23 | push_heap(h_min.begin(),h_min.end(),greater()); 24 | //相当于push_heap的逆操作,把原本最小的数放到数组末尾,将次小的数放到[0] 25 | pop_heap(h_min.begin(),h_min.end(),greater()); 26 | h_min.pop_back(); 27 | 28 | 29 | //用less初始化最大堆,[0]是最大的数 30 | make_heap(h_max.begin(),h_max.end(),less()); 31 | //在数组末尾添加一个元素后重新保证[0]是最大的数 32 | h_max.push_back(15); 33 | push_heap(h_max.begin(),h_max.end(),less()); 34 | //相当于push_heap的逆操作,把原本最大的数放到数组末尾,将次小的数放到[0] 35 | pop_heap(h_max.begin(),h_max.end(),less()); 36 | h_max.pop_back(); 37 | 38 | // 另一种最大堆的方式 39 | priority_queue, less> max_heap; 40 | max_heap.push(3); 41 | max_heap.pop(); 42 | int a = max_heap.top(); 43 | 44 | // 另一种最小堆的方式 45 | priority_queue, greater> min_heap; 46 | min_heap.push(3); 47 | min_heap.pop(); 48 | int a = min_heap.top(); 49 | 50 | // 元素是pair, 自定义比较函数 51 | priority_queue, vector>, decltype(&cmp)> q(cmp); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /cplusplus/stl/iterator.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuangShiqing/LearnAndTry/ab49cb1944b3b7146795fe2028a7317a003d4c29/cplusplus/stl/iterator.cpp -------------------------------------------------------------------------------- /cplusplus/stl/pair.cpp: -------------------------------------------------------------------------------- 1 | #include //pair 2 | #include //tie 3 | #include 4 | using namespace std; 5 | 6 | int main(int argc, char const *argv[]) 7 | { 8 | // 初始化 9 | pair map1; 10 | pair map2(1, "1"); 11 | pair map3(map2); 12 | pair map4 = make_pair(1, "1"); 13 | pair map5 = map4;// 拷贝构造 14 | 15 | // 赋值访问 16 | map1.first = 2; 17 | int a1 = map1.first; 18 | map1.second = "2"; 19 | string a2 = map1.second; 20 | 21 | // tie解包pair 22 | int a3; 23 | string a4; 24 | tie(a3, a4) = map1; 25 | return 0; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /cplusplus/stl/queue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // queue队列只能从尾插入从头弹出, 访问是可以两头访问的 6 | // deque双端队列可以任何操作都可以对头尾进行, 包括访问, 插入, 弹出 7 | int main(){ 8 | // 初始化 9 | queue q; 10 | 11 | // 插入 12 | // 插入尾 13 | q.push(1); 14 | 15 | // 访问 16 | //访问头 17 | int num = q.front(); 18 | //访问尾 19 | num = q.back(); 20 | 21 | // 弹出 22 | // 弹出头 23 | q.pop(); 24 | 25 | // 获得信息 26 | if(q.empty()) 27 | return 0; 28 | num = q.size(); 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /cplusplus/stl/stack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | 6 | int main() 7 | { 8 | //栈的初始化 9 | stack s; 10 | 11 | //从尾部插入 12 | s.push(5); 13 | 14 | //访问栈顶 15 | int num = s.top(); 16 | //top不支持访问空栈 17 | if(s.size()) 18 | num = s.top(); 19 | 20 | //弹出栈顶 21 | s.pop(); 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /cplusplus/stl/string.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | //初始化 10 | string s; //初始化一个空string 对象 11 | string s1("qwer"); //根据字符串初始化 12 | string s2 = "qwer"; //同上 13 | string s3(s1); //s1、s2不是一个对象 14 | string s4 = s1; //同上 15 | string s5(4, 's'); //初始化一个长度为4每个位置放s的string 16 | string s6 = string(4, 's'); //同上 17 | string s7(s1, 2); //将s1从index=2开始到最后都赋值给s7 18 | //用字符串赋值和用string赋值给一个string是不一样的,看s7和s8 19 | char cs[] = "12345"; 20 | string s8(cs, 3); //复制字符串cs的前3个字符到s当中 21 | string s9(s1, 2, 1); //将s1从index=2开始赋值长度为1的区域给s8长度 22 | string s13 = s1.substr(1,2);//start,length 23 | 24 | //访问 25 | char c = s1[1];//string单个访问是一个char类型字符 26 | 27 | // 添加 28 | s9.append(s1); //直接在s9后面添加整个s1 29 | s9 += s1; //同上 30 | s9.append(s1, 2, 1); //在s9后面把s1从index=2开始长度为1的区域添加上 31 | s9 += "ss"; //在尾部添加整个字符串 32 | s9.append("asdf", 3); //把字符串的前3个添加到s9后面 33 | s9.insert(1, "new"); //在index=1前添加字符串"new" 34 | s9.insert(5, "new", 2); //在index=5前添加字符串"new"中的前2个字符 35 | 36 | //删除 37 | s9.pop_back(); //删除最后一个字符 38 | s9.erase(5, 2); //删除从index=5开始的2个字符 39 | s9.erase(4); //删除从index=4开始后的所有字符 40 | s9.erase(s9.begin() + 1); //删除迭代器指向的字符 41 | s9.erase(s9.begin() + 1, s9.end()); //删除迭代器区间内的字符,左闭右开 42 | 43 | // 类型转换 44 | string s10 = to_string(300); //把int数字转成string 45 | string s11 = "10"; //把string转成int数字 46 | int number = stoi(s11); // 47 | string s12 = "1.2"; //把string转成flot数字,略有精度变化 48 | float f = stof(s11); // 49 | 50 | int num = s9[0] - '0';//将string中某个字符转换成int 51 | 52 | char *str = new char[s8.size()];//将整个string转成字符串 53 | strcpy(str, s8.c_str()); 54 | 55 | //查询 56 | int index = s11.find('1'); //查询字符'1'在s11中出现的位置,失败返回-1。也可以查询字符串 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /cplusplus/stl/vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | //初始化 9 | //初始化空vector 10 | vector v; 11 | //初始化大小和容量都为10的空vector. 不能直接用于类成员变量的声明, 要先声明vector v;然后在构造函数里v.resize(10); 12 | vector v1(10); 13 | //初始化大小和容量都为10,值都为1的vector 14 | vector v2(10, 1); 15 | vector v3{1, 2, 3, 4, 5, 6, 7, 8, 9}; 16 | vector v4(v3.begin(), v3.end()); 17 | vector> dp(10, vector(10, 0));//初始化指定大小的二维v 18 | 19 | //访问 20 | int a = v2[0]; 21 | a = v3.front(); 22 | a = v3.back(); 23 | a = *v3.begin(); 24 | a = *(v3.end() - 1); 25 | 26 | //查看属性 27 | int size = v2.size(); 28 | bool is_empty = v2.empty(); 29 | 30 | //插入和移除 31 | //在末尾插入一个数 32 | v2.push_back(3); 33 | v.push_back(2); 34 | //移除最后一个数 35 | v2.pop_back(); 36 | 37 | //移除第一个数 38 | v2.erase(v2.begin()); 39 | //移除第二个数 40 | v2.erase(v2.begin() + 1); 41 | 42 | // int b = v2.insert(v2.begin(),3); 43 | // v2.insert(1); 44 | 45 | //反转vector 46 | //在原本v上进行反转 47 | reverse(v3.begin(),v3.end()); 48 | //不改变原本v,将反转结果拷贝到一个新的同大小的v里 49 | vector v5(v3.size()); 50 | reverse_copy(v3.begin(),v3.end(),v5.begin()); 51 | 52 | //获取内存指针 53 | int* p = v5.data(); 54 | 55 | //交换 56 | swap(v3[1], v3[2]); 57 | iter_swap(v3.begin() + 1, v3.begin() + 2); 58 | 59 | //去重 60 | sort(v1.begin(), v1.end()); 61 | vector::iterator new_end = unique(v1.begin(), v1.end()); 62 | vector v_uniqued(v1.begin(),new_end); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /cuda/transpose/mk.sh: -------------------------------------------------------------------------------- 1 | nvcc main.cu -o main 2 | -------------------------------------------------------------------------------- /cuda/transpose/run.sh: -------------------------------------------------------------------------------- 1 | ./main 2 | -------------------------------------------------------------------------------- /flatbuffers/mk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Author:HuangShiqing 3 | #Time:2021-03-09 17:40:09 4 | #Name:mk.sh 5 | #Description:This is a awesome script. 6 | 7 | # check is flatbuffer installed or not 8 | git submodule init 9 | git submodule update 10 | # if [ ! -d $FLATBUFFER_DIR ]; then 11 | # git clone https://github.com/google/flatbuffers.git 12 | # fi 13 | 14 | ROOT_DIR=$(pwd) 15 | FLATBUFFER_DIR=${ROOT}/flatbuffers 16 | 17 | FLATC=${FLATBUFFER_DIR}/build/flatc 18 | if [ ! -e $FLATC ]; then 19 | echo "*** building flatc ***" 20 | cd ${FLATBUFFER_DIR} 21 | # make tmp dir 22 | [ ! -d build ] && mkdir build 23 | cd build && rm -rf * 24 | 25 | # build 26 | cmake .. && cmake --build . --target flatc -- -j4 27 | fi 28 | 29 | cd ${ROOT_DIR} 30 | ${FLATC} monster.fbs --cpp 31 | ${FLATC} net.fbs --cpp --binary --reflect-names --gen-object-api --gen-compare 32 | 33 | g++ monster_write.cpp -o monster_write -I ./flatbuffers/include/ -std=c++11 34 | g++ monster_read.cpp -o monster_read -I ./flatbuffers/include/ -std=c++11 35 | 36 | g++ net_write.cpp -o net_write -I ./flatbuffers/include/ -std=c++11 -O0 -g 37 | g++ net_read.cpp -o net_read -I ./flatbuffers/include/ -std=c++11 -O0 -g 38 | -------------------------------------------------------------------------------- /flatbuffers/monster_read.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "monster_generated.h" // This was generated by `flatc`. 4 | using namespace MyGame::Sample; // Specified in the schema. 5 | 6 | int main() 7 | { 8 | std::ifstream infile; 9 | infile.open("monster.mnn", std::ios::binary | std::ios::in); 10 | infile.seekg(0, std::ios::end); 11 | int length = infile.tellg(); 12 | infile.seekg(0, std::ios::beg); 13 | char *buffer_pointer = new char[length]; 14 | infile.read(buffer_pointer, length); 15 | infile.close(); 16 | 17 | // uint8_t *buffer_pointer = data;/* the data you just read */ 18 | // Get a pointer to the root object inside the buffer. 19 | auto monster = GetMonster(buffer_pointer); 20 | 21 | // scaler 22 | auto hp = monster->hp(); 23 | auto mana = monster->mana(); 24 | std::cout << "hp: " << hp << std::endl; 25 | std::cout << "mana: " << mana << std::endl; 26 | // string 27 | auto name = monster->name()->c_str(); 28 | std::cout << "name: " << name << std::endl; 29 | // struct 30 | auto pos = monster->pos(); 31 | auto x = pos->x(); 32 | auto y = pos->y(); 33 | auto z = pos->z(); 34 | std::cout << "x,y,z: " << x << y << z << std::endl; 35 | // vector 36 | auto inv = monster->inventory(); // A pointer to a `flatbuffers::Vector<>`. 37 | auto inv_len = inv->size(); 38 | auto third_item = inv->Get(2); 39 | std::cout << "inventory size: " << inv_len << std::endl; 40 | std::cout << "inventory[2]: " << third_item << std::endl; 41 | // vector of table 42 | auto weapons = monster->weapons(); // A pointer to a `flatbuffers::Vector<>`. 43 | auto weapon_len = weapons->size(); 44 | auto second_weapon_name = weapons->Get(1)->name()->str(); 45 | auto second_weapon_damage = weapons->Get(1)->damage(); 46 | // union 47 | auto union_type = monster->equipped_type(); 48 | if (union_type == Equipment_Weapon) 49 | { 50 | auto weapon = static_cast(monster->equipped()); // Requires `static_cast` 51 | // to type `const Weapon*`. 52 | auto weapon_name = weapon->name()->str(); // "Axe" 53 | auto weapon_damage = weapon->damage(); // 5 54 | std::cout << "weapon_name: " << weapon_name << std::endl; 55 | std::cout << "weapon_damage: " << weapon_damage << std::endl; 56 | } 57 | return 0; 58 | } -------------------------------------------------------------------------------- /flatbuffers/net_read.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "net_generated.h" 6 | using namespace PiNet; 7 | 8 | int main() { 9 | std::ifstream infile; 10 | infile.open("net.mnn", std::ios::binary | std::ios::in); 11 | infile.seekg(0, std::ios::end); 12 | int length = infile.tellg(); 13 | infile.seekg(0, std::ios::beg); 14 | char* buffer_pointer = new char[length]; 15 | infile.read(buffer_pointer, length); 16 | infile.close(); 17 | 18 | auto net = GetNet(buffer_pointer); 19 | 20 | auto ConvOp = net->oplists()->Get(0); 21 | auto ConvOpT = ConvOp->UnPack(); 22 | 23 | auto PoolOp = net->oplists()->Get(1); 24 | auto PoolOpT = PoolOp->UnPack(); 25 | 26 | auto inputIndexes = ConvOpT->inputIndexes; 27 | auto outputIndexes = ConvOpT->outputIndexes; 28 | auto type = ConvOpT->type; 29 | std::cout << "inputIndexes: " << inputIndexes[0] << std::endl; 30 | std::cout << "outputIndexes: " << outputIndexes[0] << std::endl; 31 | 32 | PiNet::OpParameterUnion OpParameterUnion = ConvOpT->parameter; 33 | switch (OpParameterUnion.type) { 34 | case OpParameter_Conv: { 35 | auto ConvOpParameterUnion = OpParameterUnion.AsConv(); 36 | auto k = ConvOpParameterUnion->kernelX; 37 | std::cout << "ConvOpParameterUnion, k: " << k << std::endl; 38 | break; 39 | } 40 | case OpParameter_Pool: { 41 | auto PoolOpParameterUnion = OpParameterUnion.AsPool(); 42 | auto k = PoolOpParameterUnion->padX; 43 | std::cout << "PoolOpParameterUnion, k: " << k << std::endl; 44 | break; 45 | } 46 | default: 47 | break; 48 | } 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /flatbuffers/net_write.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "net_generated.h" 5 | using namespace PiNet; 6 | 7 | int main() { 8 | flatbuffers::FlatBufferBuilder builder(1024); 9 | 10 | // table ConvT 11 | auto ConvT = new PiNet::ConvT; 12 | ConvT->kernelX = 3; 13 | ConvT->kernelY = 3; 14 | // union ConvUnionOpParameter 15 | OpParameterUnion ConvUnionOpParameter; 16 | ConvUnionOpParameter.type = OpParameter_Conv; 17 | ConvUnionOpParameter.value = ConvT; 18 | // table OpT 19 | auto ConvTableOpt = new PiNet::OpT; 20 | ConvTableOpt->name = "Conv"; 21 | ConvTableOpt->inputIndexes = {0}; 22 | ConvTableOpt->outputIndexes = {1}; 23 | ConvTableOpt->type = OpType_Conv; 24 | ConvTableOpt->parameter = ConvUnionOpParameter; 25 | 26 | // table PoolT 27 | auto PoolT = new PiNet::PoolT; 28 | PoolT->padX = 3; 29 | PoolT->padY = 3; 30 | // union OpParameterUnion 31 | OpParameterUnion PoolUnionOpParameter; 32 | PoolUnionOpParameter.type = OpParameter_Pool; 33 | PoolUnionOpParameter.value = PoolT; 34 | // table Opt 35 | auto PoolTableOpt = new PiNet::OpT; 36 | PoolTableOpt->name = "Pool"; 37 | PoolTableOpt->inputIndexes = {1}; 38 | PoolTableOpt->outputIndexes = {2}; 39 | PoolTableOpt->type = OpType_Pool; 40 | PoolTableOpt->parameter = PoolUnionOpParameter; 41 | 42 | // table NetT 43 | auto netT = new PiNet::NetT; 44 | netT->oplists.emplace_back(ConvTableOpt); 45 | netT->oplists.emplace_back(PoolTableOpt); 46 | netT->tensorName = {"conv_in", "conv_out", "pool_out"}; 47 | netT->outputName = {"pool_out"}; 48 | // table Net 49 | auto net = CreateNet(builder, netT); 50 | builder.Finish(net); 51 | 52 | // This must be called after `Finish()`. 53 | uint8_t* buf = builder.GetBufferPointer(); 54 | int size = builder.GetSize(); // Returns the size of the buffer that 55 | //`GetBufferPointer()` points to. 56 | std::ofstream output("net.mnn", std::ofstream::binary); 57 | output.write((const char*)buf, size); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /leetcode/search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | int BinarySearch(vector v, int k) 6 | { 7 | int left = 0; 8 | int right = v.size() - 1; 9 | int middle = (left + right) / 2; 10 | 11 | while (left <= right) 12 | { 13 | if (v[middle] == k) 14 | return middle; 15 | else if (v[middle] < k) 16 | left = middle + 1; 17 | else 18 | right = middle - 1; 19 | 20 | middle = (left + right) / 2; 21 | } 22 | return -1; 23 | } 24 | 25 | 26 | int main() 27 | { 28 | vector v{1,2,5,8,9,10}; 29 | int r = BinarySearch(v,11); 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /leetcode/sword/二叉树-二叉树遍历.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | struct TreeNode 7 | { 8 | int val; 9 | TreeNode *left; 10 | TreeNode *right; 11 | TreeNode(int x) : val(x), left(NULL), right(NULL) {} 12 | }; 13 | 14 | vector v; 15 | 16 | void front_scanf(TreeNode *pRoot) 17 | { 18 | if (pRoot == nullptr) 19 | return; 20 | 21 | v.push_back(pRoot->val); 22 | 23 | if (pRoot->left) 24 | front_scanf(pRoot->left); 25 | if (pRoot->right) 26 | front_scanf(pRoot->right); 27 | } 28 | 29 | void middle_scanf(TreeNode *pRoot) 30 | { 31 | if (pRoot == nullptr) 32 | return; 33 | 34 | if (pRoot->left) 35 | middle_scanf(pRoot->left); 36 | v.push_back(pRoot->val); 37 | if (pRoot->right) 38 | middle_scanf(pRoot->right); 39 | } 40 | 41 | void back_scanf(TreeNode *pRoot) 42 | { 43 | if (pRoot == nullptr) 44 | return; 45 | 46 | if (pRoot->left) 47 | back_scanf(pRoot->left); 48 | 49 | if (pRoot->right) 50 | back_scanf(pRoot->right); 51 | v.push_back(pRoot->val); 52 | } 53 | 54 | int main() 55 | { 56 | TreeNode *pRoot = new TreeNode(5); 57 | pRoot->left = new TreeNode(3); 58 | pRoot->right = new TreeNode(7); 59 | 60 | pRoot->left->left = new TreeNode(2); 61 | pRoot->left->right = new TreeNode(4); 62 | 63 | pRoot->right->left = new TreeNode(6); 64 | pRoot->right->right = new TreeNode(8); 65 | 66 | // front_scanf(pRoot); 67 | // middle_scanf(pRoot); 68 | back_scanf(pRoot); 69 | 70 | return 0; 71 | } -------------------------------------------------------------------------------- /leetcode/sword/位运算-不用加减乘除做加法.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 65. 不用加减乘除做加法 2 | // 写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。 3 | 4 | // 示例: 5 | // 输入: a = 1, b = 1 6 | // 输出: 2 7 | 8 | // -------------------第三次刷----------------------- 9 | // 2021年6月3日15:56:28 10 | // 准备第一次社招 11 | // 思路: 把加法分成2步,第一步用异或^代替不进位的相加,第二步用相与&后左移一位代替进位相加. 直到没有进位为止 12 | class Solution { 13 | public: 14 | int add(int a, int b) { 15 | int num1 = a; 16 | int num2 = b; 17 | 18 | //把加法分成2步,第一步用异或^代替不进位的相加,第二步用相与&后左移一位代替进位相加 19 | int sum, carry; 20 | do { 21 | sum = num1 ^ num2; 22 | //因为最高位是符号位,所以不用 23 | carry = (num1 & num2) << 1; 24 | 25 | num1 = sum; 26 | num2 = carry; 27 | } while (carry);//进位相加后还可能再产生新的进位 28 | return sum; 29 | } 30 | }; 31 | 32 | //page 310 33 | #include 34 | #include 35 | using namespace std; 36 | class Solution 37 | { 38 | public: 39 | // int Add(int num1, int num2) 40 | // { 41 | // int sum,carry; 42 | // do 43 | // { 44 | // sum = num1^num2; 45 | // carry = (num1&num2)<<1; 46 | // num1 = sum; 47 | // num2 =carry; 48 | // } 49 | // while(num2!=0); 50 | // return num1; 51 | // } 52 | int Add(int num1, int num2) 53 | { 54 | //把加法分成2步,第一步用异或^代替不进位的相加,第二步用相与&后左移一位代替进位相加 55 | int sum, carry; 56 | do 57 | { 58 | sum = num1 ^ num2; 59 | //因为最高位是符号位,所以不用 60 | carry = (num1 & num2) << 1; 61 | 62 | num1 = sum; 63 | num2 = carry; 64 | } while (carry);//进位相加后还可能再产生新的进位 65 | //负数的补码也是对的,但是分析起来很复杂 66 | //返回也是int代表不考虑溢出 67 | return sum; 68 | } 69 | }; 70 | 71 | int main() 72 | { 73 | int num1=-1; 74 | cout<>1; 33 | } 34 | while(n>0); 35 | return r; 36 | } 37 | }; 38 | 39 | 40 | #include 41 | 42 | class Solution 43 | { 44 | public: 45 | // int NumberOf1(int n) 46 | // { 47 | // unsigned int bit=1; 48 | // int number=0; 49 | 50 | // while(bit) 51 | // { 52 | // if(n&bit) 53 | // number++; 54 | // //不要右移动数n本身,左移动bit 55 | // bit=bit<<1; 56 | // } 57 | // return number; 58 | 59 | // } 60 | int NumberOf1(int n) 61 | { 62 | int number=0; 63 | 64 | while(n) 65 | { 66 | //一个数减去1后再跟原数&可以将原数最右边的1变成0 67 | n = (n-1)&n; 68 | number++; 69 | } 70 | return number; 71 | } 72 | }; 73 | 74 | int main() 75 | { 76 | // int n = 9; 77 | // int count =0; 78 | // while(n) 79 | // { 80 | // if(n&1) 81 | // count ++; 82 | // n = n >> 1; 83 | // } 84 | // int n = 9; 85 | // int count = 0; 86 | // int temp = 1; 87 | // while(temp) 88 | // { 89 | // if (n&temp) 90 | // count ++; 91 | // temp = temp << 1; 92 | // } 93 | int n = -9; 94 | Solution solution; 95 | int r = solution.NumberOf1(n); 96 | return 0; 97 | } -------------------------------------------------------------------------------- /leetcode/sword/堆栈-队列的最大值.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 59 - II. 队列的最大值 2 | // 请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。 3 | // 若队列为空,pop_front 和 max_value 需要返回 -1 4 | 5 | // 示例 1: 6 | // 输入: 7 | // ["MaxQueue","push_back","push_back","max_value","pop_front","max_value"] 8 | // [[],[1],[2],[],[],[]] 9 | // 输出: [null,null,null,2,1,2] 10 | 11 | // 示例 2: 12 | // 输入: 13 | // ["MaxQueue","pop_front","max_value"] 14 | // [[],[],[]] 15 | // 输出: [null,-1,-1] 16 | 17 | // -------------------第三次刷----------------------- 18 | // 2021年6月3日14:30:29 19 | // 准备第一次社招 20 | // 思路: 就跟那题包含min的栈思路一样, 用两个队列是很容易想到的. 一个队列d1正常使用, 另外一个d2包含对应高度的最大值, 但是问题是队列是可以前面出队的, 这样d2前面一变动, 21 | // 后面所有的最大值就失效了. 因此不能是同样高度一一对应的. 有一个特点应该注意, 比如正常队列里d1是1,1,1,3, 只要3还没出队, 对于d2来说前面的1都不重要. 因此改进 22 | // 的d2在push value的时候会pop_back()直到value d1; 27 | deque d2; 28 | MaxQueue() { 29 | 30 | } 31 | 32 | int max_value() { 33 | if(d2.empty()) 34 | return -1; 35 | 36 | return d2.front(); 37 | } 38 | 39 | void push_back(int value) { 40 | d1.push(value); 41 | 42 | while(!d2.empty() && value>d2.back()){ 43 | d2.pop_back(); 44 | } 45 | d2.push_back(value); 46 | } 47 | 48 | int pop_front() { 49 | if(d1.empty()) 50 | return -1; 51 | 52 | int tmp = d1.front(); 53 | d1.pop(); 54 | 55 | if(tmp == d2.front()){ 56 | d2.pop_front(); 57 | } 58 | return tmp; 59 | } 60 | }; 61 | 62 | /** 63 | * Your MaxQueue object will be instantiated and called as such: 64 | * MaxQueue* obj = new MaxQueue(); 65 | * int param_1 = obj->max_value(); 66 | * obj->push_back(value); 67 | * int param_3 = obj->pop_front(); 68 | */ -------------------------------------------------------------------------------- /leetcode/sword/字符串-字符流中第一个不重复的字符.cpp: -------------------------------------------------------------------------------- 1 | //page 247 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | class Solution 8 | { 9 | public: 10 | //Insert one char from stringstream 11 | // unordered_map map; 12 | // string s; 13 | 14 | // void Insert(char ch) 15 | // { 16 | // s += ch; 17 | // if (map.find(ch) == map.end()) 18 | // { 19 | // map[ch] = 1; 20 | // } 21 | // else 22 | // { 23 | // map[ch] += 1; 24 | // } 25 | // } 26 | // //return the first appearence once char in current stringstream 27 | // char FirstAppearingOnce() 28 | // { 29 | // for (int i = 0; i < s.size(); i++) 30 | // { 31 | // if (map[s[i]] == 1) 32 | // return s[i]; 33 | // } 34 | // return '#'; 35 | // } 36 | string g_s; 37 | unordered_map map; 38 | void Insert(char ch) 39 | { 40 | if (map.find(ch) == map.end()) 41 | map[ch] = 1; 42 | else 43 | map[ch] += 1; 44 | g_s += ch; 45 | } 46 | char FirstAppearingOnce() 47 | { 48 | for (int i = 0; i < g_s.size(); i++) 49 | { 50 | if (map[g_s[i]] == 1) 51 | return g_s[i]; 52 | } 53 | return '#'; 54 | } 55 | }; -------------------------------------------------------------------------------- /leetcode/sword/字符串-左旋转字符串.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 58 - II. 左旋转字符串 2 | // 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 3 | 4 | // 示例 1: 5 | // 输入: s = "abcdefg", k = 2 6 | // 输出: "cdefgab" 7 | 8 | // 示例 2: 9 | // 输入: s = "lrloseumgh", k = 6 10 | // 输出: "umghlrlose" 11 | 12 | // -------------------第三次刷----------------------- 13 | // 2021年6月3日10:43:23 14 | // 准备第一次社招 15 | // 思路: 很简单, 可能不会遇到. 就取两段str, 然后倒叙拼接即可 16 | class Solution { 17 | public: 18 | string reverseLeftWords(string s, int n) { 19 | string s1 = s.substr(0, n); 20 | string s2 = s.substr(n, s.size()-n); 21 | return s2+s1; 22 | } 23 | }; 24 | 25 | // 思路2: 这个思路很牛逼. 倍增, 然后从n开始取str 26 | class Solution { 27 | public: 28 | string reverseLeftWords(string s, int n) { 29 | s += s; 30 | return s.substr(n,s.size()/2); 31 | } 32 | }; 33 | 34 | //page 286 35 | #include 36 | 37 | using namespace std; 38 | 39 | class Solution 40 | { 41 | public: 42 | // string LeftRotateString(string str, int n) 43 | // { 44 | // if(str.size()==0) 45 | // return str; 46 | // if (n > str.size()) 47 | // { 48 | // n %= str.size(); 49 | // } 50 | // string s_new(str.begin() + n, str.end()); 51 | // string s_add(str.begin(), str.begin() + n); 52 | // s_new += s_add; 53 | // return s_new; 54 | // } 55 | string LeftRotateString(string str, int n) 56 | { 57 | if (n > str.size()) 58 | n %= str.size(); 59 | if (n <= 0) 60 | return str; 61 | 62 | string s(str.begin(), str.begin() + n); 63 | str.erase(0, n); 64 | str += s; 65 | return str; 66 | } 67 | }; 68 | 69 | int main() 70 | { 71 | Solution solution; 72 | string s = "abcXYZdef"; 73 | int n = 13; 74 | solution.LeftRotateString(s, n); 75 | return 0; 76 | } -------------------------------------------------------------------------------- /leetcode/sword/字符串-把数字翻译成字符串.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 46. 把数字翻译成字符串 2 | // 给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。 3 | 4 | // 示例 1: 5 | // 输入: 12258 6 | // 输出: 5 7 | // 解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi" 8 | 9 | // -------------------第三次刷----------------------- 10 | // 2021年5月28日14:36:41 11 | // 准备第一次社招 12 | // 思路: 递归. count++的条件是index刚好指向[...12]3. 13 | 14 | 15 | #include 16 | using namespace std; 17 | class Solution { 18 | public: 19 | void translateNum(string s, int index, int& count){ 20 | if(index==s.size()){ 21 | count++; 22 | return; 23 | } 24 | if(index>s.size()) 25 | return; 26 | 27 | translateNum(s, index+1, count); 28 | if(string(s, index, 2)<=string("25") && index<=s.size()-2 && s[index] != '0')//这里要手动确保不能出现'05'这样的数 29 | translateNum(s, index+2, count); 30 | 31 | return; 32 | } 33 | 34 | int translateNum(int num) { 35 | int count = 0; 36 | string s = to_string(num); 37 | 38 | translateNum(s, 0, count); 39 | 40 | return count; 41 | } 42 | }; 43 | 44 | int main(){ 45 | Solution s; 46 | 47 | return s.translateNum(506); 48 | } -------------------------------------------------------------------------------- /leetcode/sword/字符串-最长不含重复字符的子字符串.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 48. 最长不含重复字符的子字符串 2 | // 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。 3 | 4 | // 示例 1: 5 | // 输入: "abcabcbb" 6 | // 输出: 3 7 | // 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 8 | 9 | // 示例 2: 10 | // 输入: "bbbbb" 11 | // 输出: 1 12 | // 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 13 | 14 | // 示例 3: 15 | // 输入: "pwwkew" 16 | // 输出: 3 17 | // 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 18 | // 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 19 | 20 | // -------------------第三次刷----------------------- 21 | // 2021年5月31日10:56:00 22 | // 准备第一次社招 23 | // 思路: 双指针+map. 逐步移动right, 遇到在map里没有的, 则增加map, 更新length与right. 遇到map里有的, 则更新left. 精华是这个max. 保证left不会往前退. 往前退是可能出现重复的 24 | #include 25 | #include 26 | using namespace std; 27 | 28 | class Solution { 29 | public: 30 | int lengthOfLongestSubstring(string s) { 31 | unordered_map map; 32 | int left = 0; 33 | int right = 0; 34 | int max_length = 0; 35 | while (right < s.size()) { 36 | if (map.find(s[right]) != map.end()) { //有 37 | left = max(left, map[s[right]]+1);// abba, 进入循环时right=3, left=2, 执行到改行时, 如果没有max, 则left回退为1, 因为1-3之间是有重复的2个b的. 因此left不能回退 38 | } 39 | map[s[right]] = right; 40 | max_length = max(max_length, right - left + 1); 41 | right++; 42 | } 43 | return max_length; 44 | } 45 | }; 46 | 47 | int main(){ 48 | Solution s; 49 | s.lengthOfLongestSubstring("abcabcbb"); 50 | return 0; 51 | } -------------------------------------------------------------------------------- /leetcode/sword/数组-0到n-1中缺失的数字.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 53 - II. 0~n-1中缺失的数字 2 | // 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。 3 | 4 | // 示例 1: 5 | // 输入: [0,1,3] 6 | // 输出: 2 7 | 8 | // 示例 2: 9 | // 输入: [0,1,2,3,4,5,6,7,9] 10 | // 输出: 8 11 | 12 | // -------------------第三次刷----------------------- 13 | // 2021年6月1日10:04:34 14 | // 准备第一次社招 15 | // 思路: 16 | // 索引:[0,1,2] 17 | // 数组:[0,1,3] 18 | // 缺失的数就是第一个索引与数组对不上的索引值. 即二分的时候找的是nums[middle]!=middle, 且nums[middle-1]==middle-1. 19 | // 注意: 考虑两端 20 | // 缺失0, 考虑数组越界时已覆盖 21 | // 索引:[0,1,2] 22 | // 数组:[1,2,3] 23 | // 缺失3, 则出现二分找不到的现象, 由于题目是一定会有缺失的数字, 则r=-1时, 直接返回num.size()即可 24 | // 索引:[0,1,2] 25 | // 数组:[0,1,2] 26 | 27 | 28 | #include 29 | using namespace std; 30 | 31 | class Solution { 32 | public: 33 | int missingNumber(vector& nums) { 34 | int left = 0; 35 | int right = nums.size() - 1; 36 | int middle = (left+right)/2; 37 | int r= -1; 38 | while(left<=right){ 39 | if(nums[middle]==middle){ 40 | left=middle+1; 41 | } 42 | else{ 43 | if(middle==0){ 44 | return middle; 45 | } 46 | else{ 47 | if(nums[middle-1]==middle-1) 48 | return middle; 49 | else 50 | right=middle-1; 51 | } 52 | } 53 | 54 | middle = (left+right)/2; 55 | } 56 | return nums.size(); 57 | } 58 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-1到n整数中1出现的次数.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 43. 1~n 整数中 1 出现的次数 2 | // 输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。 3 | 4 | // 例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。 5 | 6 | // 示例 1: 7 | // 输入:n = 12 8 | // 输出:5 9 | 10 | // 示例 2: 11 | // 输入:n = 13 12 | // 输出:6 13 | 14 | // -------------------第三次刷----------------------- 15 | // 2021年5月28日14:09:56 16 | // 准备第一次社招 17 | // 思路: 18 | 19 | class Solution { 20 | public: 21 | int countDigitOne(int n) { 22 | 23 | } 24 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-n个骰子的点数.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 60. n个骰子的点数 2 | // 把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。 3 | 4 | // 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。 5 | 6 | // 示例 1: 7 | // 输入: 1 8 | // 输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667] 9 | 10 | // 示例 2: 11 | // 输入: 2 12 | // 输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778] 13 | 14 | // -------------------第三次刷----------------------- 15 | // 2021年6月3日14:30:29 16 | // 准备第一次社招 17 | // 思路: dp[i][j]表示投掷i枚骰子得到点数和为j的次数. 而状态转移方程为为: 18 | // i枚点数和只能是少一枚i-1的点数和+1~6, 即dp[i][j] = dp[i-1][j-1]+dp[i-1][j-2]+dp[i-1][j-3]+dp[i-1][j-4]+dp[i-1][j-5]+dp[i-1][j-6] 19 | // 初始状态dp[1][j]都为1, 其中j为1~6 20 | 21 | #include 22 | #include 23 | using namespace std; 24 | 25 | class Solution { 26 | public: 27 | vector dicesProbability(int n) { 28 | vector> dp(n+1, vector(6*n+1, 0)); 29 | for(int i=1;i<=6;i++) 30 | dp[1][i] = 1; 31 | 32 | for(int i=2;i<=n;i++){ 33 | for(int j=i;j<=6*i;j++){//点数和最小是当i个骰子全是1, 为i. 点数和最大是当i个骰子全是6, 为6*i 34 | // dp[i][j] = dp[i-1][j-1]+dp[i-1][j-2]+dp[i-1][j-3]+dp[i-1][j-4]+dp[i-1][j-5]+dp[i-1][j-6] 35 | for(int k=1;k<=6;k++){ 36 | if(j-k>=0) 37 | dp[i][j] += dp[i-1][j-k]; 38 | } 39 | } 40 | } 41 | 42 | int sum = pow(6, n);//投掷n个骰子可能出现的所有的情况 43 | vector ret; 44 | for(int i=n;i<=6*n;i++){ 45 | ret.push_back(dp[n][i]/sum); 46 | } 47 | return ret; 48 | } 49 | }; 50 | 51 | int main(){ 52 | Solution s; 53 | s.dicesProbability(1); 54 | return 1; 55 | } -------------------------------------------------------------------------------- /leetcode/sword/数组-丑数.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 49. 丑数 2 | // 我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。 3 | 4 | // 示例: 5 | // 输入: n = 10 6 | // 输出: 12 7 | // 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。 8 | 9 | // -------------------第三次刷----------------------- 10 | // 2021年5月31日10:56:00 11 | // 准备第一次社招 12 | // 思路: 动态规划. 一个丑数只可能是另一个丑数a乘以2或者另一个丑数b乘以3或者另一个丑数c乘以5得来. 13 | // 最直白的想法是把每个数分别乘以2,3,5, 但是得到的数顺序未知. 可以用a,b,c三个数分别来记录索引里 14 | // 乘过a,b,c的, 如a代表序列里索引从0到a都乘过2并且结果已经接在序列里. 这样每次比较3个数n2, n3, n5找到最小的即可. 15 | // 而且保证了每个数都乘过2,3,5 16 | 17 | #include 18 | using namespace std; 19 | class Solution { 20 | public: 21 | int nthUglyNumber(int n) { 22 | vector dp(n, 0); 23 | dp[0] = 1; 24 | int a=0, b=0, c=0; 25 | for(int i=1;i 2 | #include 3 | using namespace std; 4 | 5 | bool Find(int target, vector> array) { 6 | // 数组表示,array.size()=4,array[0].size()=3 7 | // 1 2 8 9 8 | // [[4],[7],[10],[13]] 9 | // 6 8 11 15 10 | 11 | // 这个是colums,竖着的列数 12 | if (array.size() == 0) 13 | return false; 14 | // 这个是rows,横着的行数 15 | if (array[0].size() == 0) 16 | return false; 17 | 18 | int rows = 0; 19 | int columns = array.size() - 1; 20 | 21 | while (true) { 22 | // 小于右上角,边界往左 23 | if (target < array[columns][rows]) { 24 | if (columns > 0) 25 | columns--; 26 | else 27 | return false; 28 | } 29 | // 大于右上角,边界往下 30 | else if (target > array[columns][rows]) { 31 | if (rows < array[0].size() - 1) 32 | rows++; 33 | else 34 | return false; 35 | } else { 36 | return true; 37 | } 38 | } 39 | } 40 | 41 | // 剑指 Offer 04. 二维数组中的查找 42 | // 在一个 n * m 43 | // 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 44 | // 示例: 45 | // 现有矩阵 matrix 如下: 46 | // [ 47 | // [1, 4, 7, 11, 15], 48 | // [2, 5, 8, 12, 19], 49 | // [3, 6, 9, 16, 22], 50 | // [10, 13, 14, 17, 24], 51 | // [18, 21, 23, 26, 30] 52 | // ] 53 | // 来源:力扣(LeetCode) 54 | // 链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof 55 | // 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 56 | 57 | // 2021年04月24日15:22:50 58 | // 准备第一次社招 59 | // 思路: 核心就是右上角的数. 建立交点位于右上角的横竖两根线, target>交点则横线下移, target<交点则竖线左移, 60 | // target==交点则找到, 横竖线越界则找不到 61 | class Solution { 62 | public: 63 | bool findNumberIn2DArray(vector>& matrix, int target) { 64 | if (matrix.size() == 0) 65 | return false; 66 | 67 | int h_max = matrix.size(); 68 | int w_max = matrix[0].size(); 69 | 70 | int h = 0; 71 | int w = w_max - 1; 72 | while (h < h_max && w >= 0) { 73 | if (target > matrix[h][w]) 74 | h++; 75 | else if (target < matrix[h][w]) 76 | w--; 77 | else 78 | return true; 79 | } 80 | return false; 81 | } 82 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-二维数组中的查找.py: -------------------------------------------------------------------------------- 1 | class Solution: 2 | # array 二维列表 3 | def Find(self, target, array): 4 | while(True): 5 | try: 6 | if target < array[0][-1]: 7 | array = [h[:-1] for h in array ] 8 | elif target == array[0][-1]: 9 | return True 10 | else: 11 | array = array[1:] 12 | except: 13 | return False -------------------------------------------------------------------------------- /leetcode/sword/数组-打印从1到最大的n位数.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 17. 打印从1到最大的n位数 2 | // 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 3 | 4 | // 示例 1: 5 | // 输入: n = 1 6 | // 输出: [1,2,3,4,5,6,7,8,9] 7 | 8 | // 说明: 9 | // 用返回一个整数列表来代替打印 10 | // n 为正整数 11 | 12 | 13 | // -------------------第三次刷----------------------- 14 | // 2021年5月11日14:52:37 15 | // 准备第一次社招 16 | // 思路: 什么鬼. for循环就好了, 而且本来应该考虑的大数问题也因为返回类型是int而限定了 17 | 18 | #include 19 | using namespace std; 20 | 21 | class Solution { 22 | public: 23 | vector printNumbers(int n) { 24 | vector r; 25 | int t = 1; 26 | for(int i=0;i& nums) { 28 | sort(nums.begin(), nums.end(), cmp); 29 | string res; 30 | for(int i=0;i 39 | #include 40 | using namespace std; 41 | 42 | static bool cmp(int a, int b) 43 | { 44 | // 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较 45 | string A = to_string(a)+to_string(b); 46 | string B = to_string(b)+to_string(a); 47 | return A numbers) { 50 | 51 | sort(numbers.begin(),numbers.end(),cmp); 52 | string res; 53 | for(int i = 0; i < numbers.size(); i++){ 54 | res += to_string(numbers[i]); 55 | } 56 | return res; 57 | } -------------------------------------------------------------------------------- /leetcode/sword/数组-数字序列中某一位的数字.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 44. 数字序列中某一位的数字 2 | // 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。 3 | 4 | // 请写一个函数,求任意第n位对应的数字。 5 | 6 | // 示例 1: 7 | // 输入:n = 3 8 | // 输出:3 9 | 10 | // 示例 2: 11 | // 输入:n = 11 12 | // 输出:0 13 | 14 | // -------------------第三次刷----------------------- 15 | // 2021年5月28日14:36:41 16 | // 准备第一次社招 17 | // 思路: 18 | // 先得到n对应的是几位数, 根据的规律是:dight位数有9*10^(dight-1)个数. 循环减就行. (先要手动减掉0) 19 | 20 | 21 | class Solution { 22 | public: 23 | int findNthDigit(int n) { 24 | if(n==0) 25 | return 0; 26 | n--; 27 | int dight = 1; 28 | int count = 9; 29 | 30 | while() 31 | } 32 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-数组中数字出现的次数II.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 56 - II. 数组中数字出现的次数 II 2 | // 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。 3 | 4 | // 示例 1: 5 | // 输入:nums = [3,4,3,3] 6 | // 输出:4 7 | 8 | // 示例 2: 9 | // 输入:nums = [9,1,7,9,7,9,7] 10 | // 输出:1 11 | 12 | // -------------------第三次刷----------------------- 13 | // 2021年6月1日17:41:12 14 | // 准备第一次社招 15 | // 思路: 这题就不能用异或对消的办法了. 可以直接暴力一点, 统计32位上所有数出现1的次数, 每位%3!=0的则目标数字在该位也为1 16 | 17 | #include 18 | using namespace std; 19 | class Solution { 20 | public: 21 | int singleNumber(vector& nums) { 22 | vector v(32); 23 | unsigned int dight=1; 24 | for(int j=0;j<32;j++){ 25 | for(int i=0;i v{3,4,3,3}; 45 | s.singleNumber(v); 46 | return -1; 47 | } -------------------------------------------------------------------------------- /leetcode/sword/数组-斐波那契数列.cpp: -------------------------------------------------------------------------------- 1 | // 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下: 2 | // F(0) = 0,   F(1) = 1 3 | // F(N) = F(N - 1) + F(N - 2), 其中 N > 1. 4 | // 斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。 5 | // 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 6 | 7 | // 来源:力扣(LeetCode) 8 | // 链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof 9 | // 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 10 | 11 | // 2021年04月25日22:41:25 12 | // 准备第一次社招 13 | // 思路: 14 | // 最简单的动态规划, 不需要解释 15 | #include 16 | using namespace std; 17 | 18 | class Solution { 19 | public: 20 | int fib(int n) { 21 | if (n == 0) 22 | return 0; 23 | else if (n == 1) 24 | return 1; 25 | 26 | vector dp(n + 1); 27 | dp[0] = 0; 28 | dp[1] = 1; 29 | for (int i = 2; i <= n; i++) { 30 | dp[i] = (dp[i - 1] + dp[i - 2])% (int)(1e9 + 7); 31 | } 32 | return dp[n]; //% (int)(1e9 + 7); 33 | } 34 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-最长递增子数组长度.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 300. 最长递增子序列 6 | // 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 7 | // 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。 8 | 9 | // 示例 1: 10 | // 输入:nums = [10,9,2,5,3,7,101,18] 11 | // 输出:4 12 | // 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 13 | 14 | // 示例 2: 15 | // 输入:nums = [0,1,0,3,2,3] 16 | // 输出:4 17 | 18 | // 示例 3: 19 | // 输入:nums = [7,7,7,7,7,7,7] 20 | // 输出:1 21 | 22 | // -------------------第二次刷----------------------- 23 | // 2021年7月2日10:59:10 24 | // 准备第一次社招 25 | // 思路: 动态规划, dp[i]代表要求的结果. 状态转移就是dp[i] = max(dp[i], dp[j]+1); 遍历i前面的dp[j]找到满足nums[j]& nums) { 34 | vector dp(nums.size(), 1); 35 | for (int i = 0; i < nums.size(); i++) { 36 | for (int j = 0; j < i; j++) { 37 | if (nums[j] < nums[i]) 38 | dp[i] = max(dp[i], dp[j]+1); 39 | } 40 | } 41 | int num=INT32_MIN; 42 | for(int i=0;i, 因为需要判断max_heap.top还是否在窗口内 25 | #include 26 | using namespace std; 27 | 28 | class Solution { 29 | public: 30 | vector maxSlidingWindow(vector& nums, int k) { 31 | priority_queue, vector>, less>> max_heap; 32 | for(int i=0;i ans ={max_heap.top().first}; 36 | for(int i=k;i 48 | #include 49 | using namespace std; 50 | 51 | class Solution 52 | { 53 | public: 54 | vector maxInWindows(const vector &num, unsigned int size) 55 | { 56 | vector v; 57 | if (size > num.size() || size == 0) 58 | return v; 59 | 60 | int left = 0; 61 | int right = size - 1; 62 | 63 | while (right <= num.size() - 1) 64 | { 65 | int temp_max = INT32_MIN; 66 | for (int i = left; i <= right; i++) 67 | { 68 | if (num[i] > temp_max) 69 | temp_max = num[i]; 70 | } 71 | v.push_back(temp_max); 72 | left++; 73 | right++; 74 | } 75 | return v; 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /leetcode/sword/数组-连续子数组的最大和.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 42. 连续子数组的最大和 2 | // 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 3 | 4 | // 要求时间复杂度为O(n)。 5 | 6 | // 示例1: 7 | // 输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 8 | // 输出: 6 9 | // 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。 10 | 11 | // -------------------第三次刷----------------------- 12 | // 2021年5月18日14:31:58 13 | // 准备第一次社招 14 | // 思路: 蛮简单的, 思路看代码就成. 就是到底是先判断小于0还是先加的问题, 应该先判断. 因为默认执行+=, 但是如果执行前sum已经小于0了, 就 15 | // 可以抛弃那部分小于0的sum重新对sum赋值即可 16 | 17 | #include 18 | using namespace std; 19 | 20 | class Solution { 21 | public: 22 | int maxSubArray(vector& nums) { 23 | int sum = 0; 24 | int max_sum = INT32_MIN; 25 | for(int i=0;imax_sum) 32 | max_sum = sum; 33 | } 34 | return max_sum; 35 | } 36 | }; 37 | 38 | 39 | //page 218 40 | #include 41 | #include 42 | 43 | using namespace std; 44 | 45 | class Solution 46 | { 47 | public: 48 | // int FindGreatestSumOfSubArray(vector array) { 49 | // if(array.size()==0) 50 | // return 0; 51 | 52 | // int nGreatestSum = -1; 53 | // int nCurSum = 0; 54 | // for(int i =0;inGreatestSum) 62 | // nGreatestSum = nCurSum; 63 | // } 64 | // return nGreatestSum; 65 | // } 66 | int FindGreatestSumOfSubArray(vector array) 67 | { 68 | if (array.size() == 0) 69 | return 0; 70 | 71 | int sum = 0; 72 | int great_sum = INT32_MIN; 73 | for (int i = 0; i < array.size(); i++) 74 | { 75 | 76 | if (sum < 0) 77 | sum = array[i]; 78 | else 79 | sum += array[i]; 80 | 81 | if (sum > great_sum) 82 | great_sum = sum; 83 | } 84 | return great_sum; 85 | } 86 | }; 87 | 88 | int main() 89 | { 90 | Solution solution; 91 | vector array{1, -2, 3, 10, -4, 7, 2, -5}; 92 | 93 | int result = solution.FindGreatestSumOfSubArray(array); 94 | } -------------------------------------------------------------------------------- /leetcode/sword/数组-青蛙跳台阶.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 10- II. 青蛙跳台阶问题 2 | // 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 3 | // 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 4 | // 示例 1: 5 | // 输入:n = 2 6 | // 输出:2 7 | // 示例 2: 8 | // 输入:n = 7 9 | // 输出:21 10 | // 示例 3: 11 | // 输入:n = 0 12 | // 输出:1 13 | 14 | // 2021年04月25日22:41:25 15 | // 准备第一次社招 16 | // 思路: 17 | // 最简单的动态规划, 不需要解释 18 | #include 19 | using namespace std; 20 | 21 | class Solution { 22 | public: 23 | int numWays(int n) { 24 | if (n == 0) 25 | return 1; 26 | else if (n == 1) 27 | return 1; 28 | 29 | vector dp(n + 1); 30 | dp[0] = 1; 31 | dp[1] = 1; 32 | for (int i = 2; i <= n; i++) { 33 | dp[i] = (dp[i - 1] + dp[i - 2]) % (int)(1e9 + 7); 34 | } 35 | return dp[n]; 36 | } 37 | }; -------------------------------------------------------------------------------- /leetcode/sword/数组-顺时针打印矩阵.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 29. 顺时针打印矩阵 2 | // 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 3 | 4 | // 示例 1: 5 | // 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 6 | // 输出:[1,2,3,6,9,8,7,4,5] 7 | 8 | // 示例 2: 9 | // 输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] 10 | // 输出:[1,2,3,4,8,12,11,10,9,5,6,7] 11 | 12 | // 限制: 13 | // 0 <= matrix.length <= 100 14 | // 0 <= matrix[i].length <= 100 15 | 16 | // -------------------第三次刷----------------------- 17 | // 2021年5月13日10:38:49 18 | // 准备第一次社招 19 | // 思路: 递归, 外面一圈, 完了里面一圈. 每一圈 20 | #include 21 | using namespace std; 22 | 23 | class Solution { 24 | public: 25 | //i是竖的, j是横的 26 | void spiralOrder_repeat(vector>& matrix, vector& v, int i_start, int i_end, int j_start, int j_end) { 27 | 28 | if(i_start>i_end&&j_start>j_end) 29 | return; 30 | 31 | if(i_start==i_end&&j_start==j_end)//只构成一个点 32 | v.push_back(matrix[i_start][j_start]); 33 | else if(i_start==i_end){//只构成一个横线, 每条线全部都打 34 | for(int j=j_start;j<=j_end;j++) 35 | v.push_back(matrix[i_start][j]); 36 | } 37 | else if(j_start==j_end){//只构成一个竖线, 每条线全部都打 38 | for(int i=i_start;i<=i_end;i++) 39 | v.push_back(matrix[i][j_start]); 40 | } 41 | else{//构成一个圈, 每条线末尾一位不打, 留给下一条线 42 | for(int j=j_start;jj_start;j--) 47 | v.push_back(matrix[i_end][j]); 48 | for(int i=i_end;i>i_start;i--) 49 | v.push_back(matrix[i][j_start]); 50 | } 51 | 52 | spiralOrder_repeat(matrix, v, i_start+1, i_end-1, j_start+1, j_end-1); 53 | 54 | } 55 | 56 | vector spiralOrder(vector>& matrix) { 57 | vector v; 58 | if (matrix.size() == 0) 59 | return v; 60 | 61 | spiralOrder_repeat(matrix, v, 0, matrix.size()-1, 0, matrix[0].size()-1); 62 | return v; 63 | } 64 | }; -------------------------------------------------------------------------------- /leetcode/sword/杂-剪绳子II.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 14- II. 剪绳子 II 2 | // 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。 3 | // 请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。 4 | 5 | // 答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 6 | 7 | // 示例 1: 8 | // 输入: 2 9 | // 输出: 1 10 | // 解释: 2 = 1 + 1, 1 × 1 = 1 11 | 12 | // 示例 2: 13 | // 输入: 10 14 | // 输出: 36 15 | // 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36 16 | 17 | // -------------------第三次刷----------------------- 18 | // 2021年5月11日14:52:37 19 | // 准备第一次社招 20 | // 思路1: 与剪绳子I没啥区别, 就是每次更新r的时候要取余 21 | class Solution { 22 | public: 23 | int cuttingRope(int n) { 24 | if(n==2) 25 | return 1; 26 | else if(n==3) 27 | return 2; 28 | else if(n==4) 29 | return 2*2; 30 | 31 | long long r = 1; 32 | while (n>=3) { 33 | r*=3; 34 | r%=(long)1e9+7;//更新后取余 35 | n-=3; 36 | } 37 | 38 | if(n==2){ 39 | r*=2; 40 | r%=(long)1e9+7;//更新后取余 41 | } 42 | else if(n==1){ 43 | r/=3; 44 | r = r*2*2; 45 | r%=(long)1e9+7;//更新后取余 46 | } 47 | else if(n==0){ 48 | r%=(long)1e9+7;//更新后取余 49 | } 50 | return r; 51 | // return r%=(long)1e9+7;//更新后取余; 52 | } 53 | }; -------------------------------------------------------------------------------- /leetcode/sword/杂-求1+2+3+...+n.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 64. 求1+2+…+n 2 | // 求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 3 | 4 | // 示例 1: 5 | // 输入: n = 3 6 | // 输出: 6 7 | 8 | // 示例 2: 9 | // 输入: n = 9 10 | // 输出: 45 11 | 12 | // -------------------第三次刷----------------------- 13 | // 2021年6月4日10:31:16 14 | // 准备第一次社招 15 | // 思路: 递归, 然后用逻辑运算符的短路性质作为停止条件. 16 | 17 | class Solution { 18 | public: 19 | int sumNums(int n) { 20 | n&&(n+=sumNums(n-1)); 21 | return n; 22 | } 23 | }; 24 | 25 | 26 | class Temp 27 | { 28 | public: 29 | Temp() 30 | { 31 | ++N; 32 | Sum += N; 33 | } 34 | 35 | static void Reset() 36 | { 37 | N = 0; 38 | Sum = 0; 39 | } 40 | static unsigned int GetSum() { return Sum; } 41 | 42 | private: 43 | static unsigned int N; 44 | static unsigned int Sum; 45 | 46 | }; 47 | 48 | unsigned int Temp::N=0; 49 | unsigned int Temp::Sum=0; 50 | 51 | 52 | 53 | class Solution 54 | { 55 | public: 56 | int Sum_Solution(int n) 57 | { 58 | Temp::Reset(); 59 | Temp *a = new Temp[n]; 60 | delete []a; 61 | a=nullptr; 62 | return Temp::GetSum(); 63 | } 64 | }; 65 | 66 | int main() 67 | { 68 | int n = 3; 69 | Solution solution; 70 | int result = solution.Sum_Solution(n); 71 | return 0; 72 | } -------------------------------------------------------------------------------- /leetcode/sword/棋盘-礼物的最大价值.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 47. 礼物的最大价值 2 | // 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、 3 | // 直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物? 4 | 5 | // 示例 1: 6 | 7 | // 输入: 8 | // [ 9 | // [1,3,1], 10 | // [1,5,1], 11 | // [4,2,1] 12 | // ] 13 | // 输出: 12 14 | // 解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物 15 | 16 | // -------------------第三次刷----------------------- 17 | // 2021年5月28日19:43:49 18 | // 准备第一次社招 19 | // 思路: 递归回溯大矩阵会超时 20 | // 思路2: 动态规划. dp[i][j]代表当前位置为终点时总共的最大礼物价值. dp[i][j] = max(dp[i-1][j], dp[i][j-1])+grid[i][j], 即当前格价值加上上或者左格的dp 21 | 22 | #include 23 | using namespace std; 24 | 25 | //递归会超时 26 | // class Solution { 27 | // public: 28 | // int biggest_value=0; 29 | // void maxValue(vector>& grid, int h, int w, int& value){ 30 | // if(h>=grid.size() || w>=grid[0].size()){ 31 | // return ; 32 | // } 33 | 34 | // value += grid[h][w]; 35 | // if(h==grid.size()-1 && w==grid[0].size()-1){ 36 | // if(value>biggest_value) 37 | // biggest_value = value; 38 | // } 39 | // else{ 40 | // maxValue(grid, h+1, w, value); 41 | // maxValue(grid, h, w+1, value); 42 | // } 43 | // value -= grid[h][w]; 44 | 45 | // return; 46 | // } 47 | 48 | // int maxValue(vector>& grid) { 49 | // int value=0; 50 | // maxValue(grid, 0, 0, value); 51 | // return biggest_value; 52 | // } 53 | // }; 54 | 55 | 56 | class Solution { 57 | public: 58 | int maxValue(vector>& grid){ 59 | int h=grid.size(); 60 | int w=grid[0].size(); 61 | vector> dp(h, vector(w, 0)); 62 | 63 | //先赋值0,0点 64 | dp[0][0] = grid[0][0]; 65 | //赋值第一行和第一列 66 | for(int i=1;i> v{{1,3,1},{1,5,1},{4,2,1}}; 81 | Solution s; 82 | int r = s.maxValue(v); 83 | return r; 84 | } -------------------------------------------------------------------------------- /leetcode/sword/链表-从尾到头打印链表.cpp: -------------------------------------------------------------------------------- 1 | //page 58 2 | 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | struct ListNode 9 | { 10 | int val; 11 | struct ListNode *next; 12 | ListNode(int x) : val(x), next(NULL) 13 | { 14 | } 15 | }; 16 | 17 | // vector printListFromTailToHead(ListNode *head) 18 | // { 19 | // if (head != nullptr) 20 | // { 21 | // if (head->next!=nullptr) 22 | // { 23 | // printListFromTailToHead(head->next); 24 | // } 25 | // return head->val; 26 | // } 27 | // } 28 | 29 | // vector printListFromTailToHead(ListNode *head) 30 | // { 31 | // vector v; 32 | // stack s; 33 | // if(head==nullptr) 34 | // return v; 35 | 36 | // while(head!=nullptr) 37 | // { 38 | // s.push(head); 39 | // head=head->next; 40 | // } 41 | 42 | // while(s.size()) 43 | // { 44 | // v.push_back(s.top()->val); 45 | // s.pop(); 46 | // } 47 | 48 | // return v; 49 | // } 50 | vector v; 51 | 52 | void printListFromTailToHead_repeat(ListNode *head) 53 | { 54 | if(head==nullptr) 55 | return; 56 | printListFromTailToHead_repeat(head->next); 57 | v.push_back(head->val); 58 | } 59 | 60 | vector printListFromTailToHead(ListNode *head) 61 | { 62 | if(head==nullptr) 63 | return v; 64 | printListFromTailToHead_repeat(head); 65 | return v; 66 | } 67 | 68 | // 剑指 Offer 06. 从尾到头打印链表 69 | // 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。 70 | // 示例 1: 71 | // 输入:head = [1,3,2] 72 | // 输出:[2,3,1] 73 | 74 | // 2021年04月24日15:22:50 75 | // 准备第一次社招 76 | // 思路: 77 | // 后序遍历, 很简单, 不多说 78 | 79 | /** 80 | * Definition for singly-linked list. 81 | * struct ListNode { 82 | * int val; 83 | * ListNode *next; 84 | * ListNode(int x) : val(x), next(NULL) {} 85 | * }; 86 | */ 87 | class Solution { 88 | public: 89 | vector v; 90 | void reversePrint_repeat(ListNode* head){ 91 | if(head==nullptr) 92 | return; 93 | reversePrint_repeat(head->next); 94 | v.push_back(head->val); 95 | } 96 | vector reversePrint(ListNode* head) { 97 | if(head==nullptr) 98 | return v; 99 | reversePrint_repeat(head); 100 | return v; 101 | } 102 | }; -------------------------------------------------------------------------------- /leetcode/sword/链表-删除链表中重复的结点.cpp: -------------------------------------------------------------------------------- 1 | //page 122 2 | #include 3 | // #include 4 | // #include 5 | using namespace std; 6 | 7 | struct ListNode 8 | { 9 | int val; 10 | struct ListNode *next; 11 | ListNode(int x) : val(x), next(NULL) 12 | { 13 | } 14 | }; 15 | 16 | class Solution 17 | { 18 | public: 19 | ListNode *deleteDuplication(ListNode *pHead) 20 | { 21 | if (pHead == nullptr || pHead->next == nullptr) 22 | return pHead; 23 | 24 | //新建一个辅助节点来处理头节点也是重复节点的情况 25 | ListNode *pNew = new ListNode(0); 26 | pNew->next = pHead; 27 | pHead = pNew; 28 | 29 | ListNode *pFront = pHead; 30 | ListNode *pNode = pHead->next; 31 | 32 | while (pNode != nullptr) 33 | { 34 | if (pNode->next != nullptr && pNode->val == pNode->next->val) 35 | { 36 | while (pNode->next != nullptr && pNode->val == pNode->next->val) 37 | { 38 | pNode = pNode->next; 39 | } 40 | if (pNode->next == nullptr) 41 | { 42 | pFront->next = nullptr; 43 | return pHead->next; 44 | } 45 | else if (pNode->val != pNode->next->val) 46 | { 47 | pFront->next = pNode->next; 48 | pNode = pNode->next; 49 | } 50 | } 51 | else 52 | { 53 | pFront = pNode; 54 | pNode = pNode->next; 55 | } 56 | } 57 | return pHead->next; 58 | } 59 | }; 60 | 61 | // 递归 62 | ListNode* deleteDuplication_2(ListNode* pHead) 63 | { 64 | if (pHead == nullptr || pHead->next == nullptr) 65 | return pHead; 66 | 67 | if (pHead->val == pHead->next->val) 68 | { 69 | ListNode* node = pHead->next; //当前开始重复的节点用pHead标记 70 | while (node != nullptr && node->val == pHead->val) 71 | node = node->next; 72 | 73 | //循环出来以后node指向下一个不重复的点,下一步从这个节点开始继续查找重复的节点 74 | return deleteDuplication_2(node); 75 | } 76 | else 77 | { 78 | //如果这个节点和下一个节点不重复,则继续从下一个节点开始找重复的 79 | pHead->next = deleteDuplication_2(pHead->next); 80 | return pHead; 81 | } 82 | } 83 | }; -------------------------------------------------------------------------------- /leetcode/sword/链表-删除链表的节点.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 18. 删除链表的节点 2 | // 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 3 | // 返回删除后的链表的头节点。 4 | // 注意:此题对比原题有改动 5 | 6 | // 示例 1: 7 | // 输入: head = [4,5,1,9], val = 5 8 | // 输出: [4,1,9] 9 | // 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 10 | 11 | // 示例 2: 12 | // 输入: head = [4,5,1,9], val = 1 13 | // 输出: [4,5,9] 14 | // 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9. 15 | 16 | // 说明: 17 | // 题目保证链表中节点的值互不相同 18 | // 若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点 19 | 20 | // -------------------第三次刷----------------------- 21 | // 2021年5月11日14:52:37 22 | // 准备第一次社招 23 | // 思路: 辅助节点+双指针就好了吧 24 | // 注意事项: 链表的删除需要用到辅助节点以防止头节点就是需要删除的节点 25 | struct ListNode { 26 | int val; 27 | ListNode *next; 28 | ListNode(int x) : val(x), next(NULL) {} 29 | }; 30 | 31 | class Solution { 32 | public: 33 | ListNode* deleteNode(ListNode* head, int val) { 34 | if(head == nullptr) 35 | return nullptr; 36 | 37 | ListNode* p0 = new ListNode(0); 38 | p0->next = head; 39 | 40 | ListNode* p1 = p0; 41 | ListNode* p2 = p0->next; 42 | 43 | while(p2->val!=val && p2!=nullptr){//访问->val的时候需要保证非空 44 | p1=p1->next; 45 | p2=p2->next; 46 | } 47 | p1->next = p2->next;//执行删除节点 48 | return p0->next;//辅助节点的后一个节点是真正的头结点 49 | } 50 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-不同的二叉搜索树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 96. 不同的二叉搜索树 7 | // 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 8 | 9 | // 示例 1: 10 | // 输入:n = 3 11 | // 输出:5 12 | 13 | // 示例 2: 14 | // 输入:n = 1 15 | // 输出:1 16 | 17 | // 二叉搜索树定义: 18 | // 1.若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 19 | // 2.若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 20 | // 3.它的左右子树也为二叉搜索树 21 | // 4.二叉搜索树「中序遍历」得到的值构成的序列一定是升序的 22 | 23 | // -------------------第二次刷----------------------- 24 | // 2021年6月17日10:37:26 25 | // 准备第一次社招 26 | // 思路: dp 27 | // 给定一个有序序列 1⋯n,为了构建出一棵二叉搜索树,我们可以遍历每个数字 i,将该数字作为树根,将 1⋯(i−1) 28 | // 序列作为左子树,将 (i+1)⋯n 序列作为右子树。接着我们可以按照同样的方式递归构建左子树和右子树 29 | 30 | // G(n): 长度为 n 的序列能构成的不同二叉搜索树的个数。 31 | // F(i, n): 以 i 为根、序列长度为 n 的不同二叉搜索树个数 (1≤i≤n)。 32 | // G(n)=F(1, n)+F(2, n)+...F(n, n) 33 | // F(i, n)=G(i-1)*G(n-i) 34 | // G(n)=G(1-1)*G(n-1) + G(2-1)*G(n-2) ...+ G(n-1)*G(n-n) = G(0)*G(n-1) + G(1)*G(n-2) ...+ G(n-1)*G(0) 35 | class Solution { 36 | public: 37 | int numTrees(int n) { 38 | vector G(n + 1, 0); 39 | G[0] = 1; 40 | G[1] = 1; 41 | 42 | for (int n_i = 2; n_i <= n; n_i++) { 43 | for (int j = 1; j <= n_i; j++) { 44 | G[n_i] += G[j - 1] * G[n_i - j]; 45 | } 46 | } 47 | return G[n]; 48 | } 49 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-二叉树展开为链表.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 114. 二叉树展开为链表 6 | // 给你二叉树的根结点 root ,请你将它展开为一个单链表: 7 | // 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 8 | // 展开后的单链表应该与二叉树 先序遍历 顺序相同。 9 | 10 | // 示例 1: 11 | // 输入:root = [1,2,5,3,4,null,6] 12 | // 输出:[1,null,2,null,3,null,4,null,5,null,6] 13 | 14 | // 示例 2: 15 | // 输入:root = [] 16 | // 输出:[] 17 | 18 | // 示例 3: 19 | // 输入:root = [0] 20 | // 输出:[0] 21 | 22 | struct TreeNode { 23 | int val; 24 | TreeNode* left; 25 | TreeNode* right; 26 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 27 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 28 | TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {} 29 | }; 30 | 31 | // 2021年6月18日10:25:35 32 | // 准备第一次社招 33 | // 思路1: 先序遍历保存后再换成链表. 有点无脑 34 | class Solution { 35 | public: 36 | vector v; 37 | void flatten_repeat(TreeNode* root) { 38 | v.push_back(root); 39 | if (root->left) 40 | flatten_repeat(root->left); 41 | if (root->right) 42 | flatten_repeat(root->right); 43 | } 44 | void flatten(TreeNode* root) { 45 | if (root == nullptr) 46 | return; 47 | flatten_repeat(root); 48 | 49 | TreeNode* pre=nullptr; 50 | TreeNode* cur = nullptr; 51 | for (int i = 1; i < v.size(); i++) { 52 | pre = v.at(i - 1); 53 | cur = v.at(i); 54 | pre->left = nullptr; 55 | pre->right = cur; 56 | } 57 | 58 | } 59 | }; 60 | 61 | // 2021年6月18日10:25:35 62 | // 准备第一次社招 63 | // 思路2: 原地转变. 类似链表反转. 需要多看看 64 | class Solution { 65 | void flatten(TreeNode* root) { 66 | if (root == nullptr) 67 | return; 68 | //1.先变左边 69 | flatten(root->left); 70 | //2.将root的左右都设置好. 记得先保存root->right 71 | TreeNode* old_right = root->right; 72 | root->right = root->left; 73 | root->left = nullptr; 74 | //3.转变右边 75 | flatten(old_right); 76 | //4.找到刚才left的最右边, 然后接上3刚转好的右边. 即root的左子树的链表尾接的是右子树的头 77 | while (root->right != nullptr) { 78 | root = root->right; 79 | } 80 | root->right = old_right; 81 | } 82 | } -------------------------------------------------------------------------------- /leetcode/二叉树-二叉树的直径.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 543. 二叉树的直径 6 | // 给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。 7 | // 这条路径可能穿过也可能不穿过根结点。 8 | // 示例 : 9 | // 给定二叉树 10 | // 1 11 | // / \ 12 | // 2 3 13 | // / \ 14 | // 4 5 15 | // 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 16 | // 注意:两结点之间的路径长度是以它们之间边的数目表示。 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年7月19日14:35:50 20 | // 准备第一次社招 21 | // 思路: 求直径(任意两个节点路径长度的最大值), 那么就遍历所有的node, 找到其left node和right node深度后累加即为该node 22 | // 相关的最大直径. 那么其实就是合并了两道题, 一题是求每个node的深度, 另外一题是遍历node找最大值 23 | 24 | struct TreeNode { 25 | int val; 26 | TreeNode* left; 27 | TreeNode* right; 28 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 29 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 30 | TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {} 31 | }; 32 | 33 | class Solution { 34 | int maxd = 0; 35 | 36 | public: 37 | int diameterOfBinaryTree(TreeNode* root) { 38 | depth(root); 39 | return maxd; 40 | } 41 | int depth(TreeNode* node) { 42 | if (node == nullptr) { 43 | return 0; 44 | } 45 | int Left = depth(node->left); 46 | int Right = depth(node->right); 47 | maxd = max(Left + Right, maxd); //将每个节点最大直径(左子树深度+右子树深度)当前最大值比较并取大者 48 | return max(Left, Right) + 1; //返回节点深度 49 | } 50 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-合并二叉树.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | struct TreeNode { 6 | int val; 7 | TreeNode* left; 8 | TreeNode* right; 9 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 10 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 11 | TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {} 12 | }; 13 | 14 | // 617. 合并二叉树 15 | // 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。 16 | // 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值, 17 | // 否则不为 NULL 的节点将直接作为新二叉树的节点。 18 | // 示例 1: 19 | // 输入: 20 | // Tree 1 Tree 2 21 | // 1 2 22 | // / \ / \ 23 | // 3 2 1 3 24 | // / \ \ 25 | // 5 4 7 26 | // 输出: 27 | // 合并后的树: 28 | // 3 29 | // / \ 30 | // 4 5 31 | // / \ \ 32 | // 5 4 7 33 | // 注意: 合并必须从两个树的根节点开始。 34 | 35 | // -------------------第二次刷----------------------- 36 | // 2021年7月20日10:53:04 37 | // 准备第一次社招 38 | // 思路: 递归即可. 具体流程看代码 39 | 40 | class Solution { 41 | public: 42 | // TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { 43 | // if (root1 == nullptr && root2 == nullptr) 44 | // return nullptr; 45 | 46 | // TreeNode* merged = new TreeNode(); 47 | // // merged->val = root1 ? root1->val : 0 + root2 ? root2->val : 0; 48 | 49 | // mergeTrees_repeat(root1, root2, merged); 50 | // return merged; 51 | // } 52 | 53 | TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { 54 | //如果有一个为nullptr,那都不用新建了,直接返回另外一个连上即可 55 | if (root1 == nullptr) { 56 | return root2; 57 | } 58 | if (root2 == nullptr) { 59 | return root1; 60 | } 61 | auto merged = new TreeNode(root1->val + root2->val); 62 | merged->left = mergeTrees(root1->left, root2->left); 63 | merged->right = mergeTrees(root1->right, root2->right); 64 | return merged; 65 | } 66 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-打家劫舍III.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | 7 | // 337. 打家劫舍 III 8 | // 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 9 | // 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类 10 | // 似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 11 | // 计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。 12 | 13 | // 示例 1: 14 | // 输入: [3,2,3,null,3,null,1] 15 | // 3 16 | // / \ 17 | // 2 3 18 | // \ \ 19 | // 3 1 20 | // 输出: 7 21 | // 解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7. 22 | 23 | // 示例 2: 24 | // 输入: [3,4,5,1,3,null,1] 25 | // 3 26 | // / \ 27 | // 4 5 28 | // / \ \ 29 | // 1 3 1 30 | // 输出: 9 31 | // 解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9. 32 | 33 | // -------------------第二次刷----------------------- 34 | // 2021年7月6日10:56:38 35 | // 准备第一次社招 36 | // 思路: 制约条件是root节点和left, right之间的二选一关系, 打劫root节点, 则需要加上left, right没有被打劫时及其下面 37 | // 子树的收益情况,那么这意味着这是一个后续遍历,深度优先。 38 | // dp[0][node]表示该node被打劫时, 该node与其子树所成的二叉树的最大值 39 | // dp[0][node] = dp[node->left][1]+dp[node->right][1]+node->val, 左右子树要不被打劫 40 | 41 | // dp[1][node]表示该node不被打劫时, 该node与其子树所成的二叉树的最大值 42 | // dp[1][node] = max(dp[node->left][0],dp[node->left][1] )+max(dp[node->right][0], dp[node->right][1]), 43 | // 左子树可以被打劫也可以不被打劫, 取最大值 44 | 45 | struct TreeNode { 46 | int val; 47 | TreeNode* left; 48 | TreeNode* right; 49 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 50 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 51 | TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {} 52 | }; 53 | class Solution { 54 | public: 55 | vector> dp; 56 | 57 | void dfs(TreeNode* root) { 58 | if (root == nullptr) 59 | return; 60 | 61 | dfs(root->left); 62 | dfs(root->right); 63 | 64 | //当前的赋值依赖于子树, 因此是后序遍历 65 | dp[0][root] = dp[1][root->left] + dp[1][root->right] + root->val; 66 | dp[1][root] = max(dp[0][root->left], dp[1][root->left]) + max(dp[0][root->right], dp[1][root->right]); 67 | } 68 | 69 | int rob(TreeNode* root) { 70 | dp.resize(2); 71 | dfs(root); 72 | return max(dp[0][root], dp[1][root]); 73 | } 74 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-把二叉搜索树转换为累加树.cpp: -------------------------------------------------------------------------------- 1 | // 538. 把二叉搜索树转换为累加树 2 | // 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree), 3 | // 使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。 4 | 5 | // 提醒一下,二叉搜索树满足下列约束条件: 6 | 7 | // 节点的左子树仅包含键 小于 节点键的节点。 8 | // 节点的右子树仅包含键 大于 节点键的节点。 9 | // 左右子树也必须是二叉搜索树。 10 | 11 | // -------------------第二次刷----------------------- 12 | // 2021年7月19日14:14:33 13 | // 准备第一次社招 14 | // 思路: 仔细读题应该蛮明确的. "等于原数中大于或等于 node.val 的值之和"不就是等于"右节点及其子节点的和". 那么这个dfs遍历 15 | // 的顺序就是右节点, node, 左节点, 也就是所谓的反序中序遍历 16 | 17 | // * Definition for a binary tree node. 18 | struct TreeNode { 19 | int val; 20 | TreeNode *left; 21 | TreeNode *right; 22 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 23 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 24 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 25 | }; 26 | 27 | class Solution { 28 | public: 29 | int sum=0; 30 | TreeNode* convertBST(TreeNode* root) { 31 | if(root!=nullptr){ 32 | convertBST(root->right); 33 | sum+=root->val; 34 | root->val =sum; 35 | convertBST(root->left); 36 | } 37 | return root; 38 | } 39 | }; -------------------------------------------------------------------------------- /leetcode/二叉树-路径总和III.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | struct TreeNode { 7 | int val; 8 | TreeNode* left; 9 | TreeNode* right; 10 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 11 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 12 | TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {} 13 | }; 14 | 15 | // 437. 路径总和 III 16 | // 给定一个二叉树,它的每个结点都存放着一个整数值。 17 | // 找出路径和等于给定数值的路径总数。 18 | // 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。 19 | // 二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。 20 | 21 | // 示例: 22 | // root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 23 | // 10 24 | // / \ 25 | // 5 -3 26 | // / \ \ 27 | // 3 2 11 28 | // / \ \ 29 | // 3 -2 1 30 | // 返回 3。和等于 8 的路径有: 31 | // 1. 5 -> 3 32 | // 2. 5 -> 2 -> 1 33 | // 3. -3 -> 11 34 | 35 | // -------------------第二次刷----------------------- 36 | // 2021年7月15日10:20:46 37 | // 准备第一次社招 38 | // 思路: 首先不要考虑从非根节点出发. 那么就比较简单的dfs即可. 然后题目说可以从非根节点出发, 39 | // 那么就第二重递归pathSum从不同的node出发进行dfs 40 | // 挺好的题解https://leetcode-cn.com/problems/path-sum-iii/solution/yi-pian-wen-zhang-jie-jue-suo-you-er-cha-smch/ 41 | 42 | class Solution { 43 | public: 44 | int count = 0; 45 | int pathSum(TreeNode *root, int targetSum) { 46 | if (!root) 47 | return 0; 48 | dfs(root, targetSum); //第一重递归, 以root为起始点查找路径 49 | pathSum(root->left, targetSum); //第二重递归, 左子树递归 50 | pathSum(root->right, targetSum); //第二重递归, 右子树递归 51 | return count; 52 | } 53 | 54 | void dfs(TreeNode *root, int sum) 55 | { 56 | if (!root) 57 | return; 58 | sum -= root->val; 59 | if (sum == 0) //注意不要return,因为不要求到叶节点结束,所以一条路径下面还可能有另一条 60 | count++; //如果找到了一个路径全局变量就+1 61 | dfs(root->left, sum); 62 | dfs(root->right, sum); 63 | } 64 | }; -------------------------------------------------------------------------------- /leetcode/位运算-比特位计数.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // -------------------第二次刷----------------------- 6 | // 2021年7月6日10:56:38 7 | // 准备第一次社招 8 | // 思路: 9 | // 动态规划 10 | // 设置dp[i]代表i的二进制位数 11 | // 数字间的关系不是直接用加减, 而是用移位. dp[i]等于dp[i右移一位]加上i的最低位是否有1 12 | // dp[i] = dp[i>>1]+i&1 13 | // dp[0] = 0; 14 | 15 | class Solution { 16 | public: 17 | vector countBits(int num) { 18 | vector bits(num + 1); 19 | for (int i = 1; i <= num; i++) { 20 | bits[i] = bits[i >> 1] + (i & 1); 21 | } 22 | return bits; 23 | } 24 | }; -------------------------------------------------------------------------------- /leetcode/位运算-汉明距离.cpp: -------------------------------------------------------------------------------- 1 | // 461. 汉明距离 2 | // 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 3 | // 给你两个整数 x 和 y,计算并返回它们之间的汉明距离。 4 | 5 | // 示例 1: 6 | // 输入:x = 1, y = 4 7 | // 输出:2 8 | // 解释: 9 | // 1 (0 0 0 1) 10 | // 4 (0 1 0 0) 11 | // ↑ ↑ 12 | // 上面的箭头指出了对应二进制位不同的位置。 13 | 14 | // 示例 2: 15 | // 输入:x = 3, y = 1 16 | // 输出:1 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年7月19日10:33:00 20 | // 准备第一次社招 21 | // 思路: 两个输入按位异或, 然后得到的结果一边右移一边与1进行按位与 22 | class Solution { 23 | public: 24 | int hammingDistance(int x, int y) { 25 | int xor_ = x ^ y; 26 | int res = 0; 27 | while (xor_ != 0) { 28 | res += xor_&1; 29 | xor_ = xor_ >> 1; 30 | } 31 | return res; 32 | } 33 | }; -------------------------------------------------------------------------------- /leetcode/图-课程表.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // 207. 课程表 7 | // 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 8 | // 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 9 | // 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。 10 | // 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。 11 | 12 | // 示例 1: 13 | // 输入:numCourses = 2, prerequisites = [[1,0]] 14 | // 输出:true 15 | // 解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。 16 | 17 | // 示例 2: 18 | // 输入:numCourses = 2, prerequisites = [[1,0],[0,1]] 19 | // 输出:false 20 | // 解释:总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。 21 | 22 | // -------------------第三次刷----------------------- 23 | // 2021年6月21日10:19:51 24 | // 准备第一次社招 25 | // 思路: 能否完成课程就是判断是否存在死锁, 你的前提是我, 我的前提是你. 本质就是求是否为一个有向无环图DAG. 26 | // 判断是否为DAG的方法就是逐一将无输入的头node的输出移除. 最后所有node都能移除则是无环的. 如果是有环的, 那么 27 | // 构成环的始终无法成为无输入的node被移除. 具体实现就是用一个vector记录每个node的输入数量, 用vector> 28 | // 记录每个node包含哪几个输出node. 逐一取出输入数量为0的node放到一个queue中, ... 29 | 30 | #include 31 | #include 32 | using namespace std; 33 | 34 | class Solution { 35 | public: 36 | bool canFinish(int numCourses, vector>& prerequisites) { 37 | vector degrees(numCourses);// 每个node的入度, 也就是node的输入数量 38 | vector> adjacents(numCourses);// 每个node的包含哪几个输出node 39 | for(int i=0;i q; 45 | int num = 0; 46 | for(int i=0;i 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | 7 | // 49. 字母异位词分组 8 | // 给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 9 | 10 | // 示例: 11 | // 输入: ["eat", "tea", "tan", "ate", "nat", "bat"] 12 | // 输出: 13 | // [ 14 | // ["ate","eat","tea"], 15 | // ["nat","tan"], 16 | // ["bat"] 17 | // ] 18 | 19 | // 说明: 20 | // 所有输入均为小写字母。 21 | // 不考虑答案输出的顺序。 22 | 23 | // -------------------第二次刷----------------------- 24 | // 2021年6月10日10:30:53 25 | // 准备第一次社招 26 | // 思路: 给字符串重新排序, 那么所有异位词都变成一样的了, 可将排序后的作为key, 排序前的作为value vector中的一个 27 | 28 | class Solution { 29 | public: 30 | vector> groupAnagrams(vector& strs) { 31 | unordered_map> map; 32 | for (string& str : strs) { 33 | string key = str; 34 | sort(key.begin(), key.end());//排序后为一样 35 | map[key].push_back(str); 36 | } 37 | vector> result; 38 | for (auto it = map.begin(); it != map.end(); it++) { 39 | result.push_back(it->second); 40 | } 41 | return result; 42 | } 43 | }; -------------------------------------------------------------------------------- /leetcode/字符串-括号的生成.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 22. 括号生成 7 | // 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 8 | 9 | // 示例 1: 10 | // 输入:n = 3 11 | // 输出:["((()))","(()())","(())()","()(())","()()()"] 12 | 13 | // 示例 2: 14 | // 输入:n = 1 15 | // 输出:["()"] 16 | 17 | // -------------------第二次刷----------------------- 18 | // 2021年6月9日10:45:22 19 | // 准备第一次社招 20 | // 思路: 21 | // 回溯法 22 | // 如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号 23 | class Solution { 24 | public: 25 | void generateParenthesis_repeat(vector& result, string& str, int n, int left, int right) { 26 | if (left == n && right == n) { 27 | result.push_back(str); 28 | return; 29 | } 30 | 31 | if (left < n) { 32 | str.push_back('('); 33 | generateParenthesis_repeat(result, str, n, left + 1, right); 34 | str.pop_back(); 35 | } 36 | if (right < left) { 37 | str.push_back(')'); 38 | generateParenthesis_repeat(result, str, n, left, right + 1); 39 | str.pop_back(); 40 | } 41 | } 42 | 43 | vector generateParenthesis(int n) { 44 | vector result; 45 | string str; 46 | generateParenthesis_repeat(result, str, n, 0, 0); 47 | return result; 48 | } 49 | }; -------------------------------------------------------------------------------- /leetcode/字符串-无重复字符的最长子串.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | // 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 8 | // 示例 1: 9 | // 输入: "abcabcbb" 10 | // 输出: 3 11 | // 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 12 | // 示例 2: 13 | // 输入: "bbbbb" 14 | // 输出: 1 15 | // 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 16 | // 示例 3: 17 | // 输入: "pwwkew" 18 | // 输出: 3 19 | // 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 20 | //   请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 21 | 22 | // -------------------第二次刷----------------------- 23 | // 2021年7月21日15:49:27 24 | // 准备第一次社招 25 | // 思路: 26 | // 思路一: 非常基础的滑动窗口, left和right作为两边, map作为记录窗口内的元素. 循环right往右移, 出现重复则从left 27 | // 开始删除直到没有重复. 28 | // 滑动窗口的题目 29 | // 3. 无重复字符的最长子串 30 | // 30. 串联所有单词的子串 31 | // 76. 最小覆盖子串 32 | // 159. 至多包含两个不同字符的最长子串 33 | // 209. 长度最小的子数组 34 | // 239. 滑动窗口最大值 35 | // 567. 字符串的排列 36 | // 632. 最小区间 37 | // 727. 最小窗口子序列 38 | 39 | class Solution { 40 | public: 41 | // 思路一 42 | int lengthOfLongestSubstring(string s) { 43 | int left = 0; 44 | unordered_map map; 45 | int maxlength=0; 46 | for(int right=0; rightmaxlength) 74 | maxlength = length; 75 | start = start + result + 1;//从第一个重复的的下一个位置开始,result是重复字符在子串的位置因此需要加上start 76 | } 77 | end ++; 78 | } 79 | return maxlength; 80 | } 81 | }; 82 | 83 | 84 | int main() 85 | { 86 | Solution s; 87 | int a = s.lengthOfLongestSubstring(" "); 88 | return 0; 89 | } -------------------------------------------------------------------------------- /leetcode/字符串-最长回文子串.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | // 5. 最长回文子串 7 | // 给你一个字符串 s,找到 s 中最长的回文子串。 8 | 9 | // 示例 1: 10 | // 输入:s = "babad" 11 | // 输出:"bab" 12 | // 解释:"aba" 同样是符合题意的答案。 13 | 14 | // 示例 2: 15 | // 输入:s = "cbbd" 16 | // 输出:"bb" 17 | 18 | // 示例 3: 19 | // 输入:s = "a" 20 | // 输出:"a" 21 | 22 | // 示例 4: 23 | // 输入:s = "ac" 24 | // 输出:"a" 25 | 26 | // 提示: 27 | // 1 <= s.length <= 1000 28 | // s 仅由数字和英文字母(大写和/或小写)组成 29 | 30 | // -------------------第二次刷----------------------- 31 | // 2021年6月7日16:12:04 32 | // 准备第一次社招 33 | // 思路1: 中心向两端扩散的思路. 注意回文子串不一定是奇数, 也可能是偶数 34 | 35 | // 思路2: 因为有递推关系, 所以也可以考虑动态规划. dp[i][j]表示从索引i为起始, 终点索引为j的子串是否是. 注意这里先两层循环先是长度, 因为长子串结果依赖于短子串 36 | class Solution { 37 | public: 38 | string longestPalindrome(string s) { 39 | int n = s.size(); 40 | vector> dp(n, vector(n)); 41 | string ans; 42 | for (int l = 0; l < n; ++l) {//从0开始遍历长度l 43 | for (int i = 0; i + l < n; ++i) {//从0开始遍历起始位置i 44 | int j = i + l; 45 | if (l == 0) { 46 | dp[i][j] = 1;//长度为0时就是单个字母,属于回文 47 | } else if (l == 1) {//长度为1代表两个字母 48 | dp[i][j] = (s[i] == s[j]); 49 | } else { 50 | dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]); 51 | } 52 | if (dp[i][j] && l + 1 > ans.size()) { 53 | ans = s.substr(i, l + 1); 54 | } 55 | } 56 | } 57 | return ans; 58 | } 59 | }; -------------------------------------------------------------------------------- /leetcode/字符串-有效的括号.cpp: -------------------------------------------------------------------------------- 1 | // 20. 有效的括号 2 | // 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 3 | // 有效字符串需满足: 4 | // 左括号必须用相同类型的右括号闭合。 5 | // 左括号必须以正确的顺序闭合。 6 | 7 | // 示例 1: 8 | // 输入:s = "()" 9 | // 输出:true 10 | 11 | // 示例 2: 12 | // 输入:s = "()[]{}" 13 | // 输出:true 14 | 15 | // 示例 3: 16 | // 输入:s = "(]" 17 | // 输出:false 18 | 19 | // 示例 4: 20 | // 输入:s = "([)]" 21 | // 输出:false 22 | 23 | // 示例 5: 24 | // 输入:s = "{[]}" 25 | // 输出:true 26 | 27 | // -------------------第二次刷----------------------- 28 | // 2021年6月8日15:55:01 29 | // 准备第一次社招 30 | // 思路: 用stack来做, 遍历, 如果遇到是左括号, 则无脑push到stack, 如果是右括号, 则判断stack的情况, 如果stk的栈顶不是对应的左括号或者栈是空的则return false 31 | // 注意可能的情况, 不一定是嵌套的{[]}, 可能是前后成对的{}[] 32 | 33 | #include 34 | #include 35 | #include 36 | using namespace std; 37 | 38 | class Solution 39 | { 40 | public: 41 | bool isValid(string s) 42 | { 43 | if (s.size() % 2 != 0) 44 | return false; 45 | 46 | //用map来加快判断是否为右括号和是否对应 47 | unordered_map pairs = {{')', '('}, {']', '['}, {'}', '{'}}; 48 | stack stk; 49 | for (char c : s) 50 | { 51 | if (pairs.count(c)) //如果是右括号 52 | { 53 | if (stk.empty() || stk.top() != pairs[c]) //如果stk的栈顶不是对应的左括号或者栈是空的 54 | { 55 | return false; 56 | } 57 | stk.pop(); 58 | } 59 | else 60 | { 61 | stk.push(c); //入栈 62 | } 63 | } 64 | if (stk.empty()) //循环完后栈是空的才是正确的 65 | return true; 66 | else 67 | { 68 | return false; 69 | } 70 | } 71 | }; -------------------------------------------------------------------------------- /leetcode/字符串-电话号码的字母组合.cpp: -------------------------------------------------------------------------------- 1 | // 17. 电话号码的字母组合 2 | // 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 3 | // 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 4 | 5 | // 示例 1: 6 | // 输入:digits = "23" 7 | // 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"] 8 | 9 | // 示例 2: 10 | // 输入:digits = "" 11 | // 输出:[] 12 | 13 | // 示例 3: 14 | // 输入:digits = "2" 15 | // 输出:["a","b","c"] 16 | 17 | // -------------------第二次刷----------------------- 18 | // 2021年6月8日15:55:01 19 | // 准备第一次社招 20 | // 思路: 回溯即可. 21 | 22 | #include 23 | #include 24 | #include 25 | using namespace std; 26 | 27 | class Solution { 28 | public: 29 | void DFS(string& digits, int index, string& str, vector& result, unordered_map& phoneMap) { 30 | if (index == digits.size()) { 31 | result.push_back(str); 32 | return; 33 | } 34 | 35 | string ss = phoneMap[digits[index]]; 36 | for (const char& s : ss) { 37 | str += s; 38 | DFS(digits, index + 1, str, result, phoneMap); 39 | str.pop_back(); 40 | } 41 | } 42 | 43 | vector letterCombinations(string digits) { 44 | vector result; 45 | string str; 46 | unordered_map phoneMap{{'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"}, 47 | {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"}}; 48 | 49 | if (digits.size() == 0) 50 | return result; 51 | 52 | DFS(digits, 0, str, result, phoneMap); 53 | return result; 54 | } 55 | }; 56 | 57 | int main() { 58 | Solution s; 59 | s.letterCombinations("23"); 60 | return 0; 61 | } -------------------------------------------------------------------------------- /leetcode/数组-三数之和.cpp: -------------------------------------------------------------------------------- 1 | // 15. 三数之和 2 | // 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。 3 | // 注意:答案中不可以包含重复的三元组。 4 | // 示例 1: 5 | // 输入:nums = [-1,0,1,2,-1,-4] 6 | // 输出:[[-1,-1,2],[-1,0,1]] 7 | 8 | // 示例 2: 9 | // 输入:nums = [] 10 | // 输出:[] 11 | 12 | // 示例 3: 13 | // 输入:nums = [0] 14 | // 输出:[] 15 | 16 | // -------------------第二次刷----------------------- 17 | // 2021年6月8日14:58:36 18 | // 准备第一次社招 19 | // 思路: 排序加头尾指针. 从左到右遍历确定第一个数i, 然后在i后面的的头尾确定第二第三个数. 20 | // 注意: 为了不出现重复数, 先排序, 然后每次移动三个数中任意一个数都需要确保后面不相等 21 | 22 | #include 23 | #include 24 | using namespace std; 25 | 26 | class Solution { 27 | public: 28 | vector> threeSum(vector& nums) { 29 | vector> result; 30 | if(nums.size()<3) 31 | return result; 32 | 33 | sort(nums.begin(),nums.end()); 34 | 35 | int i=0; 36 | while(i0) 40 | break; //提前终止循环 41 | 42 | int left = i+1; 43 | int right = nums.size()-1; 44 | while(left0) 49 | right--; 50 | else 51 | { 52 | result.push_back({nums[i],nums[left],nums[right]}); 53 | while(left=0&&nums[right]==nums[right-1]) 56 | right--; 57 | left++; 58 | right--; 59 | } 60 | } 61 | // 避免nums[i]作为第一个数重复出现 62 | while(i+1 v = {0,0,0,0}; 75 | s.threeSum(v); 76 | return 0; 77 | } -------------------------------------------------------------------------------- /leetcode/数组-下一个排列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 31. 下一个排列 6 | // 实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。 7 | // 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 8 | // 必须 原地 修改,只允许使用额外常数空间。 9 | 10 | // 示例 1: 11 | // 输入:nums = [1,2,3] 12 | // 输出:[1,3,2] 13 | 14 | // 示例 2: 15 | // 输入:nums = [3,2,1] 16 | // 输出:[1,2,3] 17 | 18 | // 示例 3: 19 | // 输入:nums = [1,1,5] 20 | // 输出:[1,5,1] 21 | 22 | // 示例 4: 23 | // 输入:nums = [1] 24 | // 输出:[1] 25 | 26 | // -------------------第二次刷----------------------- 27 | // 2021年6月9日10:45:22 28 | // 准备第一次社招 29 | // 思路: 基本思路肯定是某两个数交换. 关键在于如何确定这两个数. 因为数值要变大, 所以左边被交换到右边的数a[i]肯定是小于右边的某个数a[j]. 30 | // 同时又因为求下一个排列, 所以a[i]要尽量靠近右边. 因此a[i]就是从右往前第一个非降序的数. 即a[i]a[i]>a[j+1], 因此swap(a[i], a[j])后右边仍然为降序. 因此只需要reverse即等于sort 34 | class Solution { 35 | public: 36 | void nextPermutation(vector& nums) { 37 | int i = nums.size() - 2; 38 | // 步骤1:从后向前查找第一个顺序对 (i,i+1),满足 a[i] < a[i+1]。「较小数」即为 a[i] 39 | while (i >= 0 && nums[i] >= nums[i + 1]) 40 | i--; 41 | // 步骤2:在区间 [i+1,n) 中从后向前查找第一个元素 j 满足 a[i] < a[j]。较大数即为 a[j] 42 | if (i >= 0) { //如果在步骤 1 找不到顺序对,说明当前序列已经是一个降序序列,即最大的序列,我们直接跳过步骤 2 43 | //执行步骤 3,即可得到最小的升序序列 44 | int j = nums.size() - 1; 45 | while (j >= 0 && nums[i] >= nums[j]) 46 | j--; 47 | swap(nums[i], nums[j]); 48 | } 49 | // 步骤3:反转区间 [i+1,n)使其变为升序,而无需对该区间进行排序 50 | reverse(nums.begin() + 1 + i, nums.end()); 51 | } 52 | }; -------------------------------------------------------------------------------- /leetcode/数组-两数之和.cpp: -------------------------------------------------------------------------------- 1 | // 1. 两数之和 2 | // 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 3 | // 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 4 | // 你可以按任意顺序返回答案。 5 | 6 | // 示例 1: 7 | // 输入:nums = [2,7,11,15], target = 9 8 | // 输出:[0,1] 9 | // 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。 10 | 11 | // 示例 2: 12 | // 输入:nums = [3,2,4], target = 6 13 | // 输出:[1,2] 14 | 15 | // 示例 3: 16 | // 输入:nums = [3,3], target = 6 17 | // 输出:[0,1] 18 | 19 | // -------------------第二次刷----------------------- 20 | // 2021年06月06日20:50:26 21 | // 准备第一次社招 22 | // 思路一: 哈希表, 遍历一遍建立值和索引的map,然后再遍历并从map中找是否有差值. 时间复杂度为O(N), 查表时间为O(1) 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | using namespace std; 29 | class Solution { 30 | public: 31 | vector twoSum(vector& nums, int target) { 32 | unordered_map map; 33 | vector v; 34 | for(int i=0;i 2 | #include 3 | 4 | using namespace std; 5 | 6 | // 152. 乘积最大子数组 7 | // 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 8 | 9 | // 示例 1: 10 | // 输入: [2,3,-2,4] 11 | // 输出: 6 12 | // 解释: 子数组 [2,3] 有最大乘积 6。 13 | 14 | // 示例 2: 15 | // 输入: [-2,0,-1] 16 | // 输出: 0 17 | // 解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。 18 | 19 | // -------------------第二次刷----------------------- 20 | // 2021年06月20日09:39:52 21 | // 准备第一次社招 22 | // 思路: 和最大和数组不一样, 因为是整数, 所以数值上来说乘的越多乘积和越大. 但是分了正负, 所以 23 | // 每次得记录正的乘积和与负的乘积和 24 | 25 | class Solution { 26 | public: 27 | int maxProduct(vector& nums) { 28 | int imax = INT32_MIN; 29 | int p_max = 1; 30 | int n_min = 1; 31 | for (int i = 0; i < nums.size(); i++) { 32 | if (nums[i] < 0) 33 | swap(p_max, n_min); 34 | p_max = max(p_max * nums[i], nums[i]); 35 | n_min = min(n_min * nums[i], nums[i]); 36 | 37 | imax = max(imax, p_max); 38 | } 39 | return imax; 40 | } 41 | }; -------------------------------------------------------------------------------- /leetcode/数组-买卖股票的最佳时机.cpp: -------------------------------------------------------------------------------- 1 | // 剑指 Offer 63. 股票的最大利润 2 | // 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少? 3 | 4 | // 示例 1: 5 | // 输入: [7,1,5,3,6,4] 6 | // 输出: 5 7 | // 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 8 | // 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 9 | 10 | // 示例 2: 11 | // 输入: [7,6,4,3,1] 12 | // 输出: 0 13 | // 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 14 | 15 | // -------------------第三次刷----------------------- 16 | // 2021年6月3日10:30:16 17 | // 准备第一次社招 18 | // 思路: 19 | // 如果在扫描到数组中的第i个数字时,只要我们能够记住之前的i-1个数字中的最小值, 20 | // 就能算出在当前价位卖出时可能得到的最大利润。 21 | #include 22 | using namespace std; 23 | 24 | class Solution 25 | { 26 | public: 27 | int maxProfit(vector &prices) 28 | { 29 | if(!prices.size()) 30 | return -1; 31 | 32 | int minBefore = prices[0]; 33 | int maxResult = INT32_MIN; 34 | for(int i = 1; i< prices.size(); i++) 35 | { 36 | int tempMaxResult = prices[i] - minBefore; 37 | if(tempMaxResult > maxResult) 38 | maxResult = tempMaxResult; 39 | 40 | if(prices[i] B -> (待命) -> A -> B -> (待命) -> A -> B 11 | // 在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。 12 | 13 | // 示例 2: 14 | // 输入:tasks = ["A","A","A","B","B","B"], n = 0 15 | // 输出:6 16 | // 解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0 17 | // ["A","A","A","B","B","B"] 18 | // ["A","B","A","B","A","B"] 19 | // ["B","B","B","A","A","A"] 20 | // ... 21 | // 诸如此类 22 | 23 | // 示例 3: 24 | // 输入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2 25 | // 输出:16 26 | // 解释:一种可能的解决方案是: 27 | // A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A 28 | 29 | // -------------------第二次刷----------------------- 30 | // 2021年7月20日10:53:04 31 | // 准备第一次社招 32 | // 思路: 同一类任务放一列, 执行顺序是行主序, 最少是n列以此保证同一类任务的冷却时间(最后一行不需要考虑冷却). 行数取决于 33 | // 最大同类型任务的数量. 最后一行所需的时间取决于最长时间任务的数量, 那么所需时间就是cnt+(n+1)*(v[0]-1). 但是可能总任务 34 | // 数量大于这个结果值(此时多出来的任务可以竖着插在右边), 那么需要求两者的最大值 35 | 36 | // 这个题解非常不错https://leetcode-cn.com/problems/task-scheduler/solution/tong-zi-by-popopop/ 37 | 38 | #include 39 | #include 40 | using namespace std; 41 | 42 | class Solution { 43 | public: 44 | int leastInterval(vector& tasks, int n) { 45 | // 求最大同类任务的数量作为行数 46 | vector v(26); 47 | for(char c : tasks) 48 | v[c-'A'] ++; 49 | sort(v.begin(), v.end(), [](int& x, int& y){return x>y;}); 50 | 51 | // 求最长时间任务的数量, 用于计算最后一行的时间 52 | int cnt=1; 53 | while(cnt 2 | #include 3 | using namespace std; 4 | 5 | // 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 6 | // 示例 1: 7 | // 输入:nums = [1,2,3] 8 | // 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 9 | 10 | // 示例 2: 11 | // 输入:nums = [0,1] 12 | // 输出:[[0,1],[1,0]] 13 | 14 | // 示例 3: 15 | // 输入:nums = [1] 16 | // 输出:[[1]] 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年06月09日22:50:52 20 | // 准备第一次社招 21 | // 思路: 回溯遍历, 因为不能重复, 所以用used_index来保存 22 | 23 | class Solution { 24 | public: 25 | void permute_repeat(vector& nums, vector& used_index, vector>& result_vv, 26 | vector& result_v) { 27 | if (used_index.size() == nums.size()) { 28 | result_vv.push_back(result_v); 29 | return; 30 | } 31 | 32 | for (int i = 0; i < nums.size(); i++) { 33 | // 要求选取不能重复可以用一个used_index,要求组合不能重复则可以用一个index然后循环要从new_index要>=old_index 34 | // if (find(used_index.begin(), used_index.end(), i) == used_index.end()) { 35 | if(used_index[i] == 0){ 36 | // used_index.push_back(i); 37 | used_index[i] = 1; 38 | result_v.push_back(nums[i]); 39 | permute_repeat(nums, used_index, result_vv, result_v); 40 | // used_index.pop_back(); 41 | used_index[i] = 0; 42 | result_v.pop_back(); 43 | } 44 | } 45 | } 46 | 47 | vector> permute(vector& nums) { 48 | vector> result_vv; 49 | vector result_v; 50 | vector used_index(nums.size(), 0); 51 | permute_repeat(nums, used_index, result_vv, result_v); 52 | return result_vv; 53 | } 54 | }; -------------------------------------------------------------------------------- /leetcode/数组-分割等和子集.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 416. 分割等和子集 6 | // 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 7 | 8 | // 示例 1: 9 | // 输入:nums = [1,5,11,5] 10 | // 输出:true 11 | // 解释:数组可以分割成 [1, 5, 5] 和 [11] 。 12 | 13 | // 示例 2: 14 | // 输入:nums = [1,2,3,5] 15 | // 输出:false 16 | // 解释:数组不能分割成两个元素和相等的子集。 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年7月15日10:20:46 20 | // 准备第一次社招 21 | // 思路: 22 | 23 | //动态规划 24 | //令dp[i][j]代表数组nums[0:i]是否存在部分数的和为j, 注意是部分数的和而不是说全部数的和 25 | //要返回的结果就是dp[nums.size()-1][sum(nums)/2] 26 | 27 | // 只要nums[0]不大于target=sum(nums)/2, 初始dp[0][nums[0]] = true, 如果大于了函数可以直接返回false了 28 | // 三种情况为true: 29 | // 1.如果新增数nums[i]就等于和数j, 那么肯定能组成部分和为j. 即如果nums[i]==j, 则dp[i][j]=true 30 | // 2.如果未新增数nums[i]时就已经可以组成部分和为j, 则新增数nums[i]也肯定能组成. 即如果dp[i-1][j]==true, 则dp[i][j]=true 31 | // 3.如果未新增数nums[i]时可以组成部分和j-nums[i], 那新增数nums[i]后刚好能够组成部分和j. 即如果dp[i-1][j-nums[i]]=true, 则dp[i][j]=true 32 | 33 | class Solution { 34 | public: 35 | bool canPartition(vector& nums) { 36 | int sum = 0; 37 | for (int num : nums) 38 | sum += num; 39 | if (sum % 2 != 0) 40 | return false; 41 | int target = sum / 2; 42 | 43 | vector> dp(nums.size(), vector(target+1, false)); 44 | 45 | if (nums[0] <= target) 46 | dp[0][nums[0]] = true; 47 | 48 | for (int i = 1; i < nums.size(); i++) { 49 | for (int j = 0; j <= target; j++) { 50 | dp[i][j] = dp[i - 1][j]; 51 | 52 | if (nums[i] == j) { 53 | dp[i][j] = true; 54 | continue; 55 | } 56 | if (nums[i] < j) { 57 | dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]; 58 | } 59 | } 60 | } 61 | return dp[nums.size() - 1][target]; 62 | } 63 | }; -------------------------------------------------------------------------------- /leetcode/数组-前K个高频元素.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | // 347. 前 K 个高频元素 8 | // 给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 9 | // 示例 1: 10 | // 输入: nums = [1,1,1,2,2,3], k = 2 11 | // 输出: [1,2] 12 | 13 | // 示例 2: 14 | // 输入: nums = [1], k = 1 15 | // 输出: [1] 16 | 17 | // 提示: 18 | // 1 <= nums.length <= 105 19 | // k 的取值范围是 [1, 数组中不相同的元素的个数] 20 | // 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的 21 | // 进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。 22 | 23 | // -------------------第二次刷----------------------- 24 | // 2021年7月6日14:11:59 25 | // 准备第一次社招 26 | // 思路: 先遍历将数值和数量记录在一个map里. 然后用一个最小堆, 堆里的元素是pair. 类似于之前的滑动窗口 27 | class Solution { 28 | public: 29 | static bool cmp(pair& m, pair& n) { 30 | return m.second > n.second; 31 | } 32 | 33 | vector topKFrequent(vector& nums, int k) { 34 | unordered_map occurrences; 35 | for (auto& v : nums) { 36 | occurrences[v]++; 37 | } 38 | 39 | // pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数 40 | priority_queue, vector>, decltype(&cmp)> q(cmp); 41 | for (auto& [num, count] : occurrences) { 42 | if (q.size() == k) { 43 | if (q.top().second < count) { 44 | q.pop(); 45 | q.emplace(num, count); 46 | } 47 | } else { 48 | q.emplace(num, count); 49 | } 50 | } 51 | vector ret; 52 | while (!q.empty()) { 53 | ret.emplace_back(q.top().first); 54 | q.pop(); 55 | } 56 | return ret; 57 | } 58 | }; -------------------------------------------------------------------------------- /leetcode/数组-合并两个有序数组.cpp: -------------------------------------------------------------------------------- 1 | // 88. 合并两个有序数组 2 | // 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 3 | // 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。 4 | 5 | // 示例 1: 6 | // 输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 7 | // 输出:[1,2,2,3,5,6] 8 | 9 | // 示例 2: 10 | // 输入:nums1 = [1], m = 1, nums2 = [], n = 0 11 | // 输出:[1] 12 | 13 | #include 14 | using namespace std; 15 | 16 | // -------------------第二次刷----------------------- 17 | // 2021年7月19日15:52:25 18 | // 准备第一次社招 19 | // 思路: 双尾指针 20 | 21 | class Solution { 22 | public: 23 | void merge(vector& nums1, int m, vector& nums2, int n) { 24 | int p1 = m-1; 25 | int p2 = n-1; 26 | int p3 = m+n-1; 27 | int current; 28 | while(p1>=0||p2>=0){ 29 | if(p1==-1){ 30 | current = nums2[p2]; 31 | p2--; 32 | } 33 | else if(p2==-1){ 34 | current = nums1[p1]; 35 | p1--; 36 | } 37 | else if(nums1[p1]>nums2[p2]){ 38 | current = nums1[p1]; 39 | p1--; 40 | } 41 | else{ 42 | current = nums2[p2]; 43 | p2--; 44 | } 45 | nums1[p3] = current; 46 | p3--; 47 | } 48 | } 49 | }; -------------------------------------------------------------------------------- /leetcode/数组-合并区间.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 56. 合并区间 6 | // 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。 7 | 8 | // 示例 1: 9 | // 输入:intervals = [[1,3],[2,6],[8,10],[15,18]] 10 | // 输出:[[1,6],[8,10],[15,18]] 11 | // 解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]. 12 | 13 | // 示例 2: 14 | // 输入:intervals = [[1,4],[4,5]] 15 | // 输出:[[1,5]] 16 | // 解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年6月10日11:42:59 20 | // 准备第一次社招 21 | // 思路: 22 | // 首先,我们将列表中的区间按照左端点升序排序 23 | // 用数组 merged 存储最终的答案 24 | // 如果当前区间的左端点在数组 merged 中最后一个区间的右端点之后,那么它们不会重合,我们可以直接将这个区间加入数组 merged 25 | // 的末尾; 否则,它们重合,我们需要用当前区间的右端点更新数组 merged 中最后一个区间的右端点,将其置为二者的较大值 26 | 27 | class Solution { 28 | public: 29 | vector> merge(vector>& intervals) { 30 | vector> merged; 31 | if (intervals.size() == 0) 32 | return merged; 33 | sort(intervals.begin(), intervals.end()); 34 | 35 | merged.push_back(intervals[0]); 36 | for (int i = 1; i < intervals.size(); i++) { 37 | if (intervals[i][0] > merged.back()[1]) 38 | merged.push_back(intervals[i]); 39 | else 40 | merged.back()[1] = max(merged.back()[1], intervals[i][1]); 41 | } 42 | return merged; 43 | } 44 | }; -------------------------------------------------------------------------------- /leetcode/数组-和为K的子数组.cpp: -------------------------------------------------------------------------------- 1 | // 560. 和为K的子数组 2 | // 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。 3 | 4 | // 示例 1 : 5 | // 输入:nums = [1,1,1], k = 2 6 | // 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 7 | #include 8 | using namespace std; 9 | 10 | class Solution { 11 | public: 12 | int subarraySum(vector& nums, int k) { 13 | 14 | } 15 | }; -------------------------------------------------------------------------------- /leetcode/数组-子集.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 78. 子集 7 | // 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 8 | // 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 9 | 10 | // 示例 1: 11 | // 输入:nums = [1,2,3] 12 | // 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] 13 | 14 | // 示例 2: 15 | // 输入:nums = [0] 16 | // 输出:[[],[0]] 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年6月11日10:46:00 20 | // 准备第一次社招 21 | // 思路: 就递归遍历呗, 每次处理就两种情况, 放还是不放 22 | class Solution { 23 | public: 24 | void subsets_repeat(vector>& result, vector& result_tmp, vector& nums, int index) { 25 | if (index == nums.size()) {//该push的时候应该是index超出范围 26 | result.push_back(result_tmp); 27 | return; 28 | } 29 | 30 | // 就两种情况,把当前index对应的元素放或者不放到result_tmp里 31 | result_tmp.push_back(nums[index]); 32 | subsets_repeat(result, result_tmp, nums, index + 1); 33 | result_tmp.pop_back(); 34 | 35 | subsets_repeat(result, result_tmp, nums, index + 1); 36 | }; 37 | 38 | vector> subsets(vector& nums) { 39 | vector> result; 40 | vector result_tmp; 41 | subsets_repeat(result, result_tmp, nums, 0); 42 | return result; 43 | } 44 | }; -------------------------------------------------------------------------------- /leetcode/数组-完全平方数.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // numSquares(n)=min(numSquares(n-k) + 1) ∀k∈square numbers 6 | 7 | 8 | // 279. 完全平方数 9 | // 给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。 10 | // 给你一个整数 n ,返回和为 n 的完全平方数的 最少数量 。 11 | // 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。 12 | 13 | // 示例 1: 14 | // 输入:n = 12 15 | // 输出:3 16 | // 解释:12 = 4 + 4 + 4 17 | 18 | // 示例 2: 19 | // 输入:n = 13 20 | // 输出:2 21 | // 解释:13 = 4 + 9 22 | 23 | // -------------------第二次刷----------------------- 24 | // 2021年07月02日00:05:03 25 | // 准备第一次社招 26 | // 思路: 动态规划. dp[i]表示结果. dp[i] = min(dp[i-j*j])+1, 其中j是从0遍历到根号i找到的使dp[i-j*j]最小 27 | class Solution { 28 | public: 29 | int numSquares(int n) { 30 | vector f(n + 1); 31 | for (int i = 1; i <= n; i++) { 32 | int minn = INT_MAX; 33 | for (int j = 1; j * j <= i; j++) { 34 | minn = min(minn, f[i - j * j]); 35 | } 36 | f[i] = minn + 1; 37 | } 38 | return f[n]; 39 | } 40 | }; -------------------------------------------------------------------------------- /leetcode/数组-寻找重复数.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 287. 寻找重复数 6 | // 给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。 7 | // 假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。 8 | // 你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。 9 | 10 | // 示例 1: 11 | // 输入:nums = [1,3,4,2,2] 12 | // 输出:2 13 | 14 | // 示例 2: 15 | // 输入:nums = [3,1,3,4,2] 16 | // 输出:3 17 | 18 | // 示例 3: 19 | // 输入:nums = [1,1] 20 | // 输出:1 21 | 22 | // 示例 4: 23 | // 输入:nums = [1,1,2] 24 | // 输出:1 25 | // 提示: 26 | // 1 <= n <= 105 27 | // nums.length == n + 1 28 | // 1 <= nums[i] <= n 29 | // nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次 30 | 31 | // 进阶: 32 | // 如何证明 nums 中至少存在一个重复的数字? 33 | // 你可以设计一个线性级时间复杂度 O(n) 的解决方案吗? 34 | 35 | // -------------------第二次刷----------------------- 36 | // 2021年7月2日10:59:10 37 | // 准备第一次社招 38 | // 思路1: 数字范围都在1到n之间, 那么就考虑把一个数nums[i]准备移动到nums[nums[i]]处, 移动前先发现两者相等则代表之前已经有 39 | // 一个数移动到该处了, 代表这两数是相等的. 但是这题说不能修改nums. 那就按照之前说的先映射为链表. 入口是被两个对象指向的node. 40 | // node以及node->val都是i, node->next以及node->next->val是nums[i], node->next->next以及node->next->next->val是nums[nums[i]] 41 | // 然后用环形链表的方法来. 快慢指针找到相遇点, 然后再快慢指针p1 = head, p2 = slow找到相遇点就是入口 42 | class Solution { 43 | public: 44 | int findDuplicate(vector& nums) { 45 | int slow = 0; 46 | int fast = 0; 47 | slow = nums[slow]; 48 | fast = nums[nums[fast]]; 49 | while(slow!=fast){ 50 | slow = nums[slow]; 51 | fast = nums[nums[fast]]; 52 | } 53 | 54 | int pre1 = 0; 55 | int pre2 = slow; 56 | while(pre1 != pre2){ 57 | pre1 = nums[pre1]; 58 | pre2 = nums[pre2]; 59 | } 60 | return pre1; 61 | } 62 | }; -------------------------------------------------------------------------------- /leetcode/数组-打家劫舍.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [非常好的动态规划正规教程](https://leetcode-cn.com/problems/house-robber/solution/dong-tai-gui-hua-jie-ti-si-bu-zou-xiang-jie-cjavap/) 4 | 四步走: 5 | 1. 步骤一:定义问题 6 | dp(k)代表偷从0到k个房间所能获得的最大钱数 7 | 2. 步骤二:写出子问题的递推关系 8 | dp(k)=max(dp(k-2)+nums[k], dp(k-1)) 9 | 3. 步骤三:确定 DP 数组的计算顺序 10 | 99% 的情况我们都用自底向上的、使用 dp 数组的循环方法,即从0开始手算 11 | dp(0)=nums[0] 12 | dp(1)=max(nums[0], nums[1]) 13 | 4. 步骤四:空间优化 14 | ```c 15 | #include 16 | #include 17 | using namespace std; 18 | class Solution { 19 | public: 20 | // int imax = INT32_MIN; 21 | // void rob_repeat(vector& nums, int sum, int index) { 22 | // if (index >= nums.size()) { 23 | // imax = max(sum, imax); 24 | // } 25 | 26 | // sum += nums[index]; 27 | // rob_repeat(nums, sum, index + 2); 28 | // rob_repeat(nums, sum, index + 3); 29 | // } 30 | 31 | // int rob(vector& nums) { 32 | // rob_repeat(nums, 0, 0); 33 | // rob_repeat(nums, 0, 1); 34 | // return imax; 35 | // } 36 | 37 | 38 | int rob(vector& nums) { 39 | int N = nums.size(); 40 | if (N == 0) 41 | return 0; 42 | else if (N == 1) 43 | return nums[0]; 44 | else if (N == 2) 45 | return max(nums[0], nums[1]); 46 | 47 | vector dp(N, 0); 48 | dp[0] = nums[0]; 49 | dp[1] = max(nums[0], nums[1]); 50 | for (int k = 2; k < N; k++) 51 | dp[k] = max(dp[k - 2] + nums[k], dp[k - 1]); 52 | 53 | return dp[N - 1]; 54 | } 55 | }; 56 | ``` -------------------------------------------------------------------------------- /leetcode/数组-找到数组中消失的数字.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 448. 找到所有数组中消失的数字 6 | // 给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。 7 | 8 | // 示例 1: 9 | // 输入:nums = [4,3,2,7,8,2,3,1] 10 | // 输出:[5,6] 11 | 12 | // 示例 2: 13 | // 输入:nums = [1,1] 14 | // 输出:[2] 15 | 16 | // -------------------第二次刷----------------------- 17 | // 2021年7月19日10:33:00 18 | // 准备第一次社招 19 | // 思路: 20 | // 将数组元素对应为索引的位置加n 21 | // 遍历加n后的数组,若数组元素值小于等于n,则说明数组下标值不存在,即消失的数字 22 | 23 | class Solution { 24 | public: 25 | vector findDisappearedNumbers(vector& nums) { 26 | vector res; 27 | if (nums.empty()) 28 | return nums; 29 | for (int i = 0; i < nums.size(); i++) { 30 | int index = (nums[i] - 1) % nums.size(); //-1是因为数组元素范围是1-n,后面push的时候会+1恢复. 31 | // 取余是因为出现两次的数可能之前已经加过了 32 | nums[index] += nums.size(); 33 | } 34 | for (int i = 0; i < nums.size(); i++) { 35 | if (nums[i] <= nums.size()) 36 | res.push_back(i + 1); 37 | } 38 | return res; 39 | } 40 | }; -------------------------------------------------------------------------------- /leetcode/数组-插入区间.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | class Solution { 6 | public: 7 | vector> insert(vector>& intervals, vector& newInterval) { 8 | vector> result; 9 | // 依次扫描区间数组,分三种情况 10 | int i = 0; 11 | int n = intervals.size(); 12 | 13 | // 1.遍历区间的右端点在插入区间的左端点的左侧,代表不重合,直接push 14 | while (i < n && intervals[i][1] < newInterval[0]) { 15 | result.push_back(intervals[i]); 16 | i++; 17 | } 18 | // 2.遍历区间的左端点<=插入区间的右端点,代表重合,进行合并 19 | while (i < n && intervals[i][0] <= newInterval[1]) { 20 | newInterval[0] = min(intervals[i][0], newInterval[0]); 21 | newInterval[1] = max(intervals[i][1], newInterval[1]); 22 | i++; 23 | } 24 | result.push_back(newInterval); 25 | // 3.在右侧 26 | while (i < n && intervals[i][0] > newInterval[1]) { 27 | result.push_back(intervals[i]); 28 | i++; 29 | } 30 | 31 | return result; 32 | } 33 | }; -------------------------------------------------------------------------------- /leetcode/数组-数组中的第K个最大元素.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | using namespace std; 6 | 7 | // 215. 数组中的第K个最大元素 8 | // 在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 9 | 10 | // 示例 1: 11 | // 输入: [3,2,1,5,6,4] 和 k = 2 12 | // 输出: 5 13 | 14 | // 示例 2: 15 | // 输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 16 | // 输出: 4 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年06月13日11:15:42 20 | // 准备第一次社招 21 | // 思路: 用大小堆把数组分成两个部分. 参考求数据流的中位数 22 | 23 | class Solution { 24 | public: 25 | int findKthLargest(vector& nums, int k) { 26 | priority_queue, greater> min_heap; 27 | priority_queue, less> max_heap; 28 | 29 | for(int i=0;imin_heap.top()){ 35 | int tmp = min_heap.top(); 36 | min_heap.pop(); 37 | min_heap.push(nums[i]); 38 | max_heap.push(tmp); 39 | }else{ 40 | max_heap.push(nums[i]); 41 | } 42 | } 43 | } 44 | return min_heap.top(); 45 | } 46 | }; -------------------------------------------------------------------------------- /leetcode/数组-最佳买卖股票时机含冷冻期.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 309. 最佳买卖股票时机含冷冻期 6 | // 给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​ 7 | 8 | // 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 9 | // 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。 10 | // 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 11 | 12 | // 示例: 13 | // 输入: [1,2,3,0,2] 14 | // 输出: 3 15 | // 解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出] 16 | 17 | // 用 f[i] 表示第 i 天结束之后的「累计最大收益」 18 | // 1. 我们目前持有一支股票,对应的「累计最大收益」记为 f[i][0] 19 | // 2. 我们目前不持有任何股票,并且处于冷冻期中(第i+1天不能买),对应的「累计最大收益」记为 f[i][1] 20 | // 3. 我们目前不持有任何股票,并且不处于冷冻期中(第i+1天能买),对应的「累计最大收益」记为 f[i][2] 21 | 22 | // 对于 f[i][0] , 23 | // 1.我们目前持有的这一支股票可以是在第 i - 1 天就已经持有的,对应的状态为 f[i - 1][0] ; 24 | // 2.或者是第 i 天买入的,那么第 i - 1 天就不能持有股票并且不处于冷冻期中,对应的状态为 f[i - 1][2] 加上买入股票的负收益 25 | // prices[i] 26 | // f[i][0]=max(f[i−1][0],f[i−1][2]−prices[i]) 27 | 28 | // 对于 f[i][1], 29 | // 我们在第 i 天结束之后处于冷冻期的原因是在当天卖出了股票,那么说明在第 i-1 天时我们必须持有一支股票,对应的状态为 30 | // f[i-1][0] 加上卖出股票的正收益 prices[i] 31 | // f[i][1]=f[i−1][0]+prices[i] 32 | 33 | // 对于 f[i][2], 34 | // 我们在第 i 天结束之后不持有任何股票并且不处于冷冻期, 说明当天没有进行任何操作,即第 i-1 天时不持有任何股票, 35 | // 如果第 i-1 天处于冷冻期,对应的状态为 f[i-1][1], 如果不处于冷冻期,对应的状态为 f[i-1][2] 36 | // f[i][2]=max(f[i−1][1],f[i−1][2]) 37 | 38 | // 第零天 39 | // f[0][0]=−prices[0] 40 | // f[0][1]=0 41 | // f[0][2]=0 42 | 43 | // 结果 = max(f[n][0],f[n][1],f[n][2]) 44 | 45 | #include 46 | #include 47 | using namespace std; 48 | 49 | class Solution { 50 | public: 51 | int maxProfit(vector& prices) { 52 | int n = prices.size(); 53 | vector> dp(n, vector(3)); 54 | dp[0][0] = -prices[0]; 55 | dp[0][1] = 0; 56 | dp[0][2] = 0; 57 | for (int i = 1; i < n; ++i) { 58 | dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i]); 59 | dp[i][1] = dp[i - 1][0] + prices[i]; 60 | dp[i][2] = max(dp[i - 1][1], dp[i - 1][2]); 61 | } 62 | return max(dp[n-1][0], dp[n-1][1], dp[n-1][2]); 63 | } 64 | }; -------------------------------------------------------------------------------- /leetcode/数组-最短无序连续子数组.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 581. 最短无序连续子数组 5 | // 给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。 6 | // 请你找出符合题意的 最短 子数组,并输出它的长度。 7 | 8 | // 示例 1: 9 | // 输入:nums = [2,6,4,8,10,9,15] 10 | // 输出:5 11 | // 解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。 12 | 13 | // 示例 2: 14 | // 输入:nums = [1,2,3,4] 15 | // 输出:0 16 | 17 | // 示例 3: 18 | // 输入:nums = [1] 19 | // 输出:0 20 | 21 | // -------------------第二次刷----------------------- 22 | // 2021年7月20日10:53:04 23 | // 准备第一次社招 24 | // 思路: 25 | //从左往右遍历, 找到第一个不正常的降序的两个连续元素nums[i]>nums[i+1], 然后从i开始往右继续遍历找到最小的数min 26 | //从右往左遍历, 找到第一个不正常的降序的两个连续元素nums[i-1]>nums[i], 然后从i-1开始往左继续在乱序的数组里遍历找到最大的数max 27 | //两个异常之间肯定是乱序数组, 但是把这个乱序数组原地恢复后就一定能保证两个连接处是正常的么? 因此需要找到乱序数组里的min和max 28 | //然后重新从左往右遍历, 找到min和max正常排序应该在的位置, 即找到从左往右第一个大于min的数min_right和从右往左第一个小于max的数max_left, 29 | // 正常排序的时候min和max之间(包括这两者)组成的数组即为最短乱序数组 30 | 31 | class Solution { 32 | public: 33 | int findUnsortedSubarray(vector& nums) { 34 | bool flag = false; 35 | int mins = INT32_MAX; 36 | for(int i =0; inums[i+1]){ 38 | flag=true; 39 | mins = min(mins, min(nums[i], nums[i+1])); 40 | } 41 | 42 | if(flag==true) 43 | mins = min(mins, nums[i]); 44 | } 45 | 46 | flag = false; 47 | int maxs = INT32_MIN; 48 | for(int i =nums.size()-2; i>=0;i--){ 49 | if(nums[i+1]mins) 62 | break; 63 | } 64 | 65 | int max_left; 66 | for(max_left=nums.size()-1;max_left>=0;max_left--) 67 | { 68 | if(nums[max_left]max_left?0:max_left-min_right+1; 72 | } 73 | }; -------------------------------------------------------------------------------- /leetcode/数组-最长上升子序列.cpp: -------------------------------------------------------------------------------- 1 | // dp[i] 表示:以 nums[i] 结尾 的「上升子序列」的长度 2 | // dp[i]= max(dp[j])+1, 其中0≤j 5 | #include 6 | using namespace std; 7 | 8 | class Solution { 9 | public: 10 | int lengthOfLIS(vector& nums) { 11 | vector dp(nums.size(), 1); 12 | for (int i = 0; i < nums.size(); i++) { 13 | for (int j = 0; j < i; j++) { 14 | if (nums[j] < nums[i]) 15 | dp[i] = max(dp[i], dp[j] + 1); 16 | } 17 | } 18 | 19 | int num = INT32_MIN; 20 | for (int i = 0; i < dp.size(); i++) 21 | num = max(num, dp[i]); 22 | return num; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /leetcode/数组-最长连续序列.cpp: -------------------------------------------------------------------------------- 1 | // 128. 最长连续序列 2 | // 给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 3 | // 进阶:你可以设计并实现时间复杂度为 O(n) 的解决方案吗? 4 | 5 | // 示例 1: 6 | // 输入:nums = [100,4,200,1,3,2] 7 | // 输出:4 8 | // 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。 9 | 10 | // 示例 2: 11 | // 输入:nums = [0,3,7,2,5,8,4,6,0,1] 12 | // 输出:9 13 | 14 | // -------------------第二次刷----------------------- 15 | // 2021年6月18日11:12:32 16 | // 准备第一次社招 17 | // 思路: map的key是num, value是num所能组成的序列的长度. 如果num-1和num+1也存在, 那么新的length=left+right+1 18 | // 注意: 记得更新左序列的最左边的数的value和右序列最右边的数的value. 其中的数的value已经不重要了, 因为不会再用到 19 | 20 | #include 21 | #include 22 | using namespace std; 23 | 24 | class Solution { 25 | public: 26 | int longestConsecutive(vector& nums) { 27 | unordered_map map; 28 | int max_length = 0; 29 | for(int i = 0;i max_length) 40 | max_length = length; 41 | 42 | if(map.count(nums[i]-1)!=0) 43 | map[nums[i]-left] = length;//是最左边 44 | 45 | if(map.count(nums[i]+1)!=0) 46 | map[nums[i]+right] = length;//是最右边 47 | } 48 | 49 | } 50 | return max_length; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /leetcode/数组-根据身高重建队列.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 406. 根据身高重建队列 6 | // 假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。 7 | // 每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。 8 | // 请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue , 9 | // 其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。 10 | 11 | // 示例 1: 12 | // 输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]] 13 | // 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 14 | // 解释: 15 | // 编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。 16 | // 编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。 17 | // 编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。 18 | // 编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 19 | // 编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。 20 | // 编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。 21 | // 因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。 22 | 23 | // 示例 2: 24 | // 输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]] 25 | // 输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]] 26 | 27 | // -------------------第二次刷----------------------- 28 | // 2021年7月14日11:27:54 29 | // 准备第一次社招 30 | // 思路: 先按照身高hi升序, ki降序进行排序. 遍历这个排序后的数组放到一个空数组ans中. hi升序是保证遍历后面的大数人的时候前面已经放置了的小数人不会影响, 31 | // 只需要看前面空着的位置数量与ki相比即可. ki降序是因为前面. hi相等的数占了一个空位, 那么前面比当前hi大的空位肯定会少. 32 | class Solution { 33 | public: 34 | vector> reconstructQueue(vector>& people) { 35 | sort(people.begin(), people.end(), [](const vector& u, const vector& v) { 36 | return u[0] < v[0] || (u[0] == v[0] && u[1] > v[1]); 37 | }); 38 | int n = people.size(); 39 | vector> ans(n); 40 | for (const vector& person: people) { 41 | int spaces = person[1] + 1; 42 | for (int i = 0; i < n; ++i) { 43 | if (ans[i].empty()) {//等于空意味着这个位置以后是要放比当前值更大的数 44 | --spaces; 45 | if (!spaces) { 46 | ans[i] = person; 47 | break; 48 | } 49 | } 50 | } 51 | } 52 | return ans; 53 | } 54 | }; -------------------------------------------------------------------------------- /leetcode/数组-每日温度.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 739. 每日温度 6 | // 请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。 7 | 8 | // 示例 1: 9 | // 输入: temperatures = [73,74,75,71,69,72,76,73] 10 | // 输出: [1,1,4,2,1,1,0,0] 11 | 12 | // 示例 2: 13 | // 输入: temperatures = [30,40,50,60] 14 | // 输出: [1,1,1,0] 15 | 16 | // 示例 3: 17 | // 输入: temperatures = [30,60,90] 18 | // 输出: [1,1,0] 19 | 20 | // -------------------第二次刷----------------------- 21 | // 2021年7月21日15:49:27 22 | // 准备第一次社招 23 | // 思路: 24 | // 单调栈 25 | // 想要找一个递增的, 那就设置一个单调递减栈, 在异常pop的时候进行处理. 26 | // 以index入栈, 递减的情况把index都入栈, 直到找到异常(非递减), 逐一弹出index, 27 | // 每个r[index]都等于i-index 28 | 29 | class Solution { 30 | public: 31 | 32 | vector dailyTemperatures(vector& T) { 33 | vector r(T.size()); 34 | stack s; 35 | for(int i=0;i 2 | using namespace std; 3 | 4 | // 70. 爬楼梯 5 | // 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 6 | // 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 7 | // 注意:给定 n 是一个正整数。 8 | 9 | // 示例 1: 10 | // 输入: 2 11 | // 输出: 2 12 | // 解释: 有两种方法可以爬到楼顶。 13 | // 1. 1 阶 + 1 阶 14 | // 2. 2 阶 15 | 16 | // 示例 2: 17 | // 输入: 3 18 | // 输出: 3 19 | // 解释: 有三种方法可以爬到楼顶。 20 | // 1. 1 阶 + 1 阶 + 1 阶 21 | // 2. 1 阶 + 2 阶 22 | // 3. 2 阶 + 1 阶 23 | 24 | // -------------------第二次刷----------------------- 25 | // 2021年6月11日10:46:00 26 | // 准备第一次社招 27 | // 思路: 很简单, 没什么好说的 28 | 29 | class Solution { 30 | public: 31 | int climbStairs(int n) { 32 | if(n == 1) 33 | return 1; 34 | if(n == 2) 35 | return 2; 36 | 37 | vector dp(n+1); 38 | dp[1] = 1; 39 | dp[2] = 2; 40 | 41 | for(int i=3;i<=n;i++){ 42 | dp[i] = dp[i-1]+dp[i-2]; 43 | } 44 | return dp[n]; 45 | } 46 | }; 47 | 48 | class Solution { 49 | public: 50 | unordered_map map; 51 | int climbStairs(int n) { 52 | if(n == 1) 53 | return 1; 54 | if(n == 2) 55 | return 2; 56 | 57 | 58 | if(map.count(n)==0) 59 | { 60 | int num = climbStairs(n-1)+climbStairs(n-2); 61 | map[n] = num; 62 | return num; 63 | } 64 | else 65 | { 66 | return map[n]; 67 | } 68 | 69 | 70 | } 71 | }; -------------------------------------------------------------------------------- /leetcode/数组-盛最多水的容器.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 11. 盛最多水的容器 5 | // 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。 6 | // 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 7 | // 说明:你不能倾斜容器。 8 | 9 | // 示例 1: 10 | // 输入:[1,8,6,2,5,4,8,3,7] 11 | // 输出:49 12 | // 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 13 | 14 | // 示例 2: 15 | // 输入:height = [1,1] 16 | // 输出:1 17 | 18 | // 示例 3: 19 | // 输入:height = [4,3,2,1,4] 20 | // 输出:16 21 | 22 | // 示例 4: 23 | // 输入:height = [1,2,1] 24 | // 输出:2 25 | 26 | // 提示: 27 | // n = height.length 28 | // 2 <= n <= 3 * 104 29 | // 0 <= height[i] <= 3 * 104 30 | 31 | // -------------------第二次刷----------------------- 32 | // 2021年6月8日14:58:36 33 | // 准备第一次社招 34 | // 思路: 35 | // 双指针 36 | // 一开始,我们考虑相距最远的两个柱子所能容纳水的面积 37 | // 然后如果移动高的柱子,那么水的高度一定不会增加,且宽度一定减少,因此可以减少搜索空间,移动短的柱子 38 | 39 | class Solution { 40 | public: 41 | int maxArea(vector& height) { 42 | int i = 0; 43 | int j = height.size()-1; 44 | int maxAres = -1; 45 | while(i 27 | using namespace std; 28 | 29 | class Solution { 30 | public: 31 | int count=0; 32 | void dfs(vector& nums, int target, int sum, int index){ 33 | if(index==nums.size()){ 34 | if(sum==target)//必须要到终点才能看是否满足+1条件 35 | count++; 36 | return; 37 | } 38 | 39 | dfs(nums, target, sum+nums[index], index+1); 40 | dfs(nums, target, sum-nums[index], index+1); 41 | } 42 | 43 | int findTargetSumWays(vector& nums, int target) { 44 | if(nums.size()==0) 45 | return 0; 46 | 47 | dfs(nums, target, 0, 0); 48 | return count; 49 | } 50 | }; -------------------------------------------------------------------------------- /leetcode/数组-移动零.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 283. 移动零 6 | // 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 7 | 8 | // 示例: 9 | // 输入: [0,1,0,3,12] 10 | // 输出: [1,3,12,0,0] 11 | // 说明: 12 | // 必须在原数组上操作,不能拷贝额外的数组。 13 | // 尽量减少操作次数。 14 | 15 | // -------------------第二次刷----------------------- 16 | // 2021年7月2日10:59:10 17 | // 准备第一次社招 18 | // 思路1: 不用交换. 快慢双指针. fast只管往前遍历, slow只有在fast遇到非0值时才nums[slow]=nums[fast], 然后slow+=1. 19 | // fast到头后就继续把slow后面的都赋值成0 20 | 21 | class Solution { 22 | public: 23 | void moveZeroes(vector& nums) { 24 | int fast = 0; 25 | int slow = 0; 26 | while(fast& nums) { 48 | int left = 0; 49 | int right = 0; 50 | 51 | while (1) { 52 | while (nums[left] != 0) { 53 | left++; 54 | if (left >= nums.size()) 55 | return; 56 | } 57 | right = left; 58 | while (nums[right] == 0) { 59 | right++; 60 | if (right >= nums.size()) 61 | return; 62 | } 63 | swap(nums[left], nums[right]); 64 | } 65 | } 66 | }; -------------------------------------------------------------------------------- /leetcode/数组-组合总和.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 39. 组合总和 6 | // 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 7 | // candidates 中的数字可以无限制重复被选取。 8 | 9 | // 说明: 10 | // 所有数字(包括 target)都是正整数。 11 | // 解集不能包含重复的组合。 12 | 13 | // 示例 1: 14 | // 输入:candidates = [2,3,6,7], target = 7, 15 | // 所求解集为: 16 | // [ 17 | // [7], 18 | // [2,2,3] 19 | // ] 20 | 21 | // 示例 2: 22 | // 输入:candidates = [2,3,5], target = 8, 23 | // 所求解集为: 24 | // [ 25 | // [2,2,2,2], 26 | // [2,3,3], 27 | // [3,5] 28 | // ] 29 | 30 | // -------------------第二次刷----------------------- 31 | // 2021年06月09日22:50:52 32 | // 准备第一次社招 33 | // 思路: 求每种组合时, 设定一个distant, 然后多次遍历, 因为数字可重复, 所以每次遍历范围都是从头到尾. 34 | 35 | class Solution { 36 | public: 37 | void combinationSum_repeat(vector& candidates, int target, int distance, int index, 38 | vector>& result_vv, vector& result_v) { 39 | if (distance == 0) { 40 | result_vv.push_back(result_v); 41 | return; 42 | } 43 | 44 | for (int i = index; i < candidates.size(); i++) { 45 | if (candidates[i] <= distance) { 46 | result_v.push_back(candidates[i]); 47 | combinationSum_repeat(candidates, target, distance - candidates[i], i, result_vv, result_v); 48 | result_v.pop_back(); 49 | } else 50 | continue; 51 | } 52 | } 53 | 54 | vector> combinationSum(vector& candidates, int target) { 55 | vector> result_vv; 56 | vector result_v; 57 | combinationSum_repeat(candidates, target, target, 0, result_vv, result_v); 58 | return result_vv; 59 | } 60 | }; -------------------------------------------------------------------------------- /leetcode/数组-跳跃游戏.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 55. 跳跃游戏 7 | // 给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。 8 | // 数组中的每个元素代表你在该位置可以跳跃的最大长度。 9 | // 判断你是否能够到达最后一个下标。 10 | 11 | // 示例 1: 12 | // 输入:nums = [2,3,1,1,4] 13 | // 输出:true 14 | // 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。 15 | 16 | // 示例 2: 17 | // 输入:nums = [3,2,1,0,4] 18 | // 输出:false 19 | // 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。 20 | 21 | // -------------------第二次刷----------------------- 22 | // 2021年6月10日11:42:59 23 | // 准备第一次社招 24 | // 思路1: 25 | // dfs卡时间 26 | // class Solution { 27 | // public: 28 | // bool canJump_repeat(vector& nums, int index, unordered_map& map) { 29 | // if (index == nums.size() - 1) 30 | // return true; 31 | 32 | // for (int i = 1; i <= nums[index]; i++) { 33 | // if (map.find(index + i) != map.end()) { 34 | // if (map[index + i] == true) 35 | // return true; 36 | // } else { 37 | // bool r = canJump_repeat(nums, index + i, map); 38 | // map[index + i] = r; 39 | // if (r) 40 | // return true; 41 | // } 42 | // } 43 | // return false; 44 | // } 45 | // bool canJump(vector& nums) { 46 | // unordered_map map; 47 | 48 | // if (canJump_repeat(nums, 0, map)) 49 | // return true; 50 | // else 51 | // return false; 52 | // } 53 | // }; 54 | 55 | // 思路2: 56 | // 1.如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。 57 | // 2.可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。 58 | // 3.如果可以一直跳到最后,就成功了。 59 | bool canJump(vector& nums) { 60 | int d = 0;//能跳到的最远index 61 | for (int i = 0; i < nums.size(); i++) { 62 | if (i > d)//如果跳不到当前的i则退出 63 | return false; 64 | d = max(d,i+nums[i]);//后面能跳的最远距离可能小于前面的 65 | } 66 | return true; 67 | } 68 | -------------------------------------------------------------------------------- /leetcode/数组-除自身以外数组的乘积.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 题目:除自身以外数组的乘积 6 | // 给你一个长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 7 | // output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。 示例: 输入: [1,2,3,4] 输出: [24,12,8,6] 8 | // 提示:题目数据保证数组之中任意元素的全部前缀元素和后缀(甚至是整个数组)的乘积都在 32 位整数范围内。 9 | // 说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。 10 | 11 | // -------------------第二次刷----------------------- 12 | // 2021年6月30日15:03:58 13 | // 准备第一次社招 14 | // 思路:利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案 15 | class Solution { 16 | public: 17 | vector productExceptSelf(vector& nums) { 18 | vector left(nums.size(), 1); 19 | vector right(nums.size(), 1); 20 | vector r(nums.size(), 1); 21 | 22 | // left[i]是该元素左边所有元素的乘积 23 | for (int i = 1; i < nums.size(); i++) { 24 | left[i] = left[i - 1] * nums[i - 1]; 25 | } 26 | for (int i = nums.size() - 2; i >= 0; i--) { 27 | right[i] = right[i + 1] * nums[i + 1]; 28 | } 29 | 30 | for (int i = 0; i < nums.size(); i++) { 31 | r[i] = left[i] * right[i]; 32 | } 33 | return r; 34 | } 35 | }; -------------------------------------------------------------------------------- /leetcode/数组-零钱兑换.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | 6 | // 322. 零钱兑换 7 | // 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。 8 | // 你可以认为每种硬币的数量是无限的。 9 | 10 | // 示例 1: 11 | // 输入:coins = [1, 2, 5], amount = 11 12 | // 输出:3 13 | // 解释:11 = 5 + 5 + 1 14 | 15 | // 示例 2: 16 | // 输入:coins = [2], amount = 3 17 | // 输出:-1 18 | 19 | // 示例 3: 20 | // 输入:coins = [1], amount = 0 21 | // 输出:0 22 | 23 | // 示例 4: 24 | // 输入:coins = [1], amount = 1 25 | // 输出:1 26 | 27 | // 示例 5: 28 | // 输入:coins = [1], amount = 2 29 | // 输出:2 30 | 31 | // -------------------第二次刷----------------------- 32 | // 2021年7月5日10:20:08 33 | // 准备第一次社招 34 | // 思路: 35 | // dp[i]代表凑成总金额i最少的硬币个数 36 | // dp[i+k] = min(dp[i]+1, dp[i+k]), k为不同金额的硬币 37 | // dp[0] = 0 38 | 39 | class Solution { 40 | public: 41 | int coinChange(vector& coins, int amount) { 42 | vector dp(amount + 1, -1); 43 | dp[0] = 0; 44 | 45 | for (int i = 0; i <= amount; i++) { 46 | if (dp[i] == -1) 47 | continue; 48 | for (auto k : coins) { 49 | if ((long long)i + (long long)k <= (long long)amount)//signed integer overflow 50 | if (dp[i + k] == -1) 51 | dp[i + k] = dp[i] + 1; 52 | else 53 | dp[i + k] = min(dp[i] + 1, dp[i + k]); 54 | } 55 | } 56 | return dp[amount]; 57 | } 58 | }; -------------------------------------------------------------------------------- /leetcode/数组-颜色分类.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 75. 颜色分类 7 | // 给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 8 | // 此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 9 | 10 | // 示例 1: 11 | // 输入:nums = [2,0,2,1,1,0] 12 | // 输出:[0,0,1,1,2,2] 13 | 14 | // 示例 2: 15 | // 输入:nums = [2,0,1] 16 | // 输出:[0,1,2] 17 | 18 | // 示例 3: 19 | // 输入:nums = [0] 20 | // 输出:[0] 21 | 22 | // 示例 4: 23 | // 输入:nums = [1] 24 | // 输出:[1] 25 | 26 | // -------------------第二次刷----------------------- 27 | // 2021年6月11日10:46:00 28 | // 准备第一次社招 29 | // 思路: 30 | // 三指针:left 指针指向数组的开始;right 指针指向数组的结尾。滑动index 31 | // 0区间不包括left和2区间不包括right,即 [0,left)都是0, [left, i)都是1, [i,right]待处理的, (right, end]都是2 32 | // 若 index 位置上的元素值为 0,则说明是红色,要放在最前面,即和 left 指针指向位置上的元素进行交换; 33 | // 若 index 位置上的元素值为 1,则说明是白色(本来就是要放在中间)不需要进行交换,直接将 index 指针后移; 34 | // 若 index 位置上的元素值为 2,则说明是蓝色,要放在最后面,即和 right 指针指向位置上的元素进行交换。 35 | class Solution { 36 | public: 37 | void sortColors(vector& nums) { 38 | int left = 0; 39 | int right = nums.size() - 1; 40 | 41 | for(int i=0;i 26 | #include 27 | using namespace std; 28 | 29 | class Solution { 30 | public: 31 | int islandPerimeter(vector>& grid, int i, int j, int& length){ 32 | if(i>grid.size()-1||i<0||j>grid[0].size()-1||j<0) 33 | return 1; 34 | 35 | if(grid[i][j]==0) 36 | return 1; 37 | if(grid[i][j]==2) 38 | return 0; 39 | 40 | grid[i][j] = 2; 41 | 42 | int r1 = islandPerimeter(grid, i-1, j, length); 43 | int r2 = islandPerimeter(grid, i, j-1, length); 44 | int r3 = islandPerimeter(grid, i+1, j, length); 45 | int r4 = islandPerimeter(grid, i, j+1, length); 46 | 47 | length+=(r1+r2+r3+r4); 48 | return 0; 49 | } 50 | 51 | int islandPerimeter(vector>& grid) { 52 | int length = 0; 53 | for(int i=0;i 2 | #include 3 | 4 | using namespace std; 5 | 6 | // [非常好的棋盘遍历正规教程](https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-lei-wen-ti-de-tong-yong-jie-fa-dfs-bian-li-/) 7 | 8 | // 200. 岛屿数量 9 | // 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 10 | // 岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 11 | // 此外,你可以假设该网格的四条边均被水包围。 12 | 13 | // 示例 1: 14 | // 输入:grid = [ 15 | // ["1","1","1","1","0"], 16 | // ["1","1","0","1","0"], 17 | // ["1","1","0","0","0"], 18 | // ["0","0","0","0","0"] 19 | // ] 20 | // 输出:1 21 | 22 | // 示例 2: 23 | // 输入:grid = [ 24 | // ["1","1","0","0","0"], 25 | // ["1","1","0","0","0"], 26 | // ["0","0","1","0","0"], 27 | // ["0","0","0","1","1"] 28 | // ] 29 | // 输出:3 30 | 31 | // 2021年06月20日11:05:36 32 | // 准备第一次社招 33 | // 思路: 经典的岛屿题. 我们遍历所有点, 先后从一个未上过的点上岛, 上岛后先将flag蔓延到岛上的其他点, 34 | // 这样就不会重复上岛. 执行这个过程中需要记录什么则根据题目要求来 35 | 36 | class Solution { 37 | public: 38 | int count = 0; 39 | int numIslands(vector>& grid) { 40 | //起始点可以为任一一个棋格 41 | for(int i = 0; i < grid.size(); i++) { 42 | for(int j = 0; j < grid[0].size(); j++) { 43 | if(grid[i][j] == '1') { 44 | count++; 45 | dfs(grid, i, j); 46 | } 47 | } 48 | } 49 | 50 | return count; 51 | } 52 | 53 | void dfs(vector>& grid, int r, int c) { 54 | //判断 base case 55 | //如果坐标(r,c)超出了网格范围,直接返回 56 | if(!isArea(grid, r, c)) 57 | return; 58 | 59 | //如果这个格子不是岛屿,直接返回 60 | if (grid[r][c] != '1') 61 | return; 62 | 63 | grid[r][c] = 2; //将格子标记为【已遍历过】 64 | 65 | //访问上、下、左、右四个相邻结点 66 | dfs(grid, r-1, c); 67 | dfs(grid, r+1, c); 68 | dfs(grid, r, c-1); 69 | dfs(grid, r, c+1); 70 | } 71 | 72 | //判断坐标(r,c)是否在网格中 73 | bool isArea(vector>& grid, int r, int c) { 74 | return (0 <= r && r < grid.size() && 0 <= c && c < grid[0].size()); 75 | } 76 | 77 | }; -------------------------------------------------------------------------------- /leetcode/棋盘-岛屿的最大面积.cpp: -------------------------------------------------------------------------------- 1 | // 695. 岛屿的最大面积 2 | // 给定一个包含了一些 0 和 1 的非空二维数组 grid 。 3 | // 一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。 4 | // 找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。) 5 | 6 | // 示例 1: 7 | // [[0,0,1,0,0,0,0,1,0,0,0,0,0], 8 | // [0,0,0,0,0,0,0,1,1,1,0,0,0], 9 | // [0,1,1,0,1,0,0,0,0,0,0,0,0], 10 | // [0,1,0,0,1,1,0,0,1,0,1,0,0], 11 | // [0,1,0,0,1,1,0,0,1,1,1,0,0], 12 | // [0,0,0,0,0,0,0,0,0,0,1,0,0], 13 | // [0,0,0,0,0,0,0,1,1,1,0,0,0], 14 | // [0,0,0,0,0,0,0,1,1,0,0,0,0]] 15 | // 对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1 。 16 | 17 | // 示例 2: 18 | // [[0,0,0,0,0,0,0,0]] 19 | // 对于上面这个给定的矩阵, 返回 0。 20 | 21 | // 2021年06月20日11:05:36 22 | // 准备第一次社招 23 | // 思路: 经典的岛屿题. 我们遍历所有点, 先后从一个未上过的点上岛, 上岛后先将flag蔓延到岛上的其他点, 24 | // 这样就不会重复上岛. 执行这个过程中需要记录什么则根据题目要求来 25 | 26 | #include 27 | #include 28 | using namespace std; 29 | 30 | class Solution { 31 | public: 32 | void maxAreaOfIsland(vector>& grid, int i, int j, int& area){ 33 | if(i>grid.size()-1||i<0||j>grid[0].size()-1||j<0) 34 | return; 35 | 36 | if(grid[i][j]!=1) 37 | return; 38 | 39 | area++; 40 | grid[i][j] = 2; 41 | 42 | maxAreaOfIsland(grid, i-1, j, area); 43 | maxAreaOfIsland(grid, i, j-1, area); 44 | maxAreaOfIsland(grid, i+1, j, area); 45 | maxAreaOfIsland(grid, i, j+1, area); 46 | } 47 | 48 | int maxAreaOfIsland(vector>& grid) { 49 | int max_area = INT32_MIN; 50 | for(int i=0;i 2 | #include 3 | using namespace std; 4 | 5 | // 48. 旋转图像 6 | // 给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 7 | // 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 8 | 9 | // 示例 1: 10 | // 输入:matrix = [[1,2,3],[4,5,6],[7,8,9]] 11 | // 输出:[[7,4,1],[8,5,2],[9,6,3]] 12 | 13 | // 示例 2: 14 | // 输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] 15 | // 输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]] 16 | 17 | // 示例 3: 18 | // 输入:matrix = [[1]] 19 | // 输出:[[1]] 20 | 21 | // 示例 4: 22 | // 输入:matrix = [[1,2],[3,4]] 23 | // 输出:[[3,1],[4,2]] 24 | 25 | // -------------------第二次刷----------------------- 26 | // 2021年6月10日10:30:53 27 | // 准备第一次社招 28 | // 思路: 29 | // 找到旋转的规律, old矩阵中第 i 行的第 j列的数matrix[i][j], 经过旋转后, 出现在new矩阵中的第j行倒数第 i 列, 即matrix[j][n-1-i]. 30 | // 换一下顺序, 即new矩阵中的数matrix[i][j]来自于old矩阵中的matrix[n-1-j][i]. 31 | // 每次处理一个数, 位置点A=点B的数, 因为点B的数移动到了位置点A了, 那么要考虑位置点B的数来自于哪里. 那就继续套公式, 位置点B=点C的数, 32 | // 位置点C=点D的数, 位置点D=点A的数, 4次之后刚好绕一圈 33 | class Solution { 34 | public: 35 | void rotate(vector>& matrix) { 36 | int n = matrix.size(); 37 | //整个矩阵分四块,图示见https://leetcode-cn.com/problems/rotate-image/solution/xuan-zhuan-tu-xiang-by-leetcode-solution-vu3m/ 38 | for (int i = 0; i < n / 2; ++i) { 39 | for (int j = 0; j < (n + 1) / 2; ++j) { 40 | int temp = matrix[i][j]; 41 | matrix[i][j] = matrix[n - 1 - j][i]; 42 | matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1]; //套上面的公式 43 | matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1]; 44 | matrix[j][n - i - 1] = temp; 45 | } 46 | } 47 | } 48 | }; -------------------------------------------------------------------------------- /leetcode/棋盘-最小路径和.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | using namespace std; 5 | 6 | // 64. 最小路径和 7 | // 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 8 | // 说明:每次只能向下或者向右移动一步。 9 | 10 | // 示例 1: 11 | // 输入:grid = [[1,3,1],[1,5,1],[4,2,1]] 12 | // 输出:7 13 | // 解释:因为路径 1→3→1→1→1 的总和最小。 14 | 15 | // 示例 2: 16 | // 输入:grid = [[1,2,3],[4,5,6]] 17 | // 输出:12 18 | 19 | // -------------------第二次刷----------------------- 20 | // 2021年6月11日10:41:32 21 | // 准备第一次社招 22 | // 思路: 棋盘问题还是优先考虑dp 23 | 24 | // 设 dp为大小 m×n 矩阵,其中 dp[i][j]的值代表直到走到 (i,j)的最小路径和 25 | // 1.当左边和上边都不是矩阵边界时: 即当i != 0, j != j 时,dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] 26 | // 2.当只有左边是矩阵边界时: 只能从上面来,即当i = 0, j != 0,j =0时, dp[i][j] = dp[i][j - 1] + grid[i][j] 27 | // 3.当只有上边是矩阵边界时: 只能从左面来,即当i != 0, j = 0时, dp[i][j] = dp[i - 1][j] + grid[i][j] 28 | // 4.当左边和上边都是矩阵边界时: 即当i = 0, j = 0时,其实就是起点, dp[i][j] = grid[i][j] 29 | 30 | class Solution { 31 | public: 32 | int minPathSum(vector>& grid) { 33 | int m = grid.size(); 34 | int n = grid[0].size(); 35 | vector> dp(m, vector(n, 0)); 36 | 37 | for (int i = 0; i < m; i++) { 38 | for (int j = 0; j < n; j++) { 39 | if (i == 0 && j == 0) 40 | dp[i][j] = grid[i][j]; 41 | else if (i == 0) 42 | dp[i][j] = dp[i][j - 1] + grid[i][j]; 43 | else if (j == 0) 44 | dp[i][j] = dp[i - 1][j] + grid[i][j]; 45 | else 46 | dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; 47 | } 48 | } 49 | return dp[m-1][n-1]; 50 | } 51 | }; -------------------------------------------------------------------------------- /leetcode/链表-两数相加.cpp: -------------------------------------------------------------------------------- 1 | // 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 2 | // 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 3 | // 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 4 | // 示例: 5 | // 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 6 | // 输出:7 -> 0 -> 8 7 | // 原因:342 + 465 = 807 8 | 9 | // -------------------第二次刷----------------------- 10 | // 2021年06月06日20:50:26 11 | // 准备第一次社招 12 | // 思路: l1和l2分别从head到nullptr一一成对相加, 记录进位, 送入下对一起相加 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | using namespace std; 19 | 20 | struct ListNode { 21 | int val; 22 | ListNode *next; 23 | ListNode(int x) : val(x), next(NULL) {} 24 | }; 25 | 26 | class Solution { 27 | public: 28 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { 29 | int add = 0; 30 | ListNode* phead = new ListNode(((l1->val+l2->val+add)%10));; 31 | add = (l1->val+l2->val+add)/10; 32 | l1 = l1->next; 33 | l2 = l2->next; 34 | 35 | ListNode* l3 = phead; 36 | while(l1!=NULL&&l2!=NULL) 37 | { 38 | l3->next = new ListNode(((l1->val+l2->val+add)%10)); 39 | add = (l1->val+l2->val+add)/10; 40 | l1 = l1->next; 41 | l2 = l2->next; 42 | l3 = l3 ->next; 43 | } 44 | while(l1!=NULL) 45 | { 46 | l3->next = new ListNode((l1->val+add)%10); 47 | add = (l1->val+add)/10; 48 | l1 = l1->next; 49 | l3 = l3->next; 50 | } 51 | while(l2!=NULL) 52 | { 53 | l3->next = new ListNode((l2->val+add)%10); 54 | add = (l2->val+add)/10; 55 | l2 = l2->next; 56 | l3 = l3->next; 57 | } 58 | if(add) 59 | { 60 | l3->next = new ListNode(add%10); 61 | add = add/10; 62 | } 63 | 64 | return phead; 65 | 66 | 67 | 68 | } 69 | }; -------------------------------------------------------------------------------- /leetcode/链表-删除链表的倒数第N个节点.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Definition for singly-linked list. 3 | struct ListNode { 4 | int val; 5 | ListNode *next; 6 | ListNode() : val(0), next(nullptr) {} 7 | ListNode(int x) : val(x), next(nullptr) {} 8 | ListNode(int x, ListNode *next) : val(x), next(next) {} 9 | }; 10 | 11 | // 19. 删除链表的倒数第 N 个结点 12 | // 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 13 | 14 | // 进阶:你能尝试使用一趟扫描实现吗? 15 | 16 | // 示例 1: 17 | // 输入:head = [1,2,3,4,5], n = 2 18 | // 输出:[1,2,3,5] 19 | 20 | // 示例 2: 21 | // 输入:head = [1], n = 1 22 | // 输出:[] 23 | 24 | // 示例 3: 25 | // 输入:head = [1,2], n = 1 26 | // 输出:[1] 27 | 28 | // 提示: 29 | // 链表中结点的数目为 sz 30 | // 1 <= sz <= 30 31 | // 0 <= Node.val <= 100 32 | // 1 <= n <= sz 33 | 34 | // -------------------第二次刷----------------------- 35 | // 2021年6月8日15:55:01 36 | // 准备第一次社招 37 | // 思路: 蛮简单的, 删除链表必须记得加上辅助头节点以防删除的就是头节点. 要求只遍历一遍就弄个vector或者快慢指针 38 | 39 | class Solution { 40 | public: 41 | ListNode* removeNthFromEnd(ListNode* head, int n) { 42 | ListNode* new_head = new ListNode(0); 43 | new_head->next = head; 44 | 45 | vector v; 46 | ListNode* p = new_head; 47 | while(p!=nullptr){ 48 | v.push_back(p); 49 | p=p->next; 50 | } 51 | 52 | ListNode* target = v[v.size()-n]; 53 | ListNode* left = v[v.size()-n-1]; 54 | ListNode* right = target->next; 55 | left->next = right; 56 | 57 | return new_head->next; 58 | } 59 | }; -------------------------------------------------------------------------------- /leetcode/链表-反转链表II.cpp: -------------------------------------------------------------------------------- 1 | // 92. 反转链表 II 2 | // 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。 3 | 4 | // 示例 1: 5 | // 输入:head = [1,2,3,4,5], left = 2, right = 4 6 | // 输出:[1,4,3,2,5] 7 | 8 | // 示例 2: 9 | // 输入:head = [5], left = 1, right = 1 10 | // 输出:[5] 11 | 12 | struct ListNode { 13 | int val; 14 | ListNode *next; 15 | ListNode() : val(0), next(nullptr) {} 16 | ListNode(int x) : val(x), next(nullptr) {} 17 | ListNode(int x, ListNode *next) : val(x), next(next) {} 18 | }; 19 | 20 | // -------------------第三次刷----------------------- 21 | // 2021年7月19日17:36:59 22 | // 准备第一次社招 23 | // 思路: 用一个全局变量来控制当前node应该进行的操作. 24 | // left以左要做两个事, 一个就是不停往右递归走直到i=left-1, 另外一个就是当i=left-1时连上反转后的new_head. 25 | // [left, right)就执行反转链表, 这里需要保留new_tail 26 | // i=right时即相当于走到最后一个有意义的节点, 返回当前节点 27 | 28 | class Solution { 29 | public: 30 | int i=0; 31 | ListNode* reverseBetween(ListNode* head, int left, int right) { 32 | i++; 33 | if(inext = reverseBetween(head->next, left, right); 35 | return head; 36 | } 37 | else if(i>=left&&inext, left, right); 39 | ListNode* new_tail = head->next->next; 40 | head->next->next = head; 41 | head->next = new_tail; 42 | return new_head; 43 | } 44 | else if(i==right){ 45 | return head; 46 | } 47 | 48 | return nullptr; 49 | } 50 | }; -------------------------------------------------------------------------------- /leetcode/链表-回文链表.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace std; 4 | 5 | // 234. 回文链表 6 | // 请判断一个链表是否为回文链表。 7 | 8 | // 示例 1: 9 | // 输入: 1->2 10 | // 输出: false 11 | 12 | // 示例 2: 13 | // 输入: 1->2->2->1 14 | // 输出: true 15 | // 进阶: 16 | // 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题? 17 | 18 | // -------------------第二次刷----------------------- 19 | // 2021年6月28日11:24:58 20 | // 准备第一次社招 21 | // 思路: 找到中间节点, 反转后面, 然后一一比较 22 | // 快慢指针 23 | // 总数为奇数:当不满足fast&&fast->next时,slow指向中间 24 | // 总数为奇数:当不满足fast&&fast->next时,slow指向中间靠后 25 | // 不重要,只要从slow开始的后半段反转一下,然后比较即可 26 | 27 | struct ListNode { 28 | int val; 29 | ListNode* next; 30 | ListNode() : val(0), next(nullptr) {} 31 | ListNode(int x) : val(x), next(nullptr) {} 32 | ListNode(int x, ListNode* next) : val(x), next(next) {} 33 | }; 34 | 35 | class Solution { 36 | public: 37 | ListNode *ReverseList_2(ListNode *pHead) 38 | { 39 | // 第一个==是针对第一次调用的输入直接是nullptr 40 | // 第二个==是针对最后找到尾结点时返回尾结点 41 | if (pHead == nullptr || pHead->next == nullptr) 42 | return pHead; 43 | // 因为反转之后访问不到下一个节点,即返回回来新链表的尾结点,所以先保存下一个节点 44 | ListNode* temp = pHead->next;//保存下一个节点 45 | ListNode* newHead = ReverseList_2(pHead->next);//整体思维,宏观语义 46 | // 新链表的尾结点的next指向当前节点 47 | temp->next = pHead;//连上头与递归部分 48 | // 当前节点成了新链表的尾结点,把该节点指向nullptr 49 | pHead->next = nullptr;//调整尾部 50 | return newHead;//返回头节点 51 | } 52 | 53 | bool isPalindrome(ListNode* head) { 54 | ListNode* fast = head; 55 | ListNode* slow = head; 56 | while(fast&&fast->next){ 57 | slow = slow->next; 58 | fast = fast->next->next; 59 | } 60 | 61 | ListNode* newhalfhead = ReverseList_2(slow); 62 | while(newhalfhead&&head){ 63 | if(newhalfhead->val!=head->val) 64 | return false; 65 | newhalfhead=newhalfhead->next; 66 | head=head->next; 67 | } 68 | return true; 69 | } 70 | }; -------------------------------------------------------------------------------- /leetcode/链表-重排链表.cpp: -------------------------------------------------------------------------------- 1 | // 给定一个单链表 L 的头节点 head ,单链表 L 表示为: 2 | //  L0 → L1 → … → Ln-1 → Ln  3 | // 请将其重新排列后变为: 4 | // L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → … 5 | // 不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 6 | //   7 | // 示例 1: 8 | // 输入: head = [1,2,3,4] 9 | // 输出: [1,4,2,3] 10 | 11 | // 示例 2: 12 | // 输入: head = [1,2,3,4,5] 13 | // 输出: [1,5,2,4,3] 14 | 15 | // -------------------第二次刷----------------------- 16 | // 2021年07月19日19:13:58 17 | // 准备第一次社招 18 | // 思路: 19 | // 注意到目标链表即为将原链表的左半端和反转后的右半端合并后的结果。 20 | // 这样我们的任务即可划分为三步: 21 | // 1. 找到原链表的中点(参考「876. 链表的中间结点」)。我们可以使用快慢指针来 O(N)地找到链表的中间节点。 22 | // 2. 将原链表的右半端反转(参考「206. 反转链表」)。我们可以使用迭代法实现链表的反转。 23 | // 3. 将原链表的两端合并。因为两链表长度相差不超过 11,因此直接合并即可。 24 | 25 | struct ListNode { 26 | int val; 27 | ListNode *next; 28 | ListNode() : val(0), next(nullptr) {} 29 | ListNode(int x) : val(x), next(nullptr) {} 30 | ListNode(int x, ListNode *next) : val(x), next(next) {} 31 | }; 32 | 33 | class Solution { 34 | public: 35 | ListNode* middleNode(ListNode* head) { 36 | ListNode* slow = head; 37 | ListNode* fast = head; 38 | while (fast->next != nullptr && fast->next->next != nullptr) { 39 | slow = slow->next; 40 | fast = fast->next->next; 41 | } 42 | return slow; 43 | } 44 | 45 | ListNode* reverseList(ListNode* head) { 46 | if(head==nullptr) 47 | return head; 48 | if(head->next==nullptr) 49 | return head; 50 | 51 | ListNode* new_head = reverseList(head->next); 52 | ListNode* new_tail = head->next->next; 53 | head->next->next = head; 54 | head->next = new_tail; 55 | 56 | return new_head; 57 | } 58 | 59 | void mergeList(ListNode* l1, ListNode* l2) { 60 | ListNode* l1_tmp; 61 | ListNode* l2_tmp; 62 | while (l1 != nullptr && l2 != nullptr) { 63 | l1_tmp = l1->next; 64 | l2_tmp = l2->next; 65 | 66 | l1->next = l2; 67 | l1 = l1_tmp; 68 | 69 | l2->next = l1; 70 | l2 = l2_tmp; 71 | } 72 | } 73 | 74 | void reorderList(ListNode* head) { 75 | if (head == nullptr) { 76 | return; 77 | } 78 | ListNode* mid = middleNode(head); 79 | ListNode* l1 = head; 80 | ListNode* l2 = mid->next; 81 | mid->next = nullptr; 82 | l2 = reverseList(l2); 83 | mergeList(l1, l2); 84 | } 85 | }; -------------------------------------------------------------------------------- /nc4hw4/mk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mode=$1 3 | 4 | if [ ! -d "./Debug" ]; then 5 | mkdir Debug 6 | fi 7 | 8 | if [ "${mode}" == "debug" ]; then 9 | echo "[hsq] build for debug" 10 | gcc gemm_c4_neon.c -c -o ./Debug/gemm_c4_neon.o -mfpu=neon -mcpu=cortex-a53 -O0 -g 11 | gcc gemm_neon.s -c -mfpu=neon -mcpu=cortex-a53 -o ./Debug/gemm_neon.o -O0 -g 12 | gcc main.c ./Debug/gemm_neon.o ./Debug/gemm_c4_neon.o -o main -O0 -g 13 | else 14 | echo "[hsq] build for release" 15 | gcc gemm_c4_neon.c -c -o ./Debug/gemm_c4_neon.o -mfpu=neon -mcpu=cortex-a53 -Ofast 16 | gcc gemm_neon.s -c -mfpu=neon -mcpu=cortex-a53 -o ./Debug/gemm_neon.o -Ofast 17 | gcc main.c ./Debug/gemm_neon.o ./Debug/gemm_c4_neon.o -o main 18 | fi -------------------------------------------------------------------------------- /thread_process/pthread/create.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //getpid() 3 | #include 4 | 5 | void *print_hello(void *arg) 6 | { 7 | pid_t pid = getpid(); 8 | pthread_t tid = pthread_self(); 9 | printf("I am thread: %d, pid: %u, tid: 0x%x\r\n", *((int *)arg), (unsigned int)pid, (unsigned int)tid);//先强制类型转换泛型指针,然后取指针指向的值 10 | return 0; // 执行到线程函数的末尾会自动结束线程 11 | } 12 | 13 | int main() 14 | { 15 | int num_thread = 5; 16 | int index[num_thread]; 17 | pthread_t thread_tid[num_thread]; 18 | for (int i = 0; i < num_thread; i++) 19 | { 20 | index[i] = i; 21 | printf("main() : 创建线程, %d\n", i); 22 | int ret = pthread_create(&thread_tid[i], 0, print_hello, (void *)&(index[i])); 23 | if (ret != 0) 24 | printf("Thread creation failed"); 25 | } 26 | 27 | pthread_exit(NULL);// 退出运行main函数的主线程,该操作不影响其他5个线程 28 | return 0;// 有了上面那句pthread_exit就不会执行到这句 29 | } -------------------------------------------------------------------------------- /thread_process/pthread/exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include //getpid() 4 | 5 | void *thread_0(void *arg) { 6 | pid_t pid = getpid(); 7 | pthread_t tid = pthread_self(); 8 | for (int i = 0; i < 5; i++) 9 | printf("I am thread: %d, pid: %u, tid: 0x%x\r\n", *((int *)arg), 10 | (unsigned int)pid, (unsigned int)tid); 11 | return 0; // 执行到线程函数的末尾会自动结束线程 12 | } 13 | 14 | void *thread_1(void *arg) { 15 | pid_t pid = getpid(); 16 | pthread_t tid = pthread_self(); 17 | for (int i = 0; i < 5; i++) { 18 | printf("I am thread: %d, pid: %u, tid: 0x%x\r\n", *((int *)arg), 19 | (unsigned int)pid, (unsigned int)tid); 20 | if (i == 3) pthread_exit(0); // 自己先退出 21 | } 22 | return 0; // 执行到线程函数的末尾会自动结束线程 23 | } 24 | 25 | void *thread_2(void *arg) { 26 | pid_t pid = getpid(); 27 | pthread_t tid = pthread_self(); 28 | while (1) { 29 | printf("I am thread: %d, pid: %u, tid: 0x%x\r\n", *((int *)arg), 30 | (unsigned int)pid, (unsigned int)tid); 31 | sleep(1); // 有没有这个sleep在接受到pthread_cancel后都会终止该线程 32 | } 33 | return 0; // 永远不会自己执行到这里 34 | } 35 | 36 | int main() { 37 | int num_thread = 5; 38 | int index[num_thread]; 39 | pthread_t thread_tid[num_thread]; 40 | 41 | index[0] = 0; 42 | printf("main() : 创建线程, %d\n", 0); 43 | int ret = pthread_create(&thread_tid[0], 0, thread_0, (void *)&(index[0])); 44 | if (ret != 0) printf("Thread creation failed"); 45 | 46 | index[1] = 1; 47 | printf("main() : 创建线程, %d\n", 1); 48 | ret = pthread_create(&thread_tid[1], 0, thread_1, (void *)&(index[1])); 49 | if (ret != 0) printf("Thread creation failed"); 50 | 51 | index[2] = 2; 52 | printf("main() : 创建线程, %d\n", 2); 53 | ret = pthread_create(&thread_tid[2], 0, thread_2, (void *)&(index[2])); 54 | if (ret != 0) printf("Thread creation failed"); 55 | 56 | sleep(1); 57 | if (pthread_cancel(thread_tid[2]) == 0) // 取消别的线程 58 | printf("send the cancel cmd to thread_2\r\n"); 59 | pthread_exit(NULL); // 退出主线程,不会影响别的线程 60 | return 0; // 有了上面那句pthread_exit就不会执行到这句 61 | } -------------------------------------------------------------------------------- /thread_process/pthread/join.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include //getpid() 3 | #include 4 | 5 | void *print_hello(void *arg) 6 | { 7 | pid_t pid = getpid(); 8 | pthread_t tid = pthread_self(); 9 | printf("I am thread: %d, pid: %u, tid: 0x%x\r\n", *((int *)arg), (unsigned int)pid, (unsigned int)tid); 10 | return 0; // 执行到线程函数的末尾会自动结束线程 11 | } 12 | 13 | int main() 14 | { 15 | int num_thread = 5; 16 | int index[num_thread]; 17 | pthread_t thread_tid[num_thread]; 18 | for (int i = 0; i < num_thread; i++) 19 | { 20 | index[i] = i; 21 | printf("main() : 创建线程, %d\n", i); 22 | int ret = pthread_create(&thread_tid[i], 0, print_hello, (void *)&(index[i])); 23 | if (ret != 0) 24 | printf("Thread creation failed"); 25 | } 26 | for(int i=0;i 2 | #include 3 | #include //getpid() 4 | 5 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态初始化互斥锁 6 | int count = 0; 7 | 8 | void *print_count(void *arg) { 9 | pid_t pid = getpid(); 10 | pthread_t tid = pthread_self(); 11 | for (int i = 0; i < 5; i++) { 12 | pthread_mutex_lock(&mutex); // 上锁失败代表别的线程在使用,则当前线程阻塞 13 | count++; 14 | printf("I am thread: %d, pid: %u, tid: 0x%x count: %d\r\n", *((int *)arg), 15 | (unsigned int)pid, (unsigned int)tid, count); 16 | pthread_mutex_unlock(&mutex); 17 | } 18 | pthread_exit(0); 19 | return 0; // 执行到线程函数的末尾会自动结束线程 20 | } 21 | 22 | int main() { 23 | int num_thread = 5; 24 | int index[num_thread]; 25 | pthread_t thread_tid[num_thread]; 26 | for (int i = 0; i < num_thread; i++) { 27 | index[i] = i; 28 | printf("main() : 创建线程, %d\n", i); 29 | int ret = 30 | pthread_create(&thread_tid[i], 0, print_count, (void *)&(index[i])); 31 | if (ret != 0) printf("Thread creation failed"); 32 | } 33 | for (int i = 0; i < num_thread; i++) 34 | pthread_join(thread_tid[i], NULL); // 阻塞主线程直到其他5个线程都退出 35 | pthread_exit(NULL); // 退出运行main函数的主线程,该操作不影响其他5个线程 36 | return 0; // 有了上面那句pthread_exit就不会执行到这句 37 | } -------------------------------------------------------------------------------- /thread_process/python/lock.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from multiprocessing import Lock, Process 5 | import time 6 | 7 | global_num = 0 8 | 9 | def process_fun(index, lock): 10 | global global_num 11 | 12 | time.sleep(2) 13 | while True: 14 | with lock: 15 | global_num+=1 16 | print("process_{}: ".format(str(index)), index) 17 | if global_num > 10: 18 | break 19 | time.sleep(1) 20 | 21 | if __name__ == "__main__": 22 | thread_num = int(input("input thread number: ")) 23 | 24 | lock = Lock() 25 | num = 0 26 | for i in range(thread_num): 27 | t = Process(target=process_fun, args=(i, lock, ), name="fun_"+str(i)) 28 | t.daemon = True 29 | t.start() 30 | print("process_"+str(i)+" started") 31 | 32 | print("main_process start sleeping") 33 | time.sleep(10) 34 | print("main process exit") 35 | exit() -------------------------------------------------------------------------------- /thread_process/python/manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | from multiprocessing import Process, Manager 4 | import time 5 | 6 | ## 二: Manager 7 | # 2.1: 可管理多种数据类型, 包括list 、 dict 、 Namespace 、 Lock 、 RLock 、 Semaphore 、 BoundedSemaphore 、 Condition 、 Event 、 Barrier 、 Queue 、 Value 和 Array 8 | # 2.2: 比使用共享内存慢(不知道如何证明) 9 | 10 | def process_fun(i, d, l): 11 | time.sleep(2) 12 | while True: 13 | print("process_{}: ".format(str(i))) 14 | print("manager dict['0']: {}, dict['1']: {}, dict['2']: {}".format(d["0"], d["1"], d["2"])) 15 | print("manager list[0]: {}, list[1]: {}, list[2]: {}\r\n".format(l[0], l[1], l[2])) 16 | 17 | d["0"] += "a" 18 | d["1"] += "b" 19 | d["2"] += "c" 20 | 21 | l[0] += "a" 22 | l[1] += "b" 23 | l[2] += "c" 24 | 25 | time.sleep(2) 26 | 27 | if __name__ == "__main__": 28 | thread_num = int(input("input thread number: ")) 29 | 30 | manager = Manager() # 定义管理器 31 | d = manager.dict({"0":"a", "1":"b", "2":"c"}) 32 | l = manager.list(["a", "b", "c"]) 33 | for i in range(thread_num): 34 | t = Process(target=process_fun, args=(i, d, l, ), name="fun_"+str(i)) 35 | t.daemon = True 36 | t.start() 37 | print("process_"+str(i)+" started") 38 | 39 | print("main_process start sleeping") 40 | time.sleep(10) 41 | print("main process exit") 42 | exit() -------------------------------------------------------------------------------- /thread_process/python/pipe.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | from multiprocessing import Process, Pipe 4 | import time 5 | 6 | def process_fun_send(i, conn_send): 7 | time.sleep(2) 8 | while True: 9 | print("process_{}: ".format(str(i))) 10 | conn_send.send(["a", "b"]) # 发送的对象必须是可以序列化的,过大的对象(接近32MiB)可能会引起ValueError异常 11 | conn_send.send_bytes(b'hi') 12 | print("send\r\n") 13 | 14 | time.sleep(2) 15 | 16 | def process_fun_recv(i, conn_recv): 17 | time.sleep(2) 18 | while True: 19 | print("process_{}: ".format(str(i))) 20 | obj = conn_recv.recv() # 该方法会一直阻塞直到接收到对象 21 | print("get: ", obj, "\r\n") 22 | obj = conn_recv.recv_bytes().decode('utf-8') 23 | print("get: ", obj, "\r\n") 24 | 25 | time.sleep(2) 26 | 27 | if __name__ == "__main__": 28 | conn_send, conn_recv = Pipe() 29 | t = Process(target=process_fun_send, args=(0, conn_send), name="fun_0") 30 | t.daemon = True 31 | t.start() 32 | print("process_0 started") 33 | 34 | t = Process(target=process_fun_recv, args=(1, conn_recv), name="fun_1") 35 | t.daemon = True 36 | t.start() 37 | print("process_1 started") 38 | 39 | print("main_process start sleeping") 40 | time.sleep(10) 41 | print("main process exit") 42 | exit() -------------------------------------------------------------------------------- /thread_process/python/pool.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HuangShiqing/LearnAndTry/ab49cb1944b3b7146795fe2028a7317a003d4c29/thread_process/python/pool.py -------------------------------------------------------------------------------- /thread_process/python/process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # 官方文档 https://docs.python.org/zh-cn/3.7/library/multiprocessing.html 5 | from multiprocessing import Process 6 | import setproctitle 7 | 8 | import time 9 | 10 | def process_fun(num): 11 | setproctitle.setproctitle("process_"+str(num)) # 在子进程中设置进程名 12 | count = 0 13 | while True: 14 | count += num*1.33333*3.449*2.887/4.789 15 | num += 1 16 | 17 | if __name__ == "__main__": 18 | setproctitle.setproctitle("python main") # 设置进程名 19 | thread_num = int(input("input thread number: ")) 20 | for i in range(thread_num): 21 | t = Process(target=process_fun, args=(i,), name="fun_"+str(i)) 22 | t.daemon = True # 子进程是守护进程时, 才会在主进程退出时也退出 23 | t.start() 24 | # Process(target=process_fun, args=(i,), name="fun_"+str(i)).start() # 不设置daemon时上面三句等于该一句 25 | print("process_"+str(i)+" started") 26 | print("main_process start sleeping") 27 | time.sleep(10) 28 | print("main process exit") 29 | exit() -------------------------------------------------------------------------------- /thread_process/python/queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | from multiprocessing import Process, Queue 4 | import time 5 | 6 | def process_fun_producer(i, q): 7 | time.sleep(2) 8 | while True: 9 | print("process_{}: ".format(str(i))) 10 | q.put(["a", "b"]) 11 | print("send\r\n") 12 | 13 | time.sleep(2) 14 | 15 | def process_fun_consumer(i, q): 16 | time.sleep(2) 17 | while True: 18 | print("process_{}: ".format(str(i))) 19 | obj = q.get() 20 | print("get: ", obj, "\r\n") 21 | 22 | time.sleep(2) 23 | 24 | if __name__ == "__main__": 25 | q = Queue() 26 | t = Process(target=process_fun_producer, args=(0, q), name="fun_0") 27 | t.daemon = True 28 | t.start() 29 | print("process_0 started") 30 | 31 | t = Process(target=process_fun_consumer, args=(1, q), name="fun_1") 32 | t.daemon = True 33 | t.start() 34 | print("process_1 started") 35 | 36 | print("main_process start sleeping") 37 | time.sleep(10) 38 | print("main process exit") 39 | exit() -------------------------------------------------------------------------------- /thread_process/python/thread.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # 官方文档 https://docs.python.org/zh-cn/3.7/library/threading.html 5 | from threading import Thread 6 | import time 7 | 8 | def thread_fun(num): 9 | count = 0 10 | while True: 11 | count += num*1.33333*3.449*2.887/4.789 12 | num += 1 13 | 14 | if __name__ == "__main__": 15 | thread_num = int(input("input thread number: ")) 16 | for i in range(thread_num): 17 | t = Thread(target=thread_fun, args=(i,), name="fun_"+str(i)) 18 | t.daemon = True # 主线程不是守护线程,因此主线程创建的所有线程默认都是 daemon = False 19 | # 守护线程有一个特征,如果所有的前台线程都死亡了,那么守护线程会自动死亡 20 | t.start() 21 | print("thread_"+str(i)+" started") 22 | print("main_thread start sleeping") 23 | time.sleep(30) 24 | print("main thread exit") 25 | exit()# 唯一的前台线程退出, 剩下的守护线程也会自动退出 -------------------------------------------------------------------------------- /thread_process/python/value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | from multiprocessing import Process, Value, Array, Manager 4 | import ctypes 5 | import time 6 | import numpy as np 7 | 8 | ## 一: Value和Array 9 | # 1.1: Value和Array都是进程安全, 不用自己上锁 10 | # 1.2: 共享变量可以定义成全局变量然后在进程函数里通过global引用 11 | 12 | def process_fun(i, v, array): 13 | # def process_fun(i): 14 | # global v, array, d, l 15 | time.sleep(2) 16 | while True: 17 | print("process_{}: ".format(str(i))) 18 | print("v: ", v.value) 19 | print("array[0]: {}, array[1]: {}, array[2]: {}".format(array[0], array[1], array[2])) 20 | 21 | v.value += 1 22 | 23 | array[0] += 1 24 | array[1] += 1 25 | array[2] += 1 26 | 27 | time.sleep(2) 28 | 29 | if __name__ == "__main__": 30 | thread_num = int(input("input thread number: ")) 31 | 32 | v = Value("i", 0) # 定义共享变量 33 | array = Array("i", range(3)) # 定义共享数组 34 | for i in range(thread_num): 35 | t = Process(target=process_fun, args=(i, v, array, ), name="fun_"+str(i)) 36 | # t = Process(target=process_fun, args=(i,), name="fun_"+str(i)) 37 | t.daemon = True 38 | t.start() 39 | print("process_"+str(i)+" started") 40 | 41 | 42 | print("main_process start sleeping") 43 | time.sleep(10) 44 | print("main process exit") 45 | exit() -------------------------------------------------------------------------------- /winograd/README.md: -------------------------------------------------------------------------------- 1 | # 三种思路五个版本Winograd实现代码 2 | 3 | ## Quick Start 4 | ```sh 5 | git clone https://github.com/HuangShiqing/LearnAndTry 6 | cd winograd 7 | bash mk.sh 8 | bash run.sh 9 | ``` 10 | ## Blog 11 | [CSDN](https://blog.csdn.net/hsqyc/article/details/116136385?spm=1001.2014.3001.5501) -------------------------------------------------------------------------------- /winograd/mk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Author:HuangShiqing 3 | #Time:2020-11-03 20:06:09 4 | #Name:mk.sh 5 | #Description:This is a awesome script. 6 | 7 | CROSS_COMPILE= #/workspace/arm-linux-androideabi/bin/arm-linux-androideabi- 8 | DIR="./Debug/" 9 | SRC="./src/" 10 | 11 | if [ ! -d "${DIR}" ]; then 12 | mkdir "${DIR}" 13 | fi 14 | 15 | ${CROSS_COMPILE}gcc "${SRC}"winograd1_sample.c -o winograd1_sample -Ofast 16 | ${CROSS_COMPILE}gcc "${SRC}"winograd4_sample.c -o winograd4_sample -Ofast 17 | ${CROSS_COMPILE}gcc "${SRC}"winograd5_sample.c -o winograd5_sample -Ofast 18 | 19 | ${CROSS_COMPILE}gcc -c "${SRC}"common.c -o "${DIR}"common.o -Ofast 20 | ${CROSS_COMPILE}gcc -c "${SRC}"conv0.c -o "${DIR}"conv0.o -Ofast 21 | ${CROSS_COMPILE}gcc -c "${SRC}"winograd1.c -o "${DIR}"winograd1.o -Ofast 22 | ${CROSS_COMPILE}gcc -c "${SRC}"winograd2.c -o "${DIR}"winograd2.o -Ofast 23 | ${CROSS_COMPILE}gcc -c "${SRC}"winograd3.c -o "${DIR}"winograd3.o -Ofast 24 | ${CROSS_COMPILE}gcc -c "${SRC}"winograd4.c -o "${DIR}"winograd4.o -Ofast 25 | ${CROSS_COMPILE}gcc -c "${SRC}"winograd5.c -o "${DIR}"winograd5.o -Ofast 26 | ${CROSS_COMPILE}gcc -c "${SRC}"main.c -o "${DIR}"main.o -Ofast 27 | ${CROSS_COMPILE}gcc "${DIR}"main.o "${DIR}"common.o "${DIR}"conv0.o "${DIR}"winograd1.o "${DIR}"winograd2.o "${DIR}"winograd3.o "${DIR}"winograd4.o "${DIR}"winograd5.o -o main -Ofast 28 | -------------------------------------------------------------------------------- /winograd/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Author:HuangShiqing 3 | #Time:2020-11-03 20:06:09 4 | #Name:run.sh 5 | #Description:This is a awesome script. 6 | 7 | ./winograd1_sample 8 | ./winograd4_sample 9 | ./winograd5_sample 10 | echo -e "\r\n" 11 | echo "-----------------------" 12 | ./main -------------------------------------------------------------------------------- /winograd/src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | 5 | // float A[8] = {1, 0, 1, 1, 1, -1, 0, -1}; 6 | // float AT[8] = {1, 1, 1, 0, 0, 1, -1, -1}; 7 | // float B[16] = {1, 0, 0, 0, 0, 1, -1, 1, -1, 1, 1, 0, 0, 0, 0, -1}; 8 | // float BT[16] = {1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 1, 0, 0, 1, 0, -1}; 9 | // float G[12] = {1, 0, 0, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0, 0, 1}; 10 | // float GT[12] = {1, 0.5, 0.5, 0, 0, 0.5, -0.5, 0, 0, 0.5, 0.5, 1}; 11 | 12 | double what_time_is_it_now() { 13 | struct timeval time; 14 | if (gettimeofday(&time, NULL)) { 15 | return 0; 16 | } 17 | return (double)time.tv_sec + (double)time.tv_usec * .000001; 18 | } 19 | // 优化https://blog.csdn.net/denlee/article/details/4206923 20 | void dot(float* A, int row_A, int col_A, float* B, int row_B, int col_B, float* C) { 21 | assert(col_A == row_B); // && row_A == col_B 22 | //由矩阵相乘,要求f2=s1,以下用f2 23 | for (int i = 0; i < row_A; i++) // i表示第i行 24 | for (int j = 0; j < col_B; j++) // j表示第j列 25 | // C[i*col_A + j] = 0; //在这里 result[i][j] = result[i*f2+j]; 26 | for (int p = 0; p < col_A; p++) 27 | C[i * col_B + j] += A[i * col_A + p] * B[p * col_B + j]; 28 | } 29 | void multi(float* A, int row_A, int col_A, float* B, int row_B, int col_B, float* C) { 30 | assert(row_A == row_B && col_A == col_B); 31 | for (int i = 0; i < row_A; i++) 32 | for (int j = 0; j < col_A; j++) 33 | C[col_A * i + j] = A[col_A * i + j] * B[col_A * i + j]; 34 | } 35 | 36 | void compareResult(float* A, float* B, int len) { 37 | int correct = -1; 38 | for (int i = 0; i < len; i++) { 39 | if (A[i] != B[i]) { 40 | correct = i; 41 | break; 42 | } 43 | } 44 | if (correct != -1) 45 | printf("error in index %d\r\n", correct); 46 | else 47 | printf("same\r\n"); 48 | } -------------------------------------------------------------------------------- /winograd/src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H__ 2 | #define __COMMON_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern float A[8]; 10 | extern float AT[8]; 11 | extern float B[16]; 12 | extern float BT[16]; 13 | extern float G[12]; 14 | extern float GT[12]; 15 | 16 | double what_time_is_it_now(); 17 | void dot(float* A, int row_A, int col_A, float* B, int row_B, int col_B, float* C); 18 | void multi(float* A, int row_A, int col_A, float* B, int row_B, int col_B, float* C); 19 | void compareResult(float* A, float* B, int len); 20 | 21 | #endif /* __COMMON_H__ */ -------------------------------------------------------------------------------- /winograd/src/conv0.h: -------------------------------------------------------------------------------- 1 | #ifndef __CON0_H__ 2 | #define __CON0_H__ 3 | 4 | float im2col_get_pixel(float* im, int height, int width, int channels, int row, int col, int channel, int pad); 5 | void im2col_cpu(float* data_im, int channels, int height, int width, int ksize, int stride, int pad, float* data_col); 6 | void gemm_nn(int M, int N, int K, float ALPHA, float* A, int lda, float* B, int ldb, float* C, int ldc); 7 | void gemm_cpu(int TA, int TB, int M, int N, int K, float ALPHA, float* A, int lda, float* B, int ldb, float BETA, 8 | float* C, int ldc); 9 | void gemm(int TA, int TB, int M, int N, int K, float ALPHA, float* A, int lda, float* B, int ldb, float BETA, float* C, 10 | int ldc); 11 | 12 | #endif /* __CON0_H__ */ -------------------------------------------------------------------------------- /winograd/src/winograd1.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINOGRAD1_H__ 2 | #define __WINOGRAD1_H__ 3 | 4 | void im2col_winograd1(float* data_im, int channels, int height, int width, int ksize, int stride, int m, int r, 5 | float* data_col); 6 | void winograd1_2d(float* U, float* d, float* result); 7 | void convolutional_winograd1(float* g, float* d, float* result, int height, int width, int channels, int n, int m, 8 | int r); 9 | void col2im_winograd1(float* temp_c, int n, int height, int width, int ksize, int stride, int pad, int m, float* c); 10 | 11 | #endif /* __WINOGRAD1_H__ */ -------------------------------------------------------------------------------- /winograd/src/winograd2.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINOGRAD2_H__ 2 | #define __WINOGRAD2_H__ 3 | 4 | void transforme_g_winograd2(float* g, float* transformed_g, int c, int n); 5 | void winograd2_2d(float* U, float* d, float* result); 6 | void convolutional_winograd2(float* transformed_g, float* d, float* result, int height, int width, int channels, int n, 7 | int m, int r); 8 | 9 | #endif /* __WINOGRAD2_H__ */ -------------------------------------------------------------------------------- /winograd/src/winograd3.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINOGRAD3_H__ 2 | #define __WINOGRAD3_H__ 3 | 4 | void im2col_winograd3(float* data_im, int channels, int height, int width, int ksize, int stride, int m, int r, 5 | float* data_col); 6 | void winograd3_1d(float* d, float* g, float* m); 7 | void winograd3_2d(float* transformed_k, float* g, float* R); 8 | void convolutional_winograd3(float* g, float* transformed_k, float* R, int height, int width, int channels, int n, 9 | int m, int r); 10 | 11 | #endif /* __WINOGRAD3_H__ */ -------------------------------------------------------------------------------- /winograd/src/winograd4.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINOGRAD4_H__ 2 | #define __WINOGRAD4_H__ 3 | 4 | void transforme_g_winograd4(float* g, float* transformed_g, int c, int n); 5 | void winograd4_1d(float* d, float* transformed_g, float* m); 6 | void winograd4_2d(float* transformed_k, float* transformed_g, float* R); 7 | void convolutional_winograd4(float* transformed_g, float* transformed_k, float* R, int height, int width, int channels, 8 | int n, int m, int r); 9 | 10 | #endif /* __WINOGRAD4_H__ */ -------------------------------------------------------------------------------- /winograd/src/winograd5.h: -------------------------------------------------------------------------------- 1 | #ifndef __WINOGRAD5_H__ 2 | #define __WINOGRAD5_H__ 3 | 4 | void winograd5_2d(float* U, float* d, float* result); 5 | void convolutional_winograd5(float* transformed_g, float* d, float* result, int height, int width, int channels, int n, 6 | int m, int r); 7 | 8 | #endif /* __WINOGRAD5_H__ */ --------------------------------------------------------------------------------