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]

我的解答:

  1. /**
  2.  
  3. * Note: The returned array must be malloced, assume caller calls free().
  4.  
  5. */
  6.  
  7. int* twoSum(int* nums, int numsSize, int target, int* returnSize){
  8.  
  9. int i,j;
  10.  
  11. int* num;
  12.  
  13. for(i=0;i<numsSize;i++){
  14.  
  15. for(j=i+1;j<numsSize;j++){
  16.  
  17. if(nums[i]+nums[j]==target){
  18.  
  19. *returnSize=2;
  20.  
  21. num=(int*)malloc(sizeof(int)*2);
  22.  
  23. num[0]=i;
  24.  
  25. num[1]=j;
  26.  
  27. return num;
  28.  
  29. }
  30.  
  31. }
  32.  
  33. }
  34.  
  35. return 0;
  36.  
  37. }

  

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

题目2 ID7

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

示例 1:

输入: 123

输出: 321

示例 2:

输入: -123

输出: -321

示例 3:

输入: 120

输出: 21

注意:

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

我的解答:

  1. #define MAX 2147483647
  2. #define MIN -2147483648
  3. int reverse(int x){
  4.     long num=0;
  5.     while(x!=0){
  6.         num=num*10+x%10;
  7.         x=x/10;
  8.     }
  9.     return (num>MAX||num<MIN)?0:num;
  10. }

  

用时0ms,内存消耗6.9MB

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

题目3 ID9

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

示例 1:

输入: 121

输出: true

示例 2:

输入: -121

输出: false

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

示例 3:

输入: 10

输出: false

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

进阶:

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

我的解答:

  1. bool isPalindrome(int x){
  2.     if(x>=0&&x<=9){
  3.         return true;
  4.     }else if(x<0){
  5.         return false;
  6.     }else{
  7.         long temp,num=0;
  8.         temp=x;
  9.         while(temp!=0){
  10.             num=num*10+temp%10;
  11.             temp=temp/10;
  12.         }
  13.         if(x==num){
  14.             return true;
  15.         }else{
  16.             return false;
  17.         }
  18.     }
  19. }

  

用时12ms,内存7.1MB

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

题目4 ID13

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值

I             1

V             5

X             10

L             50

C             100

D             500

M             1000

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

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

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

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

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

给定一个罗马数字,将其转换成整数。输入确保在 1 到 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 懂得这个可以写出更好的代码

  1. int romanToInt(char * s){
  2.     int i=0;
  3.     int sum=0;
  4.     for(i=0;s[i];i++){
  5.         switch(s[i]){
  6.             case 'M':sum+=1000;break;
  7.             case 'D':sum+=500;break;
  8.             case 'L':sum+=50;break;
  9.             case 'V':sum+=5;break;
  10.             case 'C':
  11.                 if(s[i+1]=='D'){
  12.                     sum+=400;
  13.                     i++;
  14.                 }else if(s[i+1]=='M'){
  15.                     sum+=900;
  16.                     i++;
  17.                 }else{
  18.                     sum+=100;
  19.                 }
  20.                 break;
  21.             case 'X':
  22.                 if(s[i+1]=='L'){
  23.                     sum+=40;
  24.                     i++;
  25.                 }else if(s[i+1]=='C'){
  26.                     sum+=90;
  27.                     i++;
  28.                 }else{
  29.                     sum+=10;
  30.                 }
  31.                 break;
  32.             case 'I':
  33.                 if(s[i+1]=='V'){
  34.                     sum+=4;
  35.                     i++;
  36.                 }else if(s[i+1]=='X'){
  37.                     sum+=9;
  38.                     i++;
  39.                 }else{
  40.                     sum+=1;
  41.                 }
  42.                 break;
  43.         }
  44.     }
  45.     return sum;
  46. }

  

题目5 ID面试题64

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

示例 1:

输入: n = 3

输出: 6

示例 2:

输入: n = 9

输出: 45

限制:

1 <= n <= 10000

我的解答:

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

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

  1. int sumNums(int n){
  2.     int sum=n;
  3.     n&&(sum+=sumNums(n-1));
  4.     return sum;
  5. }

  

题目6 ID面试题14

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

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

示例 1:

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

输出: "fl"

示例 2:

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

输出: ""

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

说明:

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

我的解答:

  1. char * longestCommonPrefix(char ** strs, int strsSize){
  2.     int i,j;
  3.     char* ans=strs[0];
  4.     if(strsSize==0){
  5.         return "";
  6.     }
  7.     for(i=1;i<strsSize;i++){
  8.         for(j=0;ans[j]!='\0'&&strs[i][j]!='\0';j++){
  9.             if(ans[j]!=strs[i][j]){
  10.                 break;
  11.             }
  12.         }
  13.         ans[j]='\0';
  14.         if(ans==NULL){
  15.             return "";
  16.         }
  17.     }
  18.     return ans;
  19. }

  

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

题目7 ID20

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

有效字符串需满足:

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

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

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

示例 1:

输入: "()"

输出: true

示例 2:

输入: "()[]{}"

输出: true

示例 3:

输入: "(]"

输出: false

示例 4:

输入: "([)]"

输出: false

示例 5:

输入: "{[]}"

输出: true

我的解答:

  1. bool isValid(char * s){
  2.     int i,top=-1;
  3.     int length=strlen(s);
  4.     char* cha=(char*)malloc(length);
  5.     if(s==NULL){
  6.         return true;
  7.     }
  8.     if(length%2!=0){
  9.         return false;
  10.     }
  11.     for(i=0;i<length;i++){
  12.         if(s[i]=='('||s[i]=='{'||s[i]=='['){
  13.             cha[++top]=s[i];
  14.         }else if(top==-1){
  15.             return false;
  16.         }else if(cha[top]+1==s[i]||cha[top]+2==s[i]){
  17.             top--;
  18.         }else{
  19.             return false;
  20.         }
  21.     }
  22.     return top==-1;
  23. }

  

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

题目8 ID21

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

示例:

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

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

我的解答:

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

  1. /**
  2.  * Definition for singly-linked list.
  3.  * struct ListNode {
  4.  *     int val;
  5.  *     struct ListNode *next;
  6.  * };
  7.  */
  8.  
  9. struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
  10.     if(l1==NULL){
  11.         return l2;
  12.     }
  13.     if(l2==NULL){
  14.         return l1;
  15.     }
  16.     if(l1->val<l2->val){
  17.         l1->next=mergeTwoLists(l1->next,l2);
  18.         return l1;
  19.     }else{
  20.         l2->next=mergeTwoLists(l1,l2->next);
  21.         return l2;
  22.     }
  23. }

  

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

题目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]);

}

我的解答:

  1. int removeDuplicates(int* nums, int numsSize){
  2.     int i,j;
  3.     if(nums==NULL||numsSize==0){
  4.         return 0;
  5.     }
  6.     for(i=1,j=1;i<numsSize;i++){
  7.         if(nums[i]!=nums[i-1]){
  8.             nums[j]=nums[i];
  9.             j++;
  10.         }
  11.     }
  12.     return j;
  13. }

  

双指针法

题目10 ID53

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

示例:

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

输出: 6

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

进阶:

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

我的解答:

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

  1. int maxSubArray(int* nums, int numsSize){
  2.     int i,thissum=0,maxsum=-2147483648;
  3.     for(i=0;i<numsSize;i++){
  4.         thissum+=nums[i];
  5.         if(thissum>maxsum){
  6.             maxsum=thissum;
  7.         }
  8.         if(thissum<0){
  9.             thissum=0;
  10.         }
  11.     }
  12.     return maxsum;
  13. }

  

题目11 ID383

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

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

注意:

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

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

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

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

我的解答:

最开始的答案是:

  1. bool canConstruct(char * ransomNote, char * magazine){
  2.     int ransomNotenum[26],magazinenum[26];
  3.     int i=0;
  4.     while(ransomNote[i]){
  5.         ransomNotenum[ransomNote[i]-'a']++;
  6.         i++;
  7.     }
  8.     i=0;
  9.     while(magazine[i]){
  10.         magazinenum[magazine[i]-'a']++;
  11.         i++;
  12.     }
  13.     for(i=0;i<26;i++){
  14.         while(ransomNotenum[i]){
  15.             ransomNotenum[i]--;
  16.             magazinenum[i]--;
  17.         }
  18.     }
  19.     for(i=0;i<26;i++){
  20.         if(magazinenum[i]<0){
  21.             return false;
  22.         }
  23.     }
  24.     return true;
  25. }

  

时间超时了,进一步修改

合并为一个数组:

  1. bool canConstruct(char * ransomNote, char * magazine){
  2.     int ransomNotenum[26]={0};
  3.     int i=0;
  4.     while(magazine[i]!='\0'){
  5.         ransomNotenum[magazine[i]-'a']++;
  6.         i++;
  7.     }
  8.     
  9.     i=0;
  10.     while(ransomNote[i]!='\0'){
  11.         ransomNotenum[ransomNote[i]-'a']--;
  12.         i++;
  13.     }
  14.     for(i=0;i<26;i++){
  15.         if(ransomNotenum[i]<0){
  16.             return false;
  17.         }
  18.     }
  19.     return true;
  20. }

  

因为小写字母一共是26个,我们创建一个大小为26的数组,对每个字母出现的次数进行统计,赎金信里面再减去即可得到结果,查看数组中是否有为负数的值,就可知道返回值为true或false,另外应该将数组初始化为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。

我的解答:

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

  1. int tribonacci(int n){
  2.     if(n==0){
  3.         return 0;
  4.     }else if(n==1||n==2){
  5.         return 1;
  6.     }else{
  7.         return tribonacci(n-1)+tribonacci(n-2)+tribonacci(n-3);
  8.     }
  9. }

  

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

首先可以选择优化回溯算法,题目给出了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 的时候就少一个回溯了,将代码修改为:

  1. int tribonacci(int n){
  2.     switch(n){
  3.         case 0:return 0;
  4.         case 1:return 1;
  5.         case 2:return 1;
  6.         case 3:return 2;
  7.         case 4:return 4;
  8.         default:return 2*tribonacci(n-1)-tribonacci(n-4);
  9.     }
  10. }

  

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

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

  1. int tribonacci(int n){
  2.     long reason=0;
  3.     long a1=1,a2=1,a3=2;
  4.     int i;
  5.     if(n==0){
  6.         return 0;
  7.     }else if(n==1||n==2){
  8.         return 1;
  9.     }else if(n==3){
  10.         return 2;
  11.     }else{
  12.         for(i=4;i<=n;i++){
  13.             reason=a1+a2+a3;
  14.             a1=a2;
  15.             a2=a3;
  16.             a3=reason;
  17.         }
  18.         return reason;
  19.     }
  20. }

  

修改后通过。

Leetcode学习笔记(1)的更多相关文章

  1. Leetcode学习笔记(4)

    题目1 ID121 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润. 注意你不能在买入股 ...

  2. Leetcode学习笔记(2)

    题目1 ID面试题 01.04 给定一个字符串,编写一个函数判定其是否为某个回文串的排列之一. 回文串是指正反两个方向都一样的单词或短语.排列是指字母的重新排列. 回文串不一定是字典当中的单词. 示例 ...

  3. leetcode学习笔记--开篇

    1 LeetCode是什么? LeetCode是一个在线的编程测试平台,国内也有类似的Online Judge平台.程序开发人员可以通过在线刷题,提高对于算法和数据结构的理解能力,夯实自己的编程基础. ...

  4. Leetcode学习笔记(5)

    之前断了一段时间没做Leetcode,深感愧疚,重新续上 题目1 ID104 给定一个二叉树,找出其最大深度. 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数. 说明: 叶子节点是指没有子节点 ...

  5. Leetcode学习笔记(3)

    题目1 ID88 给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 num1 成为一个有序数组. 说明: 初始化 nums1 和 nums2 的元素数量 ...

  6. Leetcode学习笔记(6)

    题目1 ID112 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和. 说明: 叶子节点是指没有子节点的节点. 示例: 给定如下二叉树,以及目标 ...

  7. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  8. [Java] LinkedList / Queue - 源代码学习笔记

    简单地画了下 LinkedList 的继承关系,如下图.只是画了关注的部分,并不是完整的关系图.本博文涉及的是 Queue, Deque, LinkedList 的源代码阅读笔记.关于 List 接口 ...

  9. 学习笔记之机器学习(Machine Learning)

    机器学习 - 维基百科,自由的百科全书 https://zh.wikipedia.org/wiki/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0 机器学习是人工智能的一个分 ...

随机推荐

  1. php 上传音频文件并获取时长

    <input type="file" name="audio" id="voice_file" style="display ...

  2. 使用GitHub API上传文件及GitHub做图床

    本文介绍GitHub API基础及上传文件到仓库API,并应用API将GitHub作为图床 GitHub API官方页面 GitHub API版本 当前版本为v3,官方推荐在请求头中显示添加版本标识. ...

  3. deepin 20安装后系统没有声音解决方案(亲测有效)

    打开终端: sudo vi /etc/default/grub GRUB_CMDLINE_LINUX_DEFAULT原有配置后面添加 snd_hda_intel.dmic_detect=0 即GRUB ...

  4. 打乱Map key - value的对应顺序

    应用场景:对于考试试卷选择题选项的乱序对应问题,防止考生作弊,每个人的题目都是不一样的选项顺序. package com.muyuan.platform.elearning.util; import ...

  5. 免费|申请谷歌云服务器|msf安装

    apt-get install -y wget 参考链接 知乎-免费|申请谷歌云服务器 知乎-免费|申请谷歌云服务器 cnblogs-debian.ubuntu安装metasploit通用方法 谷歌云 ...

  6. 使用python统计《三国演义》小说里人物出现次数前十名,并实现可视化。

    一.安装所需要的第三方库 jieba (jieba是优秀的中文分词第三分库) pyecharts (一个优秀的数据可视化库) <三国演义>.txt下载地址(提取码:kist ) 使用pyc ...

  7. 巧妙使用MindManager图像功能,能够让你的思维导图更精彩

    MindManager是一款多功能思维导图工具软件,有其他软件无法媲美的项目管理和商业规划高级功能.用户们制作思维导图时一定要注意图文并茂,单纯的文字会过于单调.所以接下来,小编就为大家详细介绍Min ...

  8. Earmaster——音乐爱好者必备软件

    有很多喜爱音乐但是却由于一些"不可抗力"而没能学习到音乐基础的小伙伴,相信你们在自学乐器或是声乐的时候总会因为基础不扎实而看不懂一些复杂的乐谱,换别的曲子练习之后发现依旧看不懂,由 ...

  9. ABBYY FineReader 15 查看和编辑PDF

    使用ABBYY FineReader 15(Windows系统)OCR文字识别软件,用户可轻松查看和编辑各种类型的PDF数字文档,并可在文档中添加注释.添加与删除文字.格式化文字.搜索内容.保护PDF ...

  10. guitar pro系列教程(十二):如何设置Guitar Pro的不完全小节

    当我们新建一个GTP谱的时候,我们肯定是要用到节拍,是的,一个乐谱节拍设置的好不好,将直接影响你的乐谱效果好不好,设置节拍的步骤我们之前也有讨论过,今天主要跟大家讲的便是不完全小节. 不完全小节顾名思 ...