zoukankan      html  css  js  c++  java
  • Leetcode学习笔记(1)

    scrapy爬虫的学习告一段落,又因为现在在学习数据结构,做题平台是lettcode:https://leetcode-cn.com/

    每周都要交一次做题的笔记,所以把相关代码和思路同时放在博客上记录

    题目1 ID1

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

    示例:

    给定 nums = [2, 7, 11, 15], target = 9

    因为 nums[0] + nums[1] = 2 + 7 = 9

    所以返回 [0, 1]

    我的解答:

    /**
    
     * Note: The returned array must be malloced, assume caller calls free().
    
     */
    
    int* twoSum(int* nums, int numsSize, int target, int* returnSize){
    
        int i,j;
    
        int* num;
    
        for(i=0;i<numsSize;i++){
    
            for(j=i+1;j<numsSize;j++){
    
                if(nums[i]+nums[j]==target){
    
                    *returnSize=2;
    
                    num=(int*)malloc(sizeof(int)*2);
    
                    num[0]=i;
    
                    num[1]=j;
    
                    return num;
    
                }
    
            }
    
        }
    
        return 0;
    
    }
    

      

    通过嵌套两层for循环的方式暴力求解,返回得到的下标值

    题目2 ID7

    给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

    示例 1:

    输入: 123

    输出: 321

     示例 2:

    输入: -123

    输出: -321

    示例 3:

    输入: 120

    输出: 21

    注意:

    假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231,  231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0

    我的解答:

    #define MAX 2147483647
    #define MIN -2147483648
    int reverse(int x){
        long num=0;
        while(x!=0){
            num=num*10+x%10;
            x=x/10;
        }
        return (num>MAX||num<MIN)?0:num;
    }
    

      

    用时0ms,内存消耗6.9MB

    最开始num定义的数据类型是int,提交的时候出错了,最后一次的输入将其爆掉了,看了一下题目,意思应该是我们return 结果的那个测试环境,只能储存32位有符号整数,并不是说numint,所以修改其为long

    题目3 ID9

     

     判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

    示例 1:

    输入: 121

    输出: true

    示例 2:

    输入: -121

    输出: false

    解释: 从左向右读, -121 。 从右向左读, 121- 。因此它不是一个回文数。

    示例 3:

    输入: 10

    输出: false

    解释: 从右向左读, 01 。因此它不是一个回文数。

    进阶:

    你能不将整数转为字符串来解决这个问题吗?

    我的解答:

    bool isPalindrome(int x){
        if(x>=0&&x<=9){
            return true;
        }else if(x<0){
            return false;
        }else{
            long temp,num=0;
            temp=x;
            while(temp!=0){
                num=num*10+temp%10;
                temp=temp/10;
            }
            if(x==num){
                return true;
            }else{
                return false;
            }
        }
    }
    

      

    用时12ms,内存7.1MB

    对于正个位数可知是回文数,负数因为符号的存在而必然不会是回文数,两者都可以直接返回,对于大于9的整数我们可以像上一道题一样先求出它的整数的反转数,然后与其原数相比较,基于回文数的定义,如果是回文数,两数应当相等,如果不是则不等,再返回相应的布尔值即可。需要注意的是测试数字可能很大,所以我们将numtemp都设置成long型。

    题目4 ID13

     

     罗马数字包含以下七种字符: I V X LC M

    字符          数值

    I             1

    V             5

    X             10

    L             50

    C             100

    D             500

    M             1000

    例如, 罗马数字 2 写做 II ,即为两个并列的 112 写做 XII ,即为 X + II 27 写做  XXVII, 即为 XX + V + II 

    通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

    可以放在 V (5)  X (10) 的左边,来表示 4 9

    可以放在 L (50)  C (100) 的左边,来表示 40  90 

    可以放在 D (500)  M (1000) 的左边,来表示 400  900

    给定一个罗马数字,将其转换成整数。输入确保在3999 的范围内。

    示例 1:

    输入: "III"

    输出: 3

    示例 2:

    输入: "IV"

    输出: 4

    示例 3:

    输入: "IX"

    输出: 9

    示例 4:

    输入: "LVIII"

    输出: 58

    解释: L = 50, V= 5, III = 3.

    示例 5:

    输入: "MCMXCIV"

    输出: 1994

    解释: M = 1000, CM = 900, XC = 90, IV = 4.

    我的解答:

    可以看到在题目中给出了六种特殊情况,如果没有特殊情况的话我们只需要遍历传进来的字符串,进行每一位的变化就可以了,有了六种特殊情况之后第一反应就是if...else进行分支

    另外,在罗马数字中,小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如=8、Ⅻ=12; 小的数字(限于 Ⅰ、X C)在大的数字的左边,所表示的数等于大数减小数得到的数,如 Ⅳ=4、Ⅸ=9 懂得这个可以写出更好的代码

    int romanToInt(char * s){
        int i=0;
        int sum=0;
        for(i=0;s[i];i++){
            switch(s[i]){
                case 'M':sum+=1000;break;
                case 'D':sum+=500;break;
                case 'L':sum+=50;break;
                case 'V':sum+=5;break;
                case 'C':
                    if(s[i+1]=='D'){
                        sum+=400;
                        i++;
                    }else if(s[i+1]=='M'){
                        sum+=900;
                        i++;
                    }else{
                        sum+=100;
                    }
                    break;
                case 'X':
                    if(s[i+1]=='L'){
                        sum+=40;
                        i++;
                    }else if(s[i+1]=='C'){
                        sum+=90;
                        i++;
                    }else{
                        sum+=10;
                    }
                    break;
                case 'I':
                    if(s[i+1]=='V'){
                        sum+=4;
                        i++;
                    }else if(s[i+1]=='X'){
                        sum+=9;
                        i++;
                    }else{
                        sum+=1;
                    }
                    break;
            }
        }
        return sum;
    }
    

      

    题目5 ID面试题64

     

      1+2+...+n ,要求不能使用乘除法、forwhileifelseswitchcase等关键字及条件判断语句(A?B:C)。

    示例 1

    输入: n = 3

    输出: 6

    示例 2

    输入: n = 9

    输出: 45

    限制:

    1 <= n <= 10000

    我的解答:

    C/C++的语言特性,使用&&导致逻辑短路

    即左侧的表达式为假时整个表达式后续将不再进行评估,算是奇技淫巧吧。

    int sumNums(int n){
        int sum=n;
        n&&(sum+=sumNums(n-1));
        return sum;
    }
    

      

    题目6 ID面试题14

     

     编写一个函数来查找字符串数组中的最长公共前缀。

    如果不存在公共前缀,返回空字符串 ""

    示例 1:

    输入: ["flower","flow","flight"]

    输出: "fl"

    示例 2:

    输入: ["dog","racecar","car"]

    输出: ""

    解释: 输入不存在公共前缀。

    说明:

    所有输入只包含小写字母 a-z 

    我的解答:

    char * longestCommonPrefix(char ** strs, int strsSize){
        int i,j;
        char* ans=strs[0];
        if(strsSize==0){
            return "";
        }
        for(i=1;i<strsSize;i++){
            for(j=0;ans[j]!=''&&strs[i][j]!='';j++){
                if(ans[j]!=strs[i][j]){
                    break;
                }
            }
            ans[j]='';
            if(ans==NULL){
                return "";
            }
        }
        return ans;
    }
    

      

    ans指向第一个字符串,跟每一个字符串进行比较,当有一位不同的时候退出内层循环,并且将ans截断,如果ansNULL的话,证明没有一位相同,即无最长公共前缀。否则返回截断得到的ans

    题目7 ID20

     

     给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。

    有效字符串需满足:

    左括号必须用相同类型的右括号闭合。

    左括号必须以正确的顺序闭合。

    注意空字符串可被认为是有效字符串。

    示例 1:

    输入: "()"

    输出: true

    示例 2:

    输入: "()[]{}"

    输出: true

    示例 3:

    输入: "(]"

    输出: false

    示例 4:

    输入: "([)]"

    输出: false

    示例 5:

    输入: "{[]}"

    输出: true

    我的解答:

    bool isValid(char * s){
        int i,top=-1;
        int length=strlen(s);
        char* cha=(char*)malloc(length);
        if(s==NULL){
            return true;
        }
        if(length%2!=0){
            return false;
        }
        for(i=0;i<length;i++){
            if(s[i]=='('||s[i]=='{'||s[i]=='['){
                cha[++top]=s[i];
            }else if(top==-1){
                return false;
            }else if(cha[top]+1==s[i]||cha[top]+2==s[i]){
                top--;
            }else{
                return false;
            }
        }
        return top==-1;
    }
    

      

    左括号先储存在一个数组里面,当遇到右括号的时候弹出,先进后出的结构我们用类似于栈来实现,然后考虑几种情况即可,判断是否匹配的时候使用一个左括号对应的右括号的ASCII来比较。

    题目8 ID21

     将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

    示例:

    输入:1->2->4, 1->3->4

    输出:1->1->2->3->4->4

    我的解答:

    题目给出了指针结构体的格式

    /**
     * Definition for singly-linked list.
     * struct ListNode {
     *     int val;
     *     struct ListNode *next;
     * };
     */
    
    struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
        if(l1==NULL){
            return l2;
        }
        if(l2==NULL){
            return l1;
        }
        if(l1->val<l2->val){
            l1->next=mergeTwoLists(l1->next,l2);
            return l1;
        }else{
            l2->next=mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
    

      

    我们递归调用该函数即可按照大小顺序排列链接链表,需要考虑的问题是返回哪一个链表,我们也可以重新开一个链表,为了方便直接使用l1l2中的一个,通过比较第一个值来确定返回值,接着递归调用即可

    题目9 ID26

     给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

    不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

    示例 1:

    给定数组 nums = [1,1,2],

    函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2

    你不需要考虑数组中超出新长度后面的元素。

    示例 2:

    给定 nums = [0,0,1,1,1,2,2,3,3,4],

    函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4

    你不需要考虑数组中超出新长度后面的元素。

    说明:

    为什么返回数值是整数,但输出的答案是数组呢?

    请注意,输入数组是以引用方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

    你可以想象内部操作如下:

    // nums 是以引用方式传递的。也就是说,不对实参做任何拷贝

    int len = removeDuplicates(nums);

    // 在函数里修改输入数组对于调用者是可见的。

    // 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。

    for (int i = 0; i < len; i++) {

        print(nums[i]);

    }

    我的解答:

    int removeDuplicates(int* nums, int numsSize){
        int i,j;
        if(nums==NULL||numsSize==0){
            return 0;
        }
        for(i=1,j=1;i<numsSize;i++){
            if(nums[i]!=nums[i-1]){
                nums[j]=nums[i];
                j++;
            }
        }
        return j;
    }
    

      

    双指针法

    题目10 ID53

     

     给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

    示例:

    输入: [-2,1,-3,4,-1,2,1,-5,4],

    输出: 6

    解释连续子数组 [4,-1,2,1] 的和最大,为 6

    进阶:

    如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

    我的解答:

    看浙大数据结构的时候有相同的题目,特意来做的,maxnum需要设置大一点才能过审,当thisnum小于0的时候,对于连续子列和的增大就没有帮助了,应当舍弃。更精妙的分治法以后改进。

    int maxSubArray(int* nums, int numsSize){
        int i,thissum=0,maxsum=-2147483648;
        for(i=0;i<numsSize;i++){
            thissum+=nums[i];
            if(thissum>maxsum){
                maxsum=thissum;
            }
            if(thissum<0){
                thissum=0;
            }
        }
        return maxsum;
    }
    

      

    题目11 ID383

     

     给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成。如果可以构成,返回 true ;否则返回 false

    (题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。)

    注意:

    你可以假设两个字符串均只含有小写字母。

    canConstruct("a", "b") -> false

    canConstruct("aa", "ab") -> false

    canConstruct("aa", "aab") -> true

    我的解答:

    最开始的答案是:

    bool canConstruct(char * ransomNote, char * magazine){
        int ransomNotenum[26],magazinenum[26];
        int i=0;
        while(ransomNote[i]){
            ransomNotenum[ransomNote[i]-'a']++;
            i++;
        }
        i=0;
        while(magazine[i]){
            magazinenum[magazine[i]-'a']++;
            i++;
        }
        for(i=0;i<26;i++){
            while(ransomNotenum[i]){
                ransomNotenum[i]--;
                magazinenum[i]--;
            }
        }
        for(i=0;i<26;i++){
            if(magazinenum[i]<0){
                return false;
            }
        }
        return true;
    }
    

      

    时间超时了,进一步修改

    合并为一个数组:

    bool canConstruct(char * ransomNote, char * magazine){
        int ransomNotenum[26]={0};
        int i=0;
        while(magazine[i]!=''){
            ransomNotenum[magazine[i]-'a']++;
            i++;
        }
        
        i=0;
        while(ransomNote[i]!=''){
            ransomNotenum[ransomNote[i]-'a']--;
            i++;
        }
        for(i=0;i<26;i++){
            if(ransomNotenum[i]<0){
                return false;
            }
        }
        return true;
    }
    

      

    因为小写字母一共是26个,我们创建一个大小为26的数组,对每个字母出现的次数进行统计,赎金信里面再减去即可得到结果,查看数组中是否有为负数的值,就可知道返回值为truefalse,另外应该将数组初始化为0,这里忘记了,找了好久错。

    题目12 ID1137

     

     泰波那契序列 Tn 定义如下: 

    T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

    给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

    示例 1

    输入:n = 4

    输出:4

    解释:

    T_3 = 0 + 1 + 1 = 2

    T_4 = 1 + 1 + 2 = 4

    示例 2

    输入:n = 25

    输出:1389537

    提示:

    0 <= n <= 37

    答案保证是一个 32 位整数,即 answer <= 2^31 - 1

    我的解答:

    很容易会想到斐波那契数列一样的回溯解法:

    int tribonacci(int n){
        if(n==0){
            return 0;
        }else if(n==1||n==2){
            return 1;
        }else{
            return tribonacci(n-1)+tribonacci(n-2)+tribonacci(n-3);
        }
    }
    

      

    不出意料超时了,在评论区里面学习了一下。

    首先可以选择优化回溯算法,题目给出了T(n+3)=T(n)+T(n+1)+T(n+2),故T(n+4)=T(n+1)+T(n+2)+T(n+3),两者相减可得到T(n)=2T(n-1)-T(n+4)

    这样return 的时候就少一个回溯了,将代码修改为:

    int tribonacci(int n){
        switch(n){
            case 0:return 0;
            case 1:return 1;
            case 2:return 1;
            case 3:return 2;
            case 4:return 4;
            default:return 2*tribonacci(n-1)-tribonacci(n-4);
        }
    }
    

      

    修改了之后在最后一个输入当n=37的时候还是超出了范围:(

    从而想将代码修改为非回溯算法:

    int tribonacci(int n){
        long reason=0;
        long a1=1,a2=1,a3=2;
        int i;
        if(n==0){
            return 0;
        }else if(n==1||n==2){
            return 1;
        }else if(n==3){
            return 2;
        }else{
            for(i=4;i<=n;i++){
                reason=a1+a2+a3;
                a1=a2;
                a2=a3;
                a3=reason;
            }
            return reason;
        }
    }
    

      

    修改后通过。

  • 相关阅读:
    promise.all 发送多个请求
    解决分享里有中文,访问不了的问题
    elementui ztree 编辑的时候,回显半选状态的选项,用isLeaf判断
    倒叙数组查找数组对象中是否有该id值,将其去掉
    https强转
    Decorator学习(一)----- 基础知识
    MySQL的权限系统
    vue-cli 3.0.1 详细入门教程
    vue-cli4.5创建vue项目
    [记录] JavaScript 中的事件(Event对象、事件源对象、事件流、事件绑定)【转发】
  • 原文地址:https://www.cnblogs.com/Cl0ud/p/12404179.html
Copyright © 2011-2022 走看看