LeetCode 热题 HOT 100 打卡计划 | 第一天 | 每日进步一点点

130 阅读2分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

1. 两数之和

思路

(暴力枚举) O(n^2)

两重循环枚举下标i,j,然后判断 nums[i]+nums[j] 是否等于 target

(哈希表) O(n)

使用C++中的哈希表unordered_map<int, int> hash

  • 用哈希表存储前面遍历过的数,当枚举到当前数时,若哈希表中存在target - nums[i]的元素,则表示已经找到符合条件的两个数。
  • 若不存在target - nums[i]的元素则枚举完当前数再把当前数放进哈希表中

时间复杂度: 由于只扫描一遍,且哈希表的插入和查询操作的复杂度是 O(1),所以总时间复杂度是 O(n).

c++代码

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

2. 两数相加

思路 O(n)。

(模拟)

这是道模拟题,模拟我们小时候列竖式做加法的过程:

  1. 从最低位至最高位,逐位相加,如果和大于等于10,则保留个位数字,同时向前一位进1
  2. 如果最高位有进位,则需在最前面补1

具体实现

  1. 同时从头开始枚举两个链表,将l1l2指针指向的元素相加存到t中,再将t % 10的元素存到dummy链表中,再t / 10去掉存进去的元素,l1l2同时往后移动一格。
  2. 当遍历完所有元素时,如果t != 0,再把t存入到dummy链表中。

图片.png 做有关链表的题目, 有个常用技巧:添加一个虚拟头结点:ListNode *head = new ListNode(-1);,可以简化边界情况的判断。

时间复杂度: 由于总共扫描一遍,所以时间复杂度是 O(n)。

c++代码

 /**
  * Definition for singly-linked list.
  * 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) {}
  * };
  */
 class Solution {
 public:
     ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
         ListNode* dummy = new ListNode(-1);
         ListNode* cur = dummy;
         int t = 0;
         while(l1 || l2){
             if(l1) t += l1->val, l1 = l1->next;
             if(l2) t += l2->val, l2 = l2->next;
             cur = cur->next = new ListNode(t % 10); 
             t /= 10;
         }
         if(t) cur->next = new ListNode(t);
         return dummy->next;
     }
 };

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

思路

(双指针扫描) O(n)

定义两个指针 i,j(i<=j),表示当前扫描到的子串是 [i,j] (闭区间)。扫描过程中维护一个哈希表unordered_map <chat,int>hash,表示 [i,j]中每个字符出现的次数。

线性扫描时,每次循环的流程如下:

  • 1.指针j 向后移一位, 同时将哈希表中 s[j] 的计数加一,即hash[s[j]]++;
  • 2.假设 j移动前的区间 [i,j]​中没有重复字符,则 j 移动后,只有 s[j]可能出现2次。因此我们不断向后移动 i,直至区间 [i,j]s[j] 的个数等于1为止;
  • 3.当确保[i, j]中不存在重复元素时,更新res

时间复杂度分析: 由于 ij 均最多增加n次,且哈希表的插入和更新操作的复杂度都是 O(1),因此,总时间复杂度 O(n)。

c++代码

 class Solution {
 public:
     int lengthOfLongestSubstring(string s) {
         unordered_map<char, int> hash;
         int res = 0;
         for(int j = 0, i = 0; j < s.size(); j++){
             hash[s[j]]++;
             while(hash[s[j]]> 1){
                 hash[s[i]]--;
                 i++;
             }
             res = max(res, j - i + 1);
         }
         return res;
     }
 };