leetcode刷题总结

专栏

  • 专栏
    • c语言基础
    • 数据结构与算法
      • 博客
      • 分类
        • 数据结构
        • 算法
    • leetcode总结
  • 错题
    • 面试题 10.02. 变位词组
    • 罗马数字转整数

c语言基础

  • 二维数组

  • sizeof的用法

  • 宏定义:取最大值MAX&MIN

  • C语言中字符数组与字符串

    http://c.biancheng.net/cpp/html/2921.html

    字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了

    char str[7] = "abc123"; //正确
    char str[7];
    str = "abc123";  //错误
    //正确
    str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
    str[3] = '1'; str[4] = '2'; str[5] = '3';
    

    如果只初始化部分数组元素,那么剩余的数组元素也会自动初始化为“零”值,所以我们只需要将 str 的第 0 个元素赋值为 0,剩下的元素就都是 0 了。其他的值不是这样,必须循环赋值。

  • C语言字符串指针

    http://c.biancheng.net/cpp/html/80.html

    到底使用字符数组还是字符串常量

    在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。

    constchar *str = "http://c.biancheng.net";
    char str[] = "http://c.biancheng.net";
    

数据结构与算法

博客

  • leetcode中整数转字符的使用

  • C语言中库函数总结

  • 结构体

  • 数学

  • 字符串

  • leetcode简单题题型分类

  • 一维数组

分类

数据结构
  • Linked List 链表

  • Tree 树

  • Stack & Queue 栈&队列

  • Hash Table 哈希表

  • Array & Matrix 数列 矩阵

  • String 字符串

  • Graph 图

  • 位运算

  • bags

  • sets

算法
  • Two pointer 双指针

  • Sorting 排序

  • Greedy 贪心

  • Binary Search 二分

  • Divide and Conquer 分治

  • Searching 搜索

  • Dynamic Programing 动态

  • Math 数学

leetcode总结

  • leetcode字符串类型分类及说明

TIPS

qsort和memset参数位置

qsort(str,len,sizeof(int),cmp)

memset(str,0,len * sizeof(int))以字节为单位的

刷题方法总结

https://zhuanlan.zhihu.com/p/96883783

算法不是拼智商,是可以通过刻意练习习得的

难度要循序渐进

解题:

1、看懂题目 (5分钟)

2、分析、推导解法(不要想代码如何实现,降低心智负担)

3、将思路转化为代码(注意算法封装)

乎友题目分类

https://www.zhihu.com/question/280279208

刷题方法:

  • 第一遍:可以先思考,之后看参考答案刷,结合其他人的题解刷。思考、总结并掌握本题的类型,思考方式,最优题解。

  • 第二遍:先思考,回忆最优解法,并与之前自己写过的解答作比对,总结问题和方法。

  • 第三遍:提升刷题速度,拿出一个题,就能够知道其考察重点,解题方法,在短时间内写出解答。

定期总结:

  • 按照题目类型进行总结:针对一类问题,总结有哪些解题方法,哪种方法是最优的,为什么。
  • 总结重点:有些题你刷了好多遍都还是不会,那就要重点关注,多思考解决方法,不断练习强化

注意优化

  1. 原题目

  2. 自己的第一遍解法

  3. 网上好的解法

  4. 自己可以改进的地方

  5. 进一步精简优化自己的代码直至代码简无可简(这是非常关键的一步,到达这一步,才会发现获得能力的提升远远要超过简单地把题目解出来

  6. 获得的思考(或者学习到的地方,可以是算法、数据结构或者Java的特性—例如Stream等等)

每一个题目都经过至少一遍这样的迭代。这样几遍下来,我对于每个题目都有了更加深刻的理解,大部分的题目我都有自信能够写出最优解甚至代码都是最优化的(至少比论坛回复里面的最高票答案还要精简)。

leetcode错误码总结:

Line 207: Char 3: runtime error: load of null pointer of type ‘int’ [Serializer.c]

https://blog.csdn.net/qq_41643701/article/details/112606486?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-3.no_search_link

解决办法:
1、使用malloc动态分配存储空间
2、使用static修饰该变量
3、使用全局变量存储

错题

1、面试题 10.02. 变位词组

https://leetcode-cn.com/problems/group-anagrams-lcci/

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
bool ArrayComp(int* x, int* y) {
    for(int i = 0; i < 26; i++) {
        if (x[i] != y[i]) {
            return false;
        }
    }
    return true;
}
char*** groupAnagrams(char** strs, int strsSize, int* returnSize, int** returnColumnSizes){
    int len = strlen(strs[0]);
    char*** res = (char***)malloc(sizeof(char**) * strsSize);//可以没有变位词组
    for (int i = 0; i < strsSize; i++) {
        res[i] = (char**)malloc(sizeof(char*) * strsSize);
        for (int j = 0; j < strsSize; j++) {
            res[i][j] = (char*)malloc(sizeof(char) * len);//len不对,每一个字符串的长度不一定是3个
        }
    }

    //为每一个字符串创建一个26位字典,用于存储字母出现的次数
    char **alpha = (char**)malloc(sizeof(char*) * strsSize);
    for(int i = 0; i < len; i++) {
        alpha[i] = (char*)malloc(sizeof(char) * len);//数据类型不对,应该是int
        memset(strs[i], sizeof(char) * len, 0);//26个字母的长度
    }
    memset(alpha, sizeof(char*) * strsSize, 0);

    for (int i = 0; i < strsSize; i++) {
        for (int j = 0; j < strlen(strs[i]); j++) {
            alpha[i][strs[i][j] - 'a']++;
        }
    }

    int comResult;
    //将每一个字符串的字典序数组进行对比,全等的输出序号
    for (int i = 0; i < strsSize; i++) {
        if (alpha[i])
        for(int j = i + 1; j < strsSize; j++) {
           comResult = ArrayComp(alpha[i], alpha[j]);
           if (comResult) {
               res[i] = strs[i];
           } 
        }
    }
}
  • *returnSize和**returnColumnSizes

*returnSize代表行,可以通过指针带入子函数然后再带出来

如果直接是int型,会作为局部变量变化,子函数结束,出栈销毁(联想swap函数)

所以在main函数中的入参会是&returnSize,子函数形参*returnSize

**returnColumnSizes代表列,每一行对应的列数都不是固定的,所以需要用一个一维数组来存放,如果只是一维数组,在子函数中赋值不生效,带不出来,所以需要和行一样,取地址入参,一维数组再取地址就是一个二级指针。

参考材料:

https://www.jianshu.com/p/613584a38a08

https://leetcode-cn.com/circle/discuss/V3ozv1/

https://blog.csdn.net/AuthurWhywat/article/details/105510874

等价写法:

分配列指针空间

    //returnColumnSizes[0] = malloc(sizeof(int) * strsSize);
    //等价写法,一个是从数组的角度(行指针),一个从指针的角度(一维数组的地址),代表一维数组名即首地址
    *returnColumnSizes = malloc(sizeof(int) * strsSize);

每一行写入对应列数

        //returnColumnSizes[0][*returnSize] = n;//第*returnSize行有几列?
        (*returnColumnSizes)[*returnSize] = n;  //列数是个一维数组,声明成二维数组,跟行数道理一样,通过取一维数组的地址的方法将一维数组带出来

2、罗马数字转整数

哈希表:(经典题型:字母次数,键值转化)

  1. 字母次数

    https://leetcode-cn.com/problems/roman-to-integer/submissions/

        while (s[i] != '\0') {
            hashtable[s[i] - 'a']++;
            i++;
        }
    //for循环也可以
    

    不符合题意的,可以在最后写return -1;一旦遇到符合题意的,直接在for循环中,返回。

  2. 键值转化

  • 利用字母表

https://leetcode-cn.com/problems/roman-to-integer/solution/c-jie-fa-mei-shi-yao-jiu-na-yang-by-ning-junzhi/

    int hashmap[26] ={0};
        hashmap['I'-'A'] = 1;
        hashmap['V'-'A'] = 5;
        hashmap['X'-'A'] = 10;
        hashmap['L'-'A'] = 50;
        hashmap['C'-'A'] = 100;
        hashmap['D'-'A'] = 500;
        hashmap['M'-'A'] = 1000;
  • 利用两个数组构造哈希表

https://leetcode-cn.com/problems/roman-to-integer/solution/cyu-yan-by-song-hua-niang-jiu/
<<<<<<< HEAD

int ReverseNum(char s)
{
    int i = 0;
    char romanTable[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
    int value[] = {1, 5, 10, 50, 100, 500, 1000};

    while(s != romanTable[i]) {
        i++;
    }

    return value[i];
}

3、8. 字符串转换整数 (atoi)

https://leetcode-cn.com/problems/string-to-integer-atoi/submissions/

10的次幂的计算

C语言中10的次幂的计算使用pow(10, x),而不是^,这是VB中的用法,在C语言中会出错,表示异或

不同数据类型的格式化输出

不同数据类型的长度范围:

https://blog.csdn.net/lyl0625/article/details/7350045#:~:text=%E5%88%9B%E4%BD%9C%E4%B8%AD%E5%BF%83-,%E5%9C%A8C%E8%AF%AD%E8%A8%80%E4%B8%AD%EF%BC%8Cdouble%E3%80%81long%E3%80%81unsigned%E3%80%81int,%E6%89%80%E5%8D%A0%E5%AD%97%E8%8A%82%E6%95%B0&text=1byte%20%3D%208bit%20%E4%B8%80%E4%B8%AA%E5%AD%97%E8%8A%82,4%E4%B8%AA%E5%AD%97%E8%8A%82long

-65536~65535,为啥负数会比正数多1?

由于0没有-0这个说法,所以负数会比正数多一位

https://blog.csdn.net/zggzgw/article/details/53710822

如果int和long在某一操作系统中具有相同的长度,使用%d

如果不是,int使用%d,long使用%ld,longlong使用%lld

在leetcode系统中,可以使用atoi,但是不能使用itoa,使用sprintf(str,%d,num)

关于整型数组初始化和字符数组初始化

https://blog.csdn.net/Lucien_zhou/article/details/66529426

C语言中,字符是按其对应的ASCII码存储的,一个字符一个字节,字符常量可以用“%d”输出,看齐对应的ASCII码是多少

int a[10] = {0}; // 初始化成整型0
char a[10] = {0}; // ASCII码0,对应‘\0’
#include
#include
#include
#include
                   
int main() {
    char str[10] = {48};
    char a[10] = {0};
    printf("%c~", str[0]); // 字符‘0’,对应ascii码48
    printf("%d-", str[1]); // 48
    printf("%d-", a[1]);	// 0
    printf("%c~", a[0]);	// ‘\0’ 不显示 空字符 对应NULL
    system("pause");
    return 0;
}

“2 0000 0000 0000 0000 000” 2*10^19 用long型去装变成了-9223372036854775808

如何判断溢出

long sum;
(int) sum != sum;
  1. 只要将sum强转成int型看是否有变化,如果有变化就说明溢出,如果用sum > INT_MAX会有影响,因为相加超过long型会溢出,带来有符号数,影响判断。https://zhuanlan.zhihu.com/p/56203662

  2. 如果想采用sum > INT_MAX,需要在循环内部做,声明成long类型,一旦大于int类型返回,而不是像方法1一样将整个数都换算过来

优化:

  1. 使用纯指针的方法(做一下,熟悉指针和数组的关系)

https://leetcode-cn.com/problems/string-to-integer-atoi/solution/chun-c-by-hua-jia-wang-tie-nan-2/

  1. 循环内部做边界判断

https://leetcode-cn.com/problems/string-to-integer-atoi/solution/shuang-100yi-li-jie-si-lu-by-yi-ke-da-ba-zzzp/

https://leetcode-cn.com/problems/implement-strstr/

4、136. 只出现一次的数字

int singleNumber(int* nums, int numsSize){
    int hashmap[100000] = {0};
    for (int i = 0; i < numsSize; i++) {
        hashmap[nums[i] + 10000]++;
    }
    for (int j = 0; j < 100000; j++) {
        if (hashmap[j] == 1) {
            return (j - 10000);
        }
    }
    return -1;
}

易错点:非空整数数组要考虑到负数,int是有符号数

改进点:投机取巧使用了100000这种数字,空间浪费

知识点:

  1. strlen只能对字符数组使用,参数必须是char*,仅用于字符串

    https://www.cnblogs.com/zpcdbky/p/5857656.html

  2. 求数组长度

    主函数:sizeof(arr)/sizeof(arr[0])

    子函数:入参数组名是个指针,用上述方法得到1

优化:

  1. 位运算,aba = b

5、1249. 移除无效的括号

栈+双指针

栈:数组,记录括号位置的数组,长度为左括号的个数

双指针的while写法,用于跳格赋值

    while (p1 < len) {
        if (s[p1] != -1) {
            s[p2++] = s[p1++];
        } else {
            p1++;
        }
    }

6、121. 买卖股票的最佳时机

math.h中的函数,使用时需要在.c文件中include

作用是返回两个浮点参数中较大的一个

result = fmax(max - min, result);

也可以用于int型比较大小,低精度向高精度隐式转换

7、16. 最接近的三数之和

绝对值:abs

memset相关知识:以字节为单位赋值的

在数组赋值中:

char a5[3] = {1}	// 这行代码之后,数组a5各项值分别为1,0,0

如果初始化时指定的的元素个数比数组大小少,剩下的元素都会被初始化为0

使用memset赋值

memset(a5, 1, 3);

memset的根据针对字节进行操作的,因此以下代码并不会得到我们期待的结果。

int a6[3];
memset(a6, 1, 3 * sizeof(int));    //在32位机器下,a6[0]~a6[2]的值均为16843009。

原因见:https://blog.csdn.net/my_offer/article/details/6936242

使用memcpy给数组赋值

https://blog.csdn.net/Hollay/article/details/88565249

1、结构体

2、内存复制

3、逐个赋值

#include
int a[20],b[20];
memcpy(a,b,sizeof(a));//令a数组等于b数组

超时可能是因为打印太多导致,可以去掉循环打印

8、哈希表

资料链接:

https://zhuanlan.zhihu.com/p/340692819

https://www.csdn.net/tags/MtTaAg2sNzgyMTM1LWJsb2cO0O0O.html

哈希表与结点

https://blog.csdn.net/jiangrongjr/article/details/99665129?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

先查再找

查:

HASH_FIND_INT(hashtable, &ikey, tmp);c

查不到,添加

添加:

HASH_ADD_INT(hashtable, key, tmp);

具体使用模板

1)初始化

#include "uthash.h"
struct my_struct{
    int ikey;
    char value[10];
    UT_hash_handle hh;
};
struct my_struct *g_users = NULL;

ikey的数据类型决定了后面插入和查找调用的接口函数

句柄hh不需要初始化,但必须要定义

定义hash结构指针g_users,用于指向保存数据的hash表,必须初始化为空,在后面的查、插等操作中,uthash内部会根据其是否为空而进行不同操作

2)查找

struct my_struct *find_users(int ikey)
{
    struct my_struct *s; //哈希表的结点
    HASH_FIND_INT(g_users, &ikey, s);
    return s;
}

3)插入

void add_user(int ikey, char *value_buf)
{
    struct my_struct *s;
    HASH_FIND_INT(g_users, &ikey, s);
    if (s == NULL) {
        s = (struct my_stuct*)malloc(sizeof(struct my_struct));
        s->ikey = ikey;
        HASH_ADD_INT(g_users, ikey, s);
    }
    strcpy(s->value, value_buf);
}
  • 插入时,需要先查找,当键不在当前的hash表中时再进行插入,以确保键的唯一性
  • 需调用插入接口函数时需要明确告诉接口函数,自己定义的键变量的名字是什么。

4)删除

void delete_user(int ikey) {
    struct my_struct *s = NULL;
    HASH_FIND_INT(g_users, &ikey, s);
    if (s==NULL) {
      HASH_DEL(g_users, s); 
      free(s);            
    }
}

告诉接口哪个hash表:g_users,哪个节点:s

5)清空hash表

void delete_all() {
  struct my_struct *current_user, *tmp;
 
  HASH_ITER(hh, users, current_user, tmp) {
    HASH_DEL(g_users,current_user);  
free(current_user);            
  }
}

函数使用:

资料参考:https://www.bilibili.com/read/cv3554180

为什么函数会有出参?

因为函数可能需要很多传出参数,如果此时还使用返回值作为输出参数,无法输出多个参数

此时需要使用出参

void copy(const int a, int *b);
copy1(foo, &bar);

*b是指向int的指针,&bar是int型数据bar的地址

不能写成b,因为即使给b赋值了,只要函数执行结束,栈销毁了,b的值无法保存

返回值此时作为函数是否执行成功的一个判断

sprintf的用法

http://c.biancheng.net/cpp/html/295.html

no itoa but have atoi

sprintf(buf, "%d", x1)

%d是x1的格式

DFS & BFS

https://developer.aliyun.com/article/756316

DFS:

从未被访问的顶点V开始,沿着一条路一直走到底,从这一条路尽头的节点回退到上一个节点,再从另一条路开始走到底…,不断递归重复此过程,直到所有的顶点都遍历完成

方法:

1、递归:

容易理解

层级很深,容易栈溢出

2、非递归实现:

通过压栈和出栈的方式实现二叉树的前序遍历

先遍历当前节点

回溯与递归的区别:

回溯 = 递归 + 剪枝

二分查找

二分查找也称折半查找(Binary Search),效率高,

1、前提是数据结构必须先排好序,可以在数据规模的对数时间复杂度O(mlogn)内完成查找。

2、二分查找要求线性表具有随机访问的特点(例如数组)

3、要求线性表能根据中间元素的特点推测它两侧的性质,以达到缩减问题规模的效果

T240 已做

T33

1712. 将数组分成三个子数组的方案数

简单题:

704. 二分查找
35. 搜索插入位置
int searchInsert(int* nums, int numsSize, int target)
{
    int left = 0;
    int right = numsSize - 1; // right指向最后一个元素的索引

    while (left <= right)
    {
        int mid = left + (right - left) / 2; // 等价于left + (right - left) / 2,但有效防止了 left 和 right 太大直接相加导致溢出
        if (nums[mid] == target)
            return mid;
        else if (nums[mid] < target)
            left = mid + 1; // [mid + 1, right]
        else if (nums[mid] > target)
            right = mid - 1; // [left, mid - 1]
    }
    return left; // 纯二分搜索[704]是返回return -1,这里要按顺序插入,所以返回left索引位置
}

中等题:

875. 爱吃香蕉的珂珂
1011. 在 D 天内送达包裹的能力

题型总结:

二分搜索的原型就是在「有序数组」中搜索一个元素 target,返回该元素对应的索引。(原始–704)

如果该元素不存在,那可以返回一个什么特殊值,这种细节问题只要微调算法实现就可实现。(微调–35)

还有一个重要的问题,如果「有序数组」中存在多个 target 元素,那么这些元素肯定挨在一起,这里就涉及到算法应该返回最左侧的那个 target 元素的索引还是最右侧的那个 target 元素的索引,也就是所谓的「搜索左侧边界」和「搜索右侧边界」,这个也可以通过微调算法的代码来实现。(多个target–875)

232. 用栈实现队列

https://leetcode-cn.com/problems/implement-queue-using-stacks/solution/232yong-zhan-shi-xian-dui-lie-by-hs-zhan-40gq/

单调栈

参考资料: 代码随想录

https://www.cnblogs.com/liang24/p/14200734.html

peek 回到栈顶 stack[pop]

pop 弹出栈 top–

使用场景:寻找任一个元素的右边或左边第一个比自己大或小的元素的位置,可以想到用单调栈

本质上是空间换时间,用一个栈记录我们遍历过的元素

1.单调栈里存放元素的下标i就可以了,如果使用对应的元素,直接T[i]就可以获取

2.求元素右边第一个比自己大的元素位置:栈顶到栈底递增

求元素右边第一个比自己小的元素位置:栈顶到栈底递减

739. 每日温度

https://leetcode-cn.com/problems/daily-temperatures/

https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

496. 下一个更大元素 I

动画演示:https://leetcode.cn/problems/next-greater-element-i/solution/dong-hua-yan-shi-dan-diao-zhan-496xia-yi-ql65/
两种解法:https://leetcode.cn/problems/next-greater-element-i/solution/cyu-yan-liang-chong-jie-fa-bao-li-fa-dan-5e9r/

技巧:将数组翻倍 i % n, n = 2 * len

队列

优先队列(堆)

https://leetcode-cn.com/problems/seat-reservation-manager/

并查集

https://leetcode-cn.com/problems/number-of-provinces/

岛屿数量已做,可以研究下dfs和并查集的区别

用法场景:

1、维护无向图的连通性。支持判断两个点是否在同一连通块内,和。
2、判断增加一条边是否会产生环:用在求解最小生成树的Kruskal算法里。

概念:

https://blog.csdn.net/liujian20150808/article/details/50848646

模板:

https://segmentfault.com/a/1190000004023326

并:union

查:find

涉及路径压缩(先不看)

使用数组的数据结构:

查找:

int Find_Set(int i)
{
    if (set[i] == i)
        return set[i];
    return Find_Set(set[i]);
}

并:

void Union(int i,int j)
{
    i=Find_Set(i);
    j=Find_Set(j);
    if(i==j) return ;
    if(rank[i]>rank[j]) set[j]=i;
    else
    {
        if(rank[i]==rank[j]) rank[j]++;   
        set[i]=j;
    }
}

前缀和 & HASH

523. 连续的子数组和

题目中如果有连续的限制则优先想到:滑动窗和前缀和

能够有效降低算法的时间复杂度

首先要构建前缀和数组

prefix[i] = prefix[i-1] + sum[i]

为什么会有-1,0 这一对键值对?

这是对一种情况的补充,即当前两个数和的余数为零时符合条件,则当循环到第二个数时 i =1, 此时 i - m[0] = 1 - (-1) = 2, 符合条件return true。(比如给出的k= 2, 第一个数是2,第二个数是4,那6也是k的倍数,但是按照一般规律,余数相等的两个索引相减,输出是index = 1 - 0 = 1,不符合子数组大小至少为2条件。提前存入-1,如果nums[0],nums[1]符合要求,会输出正确结果index = 1 - (-1)= 2)

差分

LeetCode 1109. 航班预订统计(差分思想)
LeetCode 1094. 拼车

使用场景:

差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减.

差分数组就是num[i] - nums[i - 1]组成的数组

概念:

原数组:    1 2 2 4
差分数组:  1 1 0 2
前缀和数组: 1 2 2 4

原数组————num[i] - nums[i - 1]--------> 差分数组 ————nums[i] + nums[i - 1]————> 原数组

核心算法:区间元素增减转变为两个位置增减

区间[l, r]增量为inc,对应差分数组d改变是d[l]增加inc,d[r+1]减少inc。

例:

原: 1 2 3 [0 ~ 1]增加1

差: 1 1 1 ——> 2 1 0

前: 2 3 3

滑动窗口

3. 无重复字符的最长子串

使用26个字母作为hash表的key值已经不可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHx1LJnw-1652316025236)(C:\Users\ProudSun\AppData\Roaming\Typora\typora-user-images\image-20220306122246573.png)]

hash[128] 128是ascii码表的长度

hash[256] 256是ascii码扩展表的长度

设计题

哈希表

查找、插入、删除,时间复杂度为O(1)

146 LRU缓存机制

707 设计链表

11.19考试总结:

等心声社区的软件认证社区出来之后,去看第二题和第三题的题解

第二题都不知道哪里错,部分通过,考虑一下算法和边界(被算法选择和边界的蹂躏还不够)

第三题是数组,其实对于数组的用法其实已经比较清楚了,为啥再设计算法的时候会频繁出错,

1、代码量大的做的比较少,有一定思路,但是在定位会在细节地方出错

continue和break都是跳出第一层循环

2、对于数组的小知识点总结的不够多,用也会用

解决两个问题:

1、如何看到题目能够想到最适合的算法?

将九阴真经中除了动态规划和贪心算法,其他所有题目都要做一遍,总结出模板

2、加大数组和字符串的练习,总结小知识点传到手机上没事就看,然后一定要自己做,锻炼思维

设计题看群里的总结题型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKDNsBQi-1640942784320)(C:\Users\ProudSun\AppData\Roaming\Typora\typora-user-images\image-20211121162007212.png)]

字符串和栈:

227. 基本计算器 II

//栈的声明
int stack[n];
//https://stackoverflow.com/questions/5368531/why-cant-i-create-an-array-of-size-n
//C99支持VLA,变长数组

字符串设计题:

比较经典

https://leetcode-cn.com/problems/design-log-storage-system/

不同的设计方法

1、设计单个结点,通过链表的方式循环插入

https://leetcode-cn.com/problems/design-log-storage-system/solution/c-yu-yan-tong-guo-shu-ru-de-grahuo-de-strncmpyao-p/

2、设计id与time的映射关系,二维数组

https://leetcode-cn.com/problems/design-log-storage-system/solution/zhe-dao-ti-kao-de-qi-shi-shi-zi-fu-chuan-9q93/

3、id与time的映射关系,字符串数组,index使用id

https://leetcode-cn.com/problems/design-log-storage-system/solution/strncmpbi-jiao-qiang-han-dai-ma-by-distracted-nobe/

leetcode错误类型总结:

cnblogs.com/lick468/p/10648771.html

https://blog.csdn.net/hanghangde/article/details/50371268

两数之和

回文数:总结整数除法的模板,以及sprintf的用法

设计题

memset

switch case:

case后面只能加整数或者能够转换成整数的字符

刷题大纲

数组

15、三数之和
16、最接近的三数之和
18、四数之和
42、接雨水 hard
75、颜色分类
78、子集
79、
84、
88、
90、
216、
287、
907、
969、

字符串

74
93
767
842

滑动窗口

76
567
438
3
424
1040

904

1208

二分法

704 绝对值
34 边界
875 模拟 最小值和最大值
1011 模拟

设计题

146 LRU缓存
359 日志速率限制器
380 O(1)时间插入、删除和获取
622 设计循环队列
635 设计日志存储系统
707 设计链表
1396 设计地铁系统
1845 座位预约管理系统
2013 检测正方形
剑指offer09 用两个栈实现队列

差分

1109
1904

前缀和

523

并查集

547

优先队列

1845

单调栈

739
84

https://codeantenna.com/a/Jv2CEZfkee

top一般是-1

https://www.cnblogs.com/liang24/p/14200734.html

哨兵法

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了

739.每日温度 求一个元素右边第一个更大的元素,单调栈就是递增的(从栈顶top到栈底是递增的)

  1. 柱状图中的最大的矩形 求一个元素右边第一个更小的元素,单调栈就是递减的

模板:

单调递增栈,当前元素大于栈顶元素,栈顶弹出,用当前元素更新栈顶;

当前元素小于等于栈顶元素,用当前元素入栈;

for (int i = 1; i < temperaturesSize; i++) {
	while (top > -1 && temperatures[i] > temperatures[stack[top]]) {
        ret[stack[top]] = i - stack[top];
        top--;
    }
    stack[++top] = i;
}

双指针

二分搜索:两端向中心的双指针,左右指针

滑动窗口:快慢指针

优先队列:

406

23

贪心

概念:局部最优——>整体最优

场景:尝试性推导,举反例无法推翻

贪心算法之区间调度问题

435. 无重叠区间

452. 用最少数量的箭引爆气球

  1. 看到取值范围在-231~231-1,qsort的cmp函数可能会溢出,使用? :双目表达式
  2. 有重叠的时候,需要每次更新end为重叠区间右边界的最小值,因为我们需要计算更多的重叠区间

扫描线技巧:安排会议室

253. 会议室 II

剪视频贪心算法

1024. 视频拼接

如何运用贪心思想玩跳跃游戏

45. 跳跃游戏 II

题目中说道,生成的测试用例可以到达nums[n - 1]

那只要能走到nums[n - 2]即可

55. 跳跃游戏

贪心没有套路,就是常识性推导就反例

860. 柠檬水找零

此题分类讨论,找到贪心的关键,有10元尽量用掉10元,因为5元用于找零,作用大

二叉树

数据结构的基本存储方式就是链式和顺序两种,基本操作就是增删查改,遍历方式无非迭代和递归。

遍历

dfs(递归)

bfs(队列)

https://leetcode.cn/problems/maximum-depth-of-binary-tree/solution/by-testlu-b3v7/

104. 二叉树的最大深度

深度:任意结点到根节点的距离 前序遍历

高度:任意结点到叶子节点的距离 后序遍历

递归遍历(DFS)、迭代遍历(非递归用栈)、层序遍历(队列)

递归三要素

1.确定递归函数的参数和返回值(根节点,返回值,大小)

2.确定终止条件(如果结点为空,结束)

3.确定单层递归的逻辑(前中后序遍历)

    if (root == NULL) {
        return NULL;
    }
    *returnSize = 0;
    // 需要将returnSize放在空指针判断前面
    // returnSize是个入参,答案空间是分配的,需要出参空间大小

144.二叉树的前序遍历

94.二叉树的中序遍历

145.二叉树的后序遍历

https://blog.csdn.net/My_Jobs/article/details/43451187 java版

https://blog.csdn.net/ygl184/article/details/124494665 C版

前中后序遍历,非递归,迭代法

https://leetcode.cn/problems/binary-tree-postorder-traversal/solution/by-xu-da-dao-wo-xiu-xian-liao-dma0/

层序遍历

102. 二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-xu-bian-li-cyu-yan-xi-jdk8/

模板:

https://leetcode.cn/circle/discuss/vkH0Y6/

// 0. 声明队列
queue q;
// 1. 将根节点加入队列
q.push(root);
// 2. 遍历队列中每个节点
while(!q.empty()) {
    TreeNode* cur = q.front();
    q.pop();
    // 3. 记录当前节点值
    vec.push(cur->val);
    // 4. 将左右孩子放入队列
    q.push(cur->left);
    q.push(cur->right);
}

分治法与遍历法是两种常见的递归方法

分治法:先让左右子树去解决同样的问题,然后得到结果之后,再整合为整棵树的结果。

遍历法:通过前序/中序/后序的某种遍历,游走整棵树,通过一个全局变量或者传递的参数来记录这个过程中所遇到的点和需要计算的结果。

区别:从程序实现角度分治法的递归函数,通常有一个返回值,遍历法通常没有。

动态规划:

动态规划每一个状态是由上一个状态推导出来的

贪心没有状态推导,而是从局部直接选最优的

dp[j] += dp[j - weight[i]
max = fmax(dp[j], dp[j - weight[i]] + value[i])

动态规划5部曲:

1.确定dp数组以及下标的含义

dp[max] = {0} // 凑成MAX有dp[MAX]方法

2.确定递推公式

dp[i] = dp[i-1] + dp[i - 2]

3.dp数组初始化

dp[0] = 1 // 无法解释,可以初始化dp[1], dp[2]

322. 零钱兑换

https://leetcode.cn/problems/coin-change/solution/chun-c-by-xun-ge-v-4nmv/

回溯

void backtracking(参数) {
	if (终止条件) {
		存放结果;
		return;
	}
	
	for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
		处理节点;
		backtracking(路径, 选择列表); // 递归 路径代表二叉树里的树枝
		回溯,撤销处理结果
	}
}

77. 组合

DFS、动态规划、回溯、递归之间的关系

https://www.zhihu.com/question/266403334

回溯一般都会用到DFS

回溯 = 递归 + 剪枝

递归:

函数中直接或间接调用自身,需要1)递归终止条件 2)递归过程

菲波纳切 f(n) = f(n -1) + f(n -2)

迭代:

利用变量的原值推算出变量的一个新值,递归是自己调用自己,迭代就是A不停地调用B。递归中一定有迭代,但迭代中不一定有递归,大部分可以相互转换。

能用迭代的不用递归,递归调用函数,浪费空间,并且递归太深容易造成堆栈溢出。

回溯法:

回溯法是一种优先搜索法,按优选条件向前搜索,以到达目标。但当搜索不到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通,退回再走的技术为回溯法,而满足回溯条件的某个状态点称为回溯点。

*returnSize与 *returnColumnSizes的意义

https://blog.csdn.net/weixin_45695709/article/details/123055736

**returnColumnSizes

第一个*是代表防止出参时,函数结束,栈销毁,要想在不同的函数间传递并修改这些值,必须再将这个一维数组的地址,因此使用二级地址returnColumnSizes来保存

第二个*是代表用一维数组来存储每一行的列数,数组索引代表行号,数组值代表列数

46. 全排列

https://leetcode.cn/problems/permutations/solution/46-quan-pai-lie-by-ac_fun-peev/

void dfs(int step)
{
判断边界;
forint i=1;i<=n;++i)//尝试每一种可能;
{
dfs(step+1);//继续下一步
}
返回;
}

337.打家劫舍

https://leetcode.cn/problems/house-robber-iii/solution/337-da-jia-jie-she-iii-by-dyzahng-k3b5/

时间模拟

1701. 平均等待时间

代码随想录图论部分:

深度优先搜索:

https://leetcode.cn/problems/all-paths-from-source-to-target/solution/by-carlsun-2-66pf/

广度优先搜索:

https://leetcode.cn/circle/discuss/V3FulB/

岛屿最大面积:

https://programmercarl.com/0695.%E5%B2%9B%E5%B1%BF%E7%9A%84%E6%9C%80%E5%A4%A7%E9%9D%A2%E7%A7%AF.html#dfs

图论相关可以查看:

https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-03a72/tu-lun-ji–d55b2/

图与多叉树的遍历是最常考的

是否能够到达所有节点,不需要回溯搜索一条可行路径时需要回溯

class Solution {
private:
    vector> result; // 收集符合条件的路径
    vector path; // 0节点到终点的路径
    // x:目前遍历的节点
    // graph:存当前的图
    void dfs (vector>& graph, int x) {
        // 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.size() - 1
        if (x == graph.size() - 1) { // 找到符合条件的一条路径
            result.push_back(path);
            return;
        }
        for (int i = 0; i < graph[x].size(); i++) { // 遍历节点n链接的所有节点
            path.push_back(graph[x][i]); // 遍历到的节点加入到路径中来
            dfs(graph, graph[x][i]); // 进入下一层递归
            path.pop_back(); // 回溯,撤销本节点
        }
    }
public:
    vector> allPathsSourceTarget(vector>& graph) {
        path.push_back(0); // 无论什么路径已经是从0节点出发
        dfs(graph, 0); // 开始遍历
        return result;
    }
};

图和多叉树最大的区别是,图是可能包含环的,你从图的某一个节点开始遍历,有可能走了一圈又回到这个节点,而树不会出现这种情况,从某个节点出发必然走到叶子节点,绝不可能回到它自身。

所以,如果图包含环,遍历框架就要一个 visited 数组进行辅助:

通过visited标记已经访问过的节点,防止成环节点,重复访问

如果无环,一定可以到达终点,则不需要visited数组

如果题目中要求寻找所有路径,需要声明栈来存储

深搜三部曲:

1.确认递归函数,参数

2.确认终止条件

3.处理目前搜索节点出发的路径

广搜使用场景

广搜的搜索方式就适合于解决两个点之间的最短路径问题。

代码随想录github题库:

https://github.com/youngyangyang04/leetcode-master

如果是二叉树的数据结构就要想到使用递归的方式

一般dfs

优先队列:

C语言扩展库:HW-CSTL V2.3

http://3ms.huawei.com/km/groups/3803117/blogs/details/8089929?l=zh-cn

例题:

http://3ms.huawei.com/km/groups/3803117/blogs/details/11136677?l=zh-cn

http://3ms.huawei.com/km/groups/3803117/blogs/details/12105923

函数原型总结:

http://3ms.huawei.com/km/blogs/details/10811679#%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97

leetcode题目:

https://leetcode.cn/problems/avoid-flood-in-the-city/

使用优先队列的库:

创建优先队列(带比较函数) ——> push入节点 (进入时已经排好序)——>弹出节点(输出)

VosDupFreeFuncPair 这个类型什么情况下使用?

就是初始化赋值

所需要花费的1分钟,主要是实现自定义数据结构、字符串所需要的拷贝函数 VosDupFreeFuncPair。如果元素是整型的话,就可以0分钟实现拉。

自定义数据结构例子:

结构体

https://openx.huawei.com/mkdocs/project/1186/hemyskills/docs/site/200-%E5%8F%AF%E4%BF%A1%E8%AE%A4%E8%AF%81/20-%E4%B8%8A%E6%9C%BA%E7%BC%96%E7%A8%8B/20201204-2020%E5%B9%B412%E6%9C%884%E6%97%A5%E5%B7%A5%E4%BD%9C%E7%BA%A7/03-%E7%AC%AC%E4%B8%89%E9%A2%98-%E9%9B%B6%E4%BB%B6%E4%BB%B7%E6%A0%BC%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/index.html?source=w3#2

字符串(通过map讲解)

http://3ms.huawei.com/hi/group/2030499/wiki_6713842.html

整型可以不写

http://3ms.huawei.com/km/groups/3803117/blogs/details/12105923

数组

http://3ms.huawei.com/km/groups/3803117/blogs/details/12105923

http://3ms.huawei.com/km/blogs/details/10811679#%E4%BC%98%E5%85%88%E9%98%9F%E5%88%97

集成环境使用leetcode-cstl

队列内部按照优先级排序,也叫堆排序,实现方式是二叉树

HW-CSTL库用法优先队列API用法:

vos_common_impl.h内部集成了默认的VOS_IntCmpFunc、VOS_StrCmpFunc

// int
int32_t VOS_IntCmpFunc(uintptr_t data1, uintptr_t data2)
{
    return (int32_t)(data1 - data2);
}
// 字符串
int32_t VOS_StrCmpFunc(uintptr_t data1, uintptr_t data2)
{
    char *tmpData1 = (char *)data1;
    char *tmpData2 = (char *)data2;

    return strcmp(tmpData1, tmpData2); 
}
// 结构体

创建优先队列

参考资料:

http://3ms.huawei.com/km/blogs/details/9535747

#include "vos_priorityqueue.h"

// 输入单个整数,第二个参数为NULL
VosPriQue *PriorityQueue = VOS_PriQueCreate(VOS_IntCmpFunc, NULL);

// 输入是数组,需要输入int变量的地址
// VOS_PriQuePushBatch 压入的不是int值,而是int变量的地址
static inline void *dupFunc(void *src)
{
    /* 将地址内存储的值取出并返回 */
    return (void *)(uintptr_t)(*(int32_t *)src);
}
VosDupFreeFuncPair dataFunc = {dupFunc, NULL};	// 
int32_t data[12] = {6, 12, 32, 4, 0, 1, 43, 11, 23, 12, 10000, -1};
priQueue = VOS_PriQueCreate(VOS_IntCmpFunc, &dataFunc);

入队(单个整型、整型数组、结构体)

// int
VOS_PriQuePush(priQueue, value);
// array
for (int i = 0; i < ARRAY_SIZE(data); i++) {	// http://3ms.huawei.com/km/blogs/details/10811679
    VOS_PriQuePush(pq, (uintptr_t)(data + i));
}

if (VOS_PriQuePushBatch(priQueue, data, 12, sizeof(int32_t)) != 0) { // 使用VOS_PriQuePushBatch
    return;
}

队头元素

VOS_PriQueTop(PriorityQueue);

出队(单个整型、)

VOS_PriQuePop(PriorityQueue);

计算队列大小

VOS_PriQueSize

创建和压入队列已经整理完毕,

D:\Documents\Files\GSA\自己整理\资料\可信

看下各种数据结构优先队列的用法

你可能感兴趣的:(leetcode总结,leetcode,算法,数据结构)