Leetcode刷题日记:1-5题篇

Leetcode刷题日记:1-5题篇

  • 简介
  • 题目:
    • 一.两数之和
    • 二、两数相加
    • 三、无重复字符的最长子串
    • 四、寻找两个正序数组的中位数
    • 五、最长回文子串

简介

这个系列将是Leetcode的刷题记录贴,按照题库顺序,每五题为一贴,主要目的做自己的记录、总结之用,但若有同学或同好看到此贴,并有所帮助,也算额外之喜,共勉!

题目:

一.两数之和

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

示例:

输入:nums = [3,2,4], target = 6
输出:[1,2]

题解:

class Solution {
		public:
		    vector<int> twoSum(vector<int>& nums, int target) {
		        for(int i=0;i<nums.size();i++)
		            for(int j=i+1;j<nums.size();j++)
		                if(nums[i]+nums[j]==target) return {i,j};
		        return {};
		    }

思路:暴力做法,遍历两遍数组,时间复杂度O(n^2),空间复杂度O(1)

总结:
1.注意 vector 类型返回值的函数,若要返回空值写 return 语句时如下:
return {}

二、两数相加

给定两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。请你将两个数相加,并以相同形式返回一个表示和的链表。这两个数都不会以 0 开头。

示例:

输入:L1 = [9,9,9,9,9,9,9], L2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

题解:

class Solution {
		public:
		    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
		        int carry=0,digit=0;
		        ListNode *head=nullptr,*tail=nullptr;
		        while(l1||l2){
		            int m=(l1)?l1->val:0;
		            int n=(l2)?l2->val:0;
		            digit=(m+n+carry)%10;
		            carry=(m+n+carry)/10;
		            if(!head) head=tail=new ListNode(digit);
		            else{
		                tail->next=new ListNode(digit);
		                tail=tail->next;
		            }
		            if(l1) l1=l1->next;
		            if(l2) l2=l2->next;
		        }
		        if(carry!=0){
		            tail->next=new ListNode(carry);
		            tail=tail->next;
		        }
		        return head;
		    }
		};

思路:新建两个指针,同时遍历两个链表并加和计算,头指针指向第一位,尾指针随位数向后移动,每一位相加后再与上一位的进位相加,最后判断是否还有进位,有的话再附加一个节点,输出。时间复杂度为O(m+n),空间复杂度为O(1)

总结:
1.ListNode 的操作,ListNode 的定义如下所示:

		    struct ListNode {
		    int val;
		    ListNode *next;
		    ListNode() : val(0), next(nullptr) {}
		    ListNode(int x) : val(x), next(nullptr) {}
		    ListNode(int x, ListNode *next) : val(x), next(next) {}
		};

2.可以使用 *ptr=new ListNode(int) 语句来新建一个链表节点,其值为 int,指针为 *ptr
3.声明一个指针后立刻进行初始化,*ptr=nullptr
4. A ? B : C 是一个条件运算符表达式,其值取决于 A 的计算结果。如果 A 为真,则 B 为整个表达式的值。如果 A 为假,则 C 为表达式的值
5.本题解的空间复杂度为 O(1) ,而非 O(m+n) ,考虑算法的空间复杂度时不计入返回值

三、无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例:

输入: s = “abcabcbb”
输出: 3

题解:

class Solution {
		public:
		    int lengthOfLongestSubstring(string s) {
		        unordered_map<char,int> findstr;
		        int count=0,res=0,i=0,start=0;
		        for(int i=0;i<s.size();i++){
		        	if(s.size()==0) break;
		            if(findstr.find(s[i])==findstr.end()){
		                findstr[s[i]]=i;
		                count++;
		                res=max(res,count); 
		            }
		            else{
		                if(findstr[s[i]]>start-1){
		                    start=findstr[s[i]];
		                    count=i-start;
		                }
		                else{
		                    count++;
		                    res=max(res,count); 
		                }
		                findstr[s[i]]=i;
		            }
		        }
		    return res;
		    }
		};

思路:使用hash_map存储字符串中每一个字符及其位置,遍历字符串,若是新的字符,将其存入hash_map, 对count加1,否则判断是否需要更新字串的起点,例如对于字符串’abba’,由于b第二次出现时,子串的起点为0,我们将字串的起点更新为第二个b的位置,将count的值更新为第二个b和第一个b之间的字符个数,紧接着a第二次出现,这时a并不会与第一个a重复,不需要再次更新起点,而是对count进行+1操作。

总结:
1.hash_map的定义与基本的函数操作
2.对map的某个key进行注意赋值时使用的是 [] 而非 ()

四、寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
示例:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000

题解:

class Solution {
		public:
		    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
		        int m=nums1.size(),n=nums2.size();
		        for(int i=0;i<n;i++){
		            nums1.push_back(nums2[i]);
		        }
		        sort(nums1.begin(),nums1.end());
		        if((m+n)%2==0) return ((nums1[(m+n)/2]+nums1[(m+n)/2-1])/2.0);
		        else return nums1[(m+n-1)/2];
		    }
		};

思路:先将两个数组合并,使用sort进行排序,再根据合并后的数组的奇偶性输出中间数字。

总结:
1.当数组为偶数时,要对两个数字求和再相除,最终得到一个包含小数的数字,这时使用 / 符号时,除数应写为2.0而不是2

五、最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。(回文子串,正反顺序读取相同的字符串)
示例:

输入:s = “babad”
输出:“bab”

题解:

class Solution {
		public:
		    string longestPalindrome(string s) {
		        int slen=s.size();
		        int subbegin=0,maxsublen=1;
		        if(slen<2) return s;
		        //记录是否是回文子串的数组
		        bool ifPalindrome[slen][slen];
		        for(int i=0;i<slen;i++)
		            ifPalindrome[i][i]=true;
		        //从长度为2的字串开始枚举    
		        for(int sublen=2;sublen<=slen;sublen++){
		            for(int l=0;l<slen;l++){
		                int r=l+sublen-1;
		                //判断右边界越界
		                if(r>=slen) break;
		                if(s[l]!=s[r]) ifPalindrome[l][r]=false;
		                //小于3长度的子串边界相同即为回文子串
		                else if(r-l<3) ifPalindrome[l][r]=true;
		                //大于3则取决于边界内的子串
		                else ifPalindrome[l][r]=ifPalindrome[l+1][r-1];
		                //判断是否需要更新最长子串
		                if(ifPalindrome[l][r]==true&&sublen>maxsublen){
		                    maxsublen=sublen;
		                    subbegin=l;
		                }
		            }
		        }
		    return s.substr(subbegin,maxsublen);
		    }
		};

思路:动态规划思想,对于每一个长度为一的子串其一定是回文子串,对一个长度为 n 的子串 s[i]~s[i+n-1],首先判断其边界是否相等,如果相等那么其回文性取决于子串 s[i+1]~s[i+n-2],以此类推。在这个过程中,每得到一个回文子串,都与当前记录的最长子串长度 maxsublen 比较,并记录此子串的起始位置 subbegin。

总结:
1.动态规划的思想,笔者目前的理解更像是一种“递归”,将大问题化解为一个个小问题,找到其中的基本元素。笔者在这里第一次接触到动态规划算法,书本上讲到动态规划动辄便是状态转移方程之类,看起来非常高大上,实则真正用起来关键是要想明白如何从要解决的问题经过同一个“递归”的过程变成小的基本问题。在此基础上,要时刻注意边界条件,例如本题中首先想到的便是,使用左边界和长度相加得到的右边界不能超过原串的长度
2.string 的函数 string.substr(x,y),其中 x 标识子串的起始位置, y 表示子串的长度

题目来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

博客著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(力扣刷题记录,leetcode,算法,数据结构,c++)