LeetCode00001-00019

2021/08/02

    1. Add Two Numbers

在这里插入图片描述

  • 解析: 非常简单的列表相加问题,一开始弄错为反向列表相加了,绕了点路子
  • key points:
    • 每次相加之后要把进位保留下来
    • 考虑corner case,即最后一个进位为0时需要删除最后一个节点,不为零时需要保留最后一个节点
    • 活用3元表达式缩减代码
  • my answer
class Solution {
  public:
      ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
          ListNode* l1_r =l1, * l2_r =l2 ;
          ListNode* cur_l1_r=l1_r, * cur_l2_r=l2_r;
          int carry_bit=0;
          ListNode* l_sum = new ListNode(carry_bit);
          ListNode* cur_l_sum = l_sum, * pre_l_sum = NULL;
          while(cur_l1_r!=NULL || cur_l2_r !=NULL){
              int d1=0,d2=0;
              if(cur_l1_r!=NULL){
                  d1 = cur_l1_r->val;
                  cur_l1_r = cur_l1_r->next;
              }
              if(cur_l2_r!=NULL){
                  d2 = cur_l2_r->val;
                  cur_l2_r = cur_l2_r->next;
              }
              int digit_sum = d1+d2+cur_l_sum->val;
              carry_bit = digit_sum/10;
              cur_l_sum->val = digit_sum%10;
              cur_l_sum->next = new ListNode(carry_bit);
              pre_l_sum = cur_l_sum;
              cur_l_sum = cur_l_sum->next;
          }
          if(cur_l_sum->val == 0){
              pre_l_sum->next =NULL;
              delete cur_l_sum;
          }
          return l_sum;
      }
  };
  • official answer:
class Solution {
  public:
      ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
          ListNode *head = nullptr, *tail = nullptr;
          int carry = 0;
          while (l1 || l2) {
              int n1 = l1 ? l1->val: 0;
              int n2 = l2 ? l2->val: 0;
              int sum = n1 + n2 + carry;
              if (!head) {
                  head = tail = new ListNode(sum % 10);
              } else {
                  tail->next = new ListNode(sum % 10);
                  tail = tail->next;
              }
              carry = sum / 10;
              if (l1) {
                  l1 = l1->next;
              }
              if (l2) {
                  l2 = l2->next;
              }
          }
          if (carry > 0) {
              tail->next = new ListNode(carry);
          }
          return head;
      }
  };

在这里插入图片描述

  • 解析: 遍历所有substring,判断是否重复,利用hashtable 加速查询

  • key points:

    • 如何遍历所有substring
    • 使用hashtable加速算法
    • 利用滑动窗思想(下一次不重复的查找起点可以跨越之前已经检查过的位置,类似KMP算法)[x]
  • my answer (slow):

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int max_len = 0;
            int s_len = s.size();
            if(s_len == 0) return 0;
            max_len = 1;
            std::unordered_set<char> c;
            for(int i=0;i<s_len;i++){
                for(int j=i;j>=0;j--){
                    if(c.find(s[j])==c.end())
                        c.insert(s[j]);
                    else
                        break;
                }
                if(c.size()>max_len)
                    max_len = c.size();
                c.clear();
            }
            return max_len;
        }
    };
    
  • official anwser:

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            // 哈希集合,记录每个字符是否出现过
            unordered_set<char> occ;
            int n = s.size();
            // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
            int rk = -1, ans = 0;
            // 枚举左指针的位置,初始值隐性地表示为 -1
            for (int i = 0; i < n; ++i) {
                if (i != 0) {
                    // 左指针向右移动一格,移除一个字符
                    occ.erase(s[i - 1]);
                }
                while (rk + 1 < n && !occ.count(s[rk + 1])) {
                    // 不断地移动右指针
                    occ.insert(s[rk + 1]);
                    ++rk;
                }
                // 第 i 到 rk 个字符是一个极长的无重复字符子串
                ans = max(ans, rk - i + 1);
            }
            return ans;
        }
    };
    

2021/08/03

  • Median of Two Sorted Arrays[X]

    在这里插入图片描述

    • 解析:需要满足对数时间复杂度,问题在于如何实现双有序队列的二分查找,这一思想可以扩展到多有序队列

    • keypoints:

      • 查找中位数的问题建模,如何每次去掉一半的数
      • 编程技巧 [need re-make]
    • my answer : None

    • official answer:

      class Solution {
      public:
          int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
              /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
               * 这里的 "/" 表示整除
               * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
               * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
               * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
               * 这样 pivot 本身最大也只能是第 k-1 小的元素
               * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
               * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
               * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
               */
      
              int m = nums1.size();
              int n = nums2.size();
              int index1 = 0, index2 = 0;
      
              while (true) {
                  // 边界情况
                  if (index1 == m) {
                      return nums2[index2 + k - 1];
                  }
                  if (index2 == n) {
                      return nums1[index1 + k - 1];
                  }
                  if (k == 1) {
                      return min(nums1[index1], nums2[index2]);
                  }
      
                  // 正常情况
                  int newIndex1 = min(index1 + k / 2 - 1, m - 1);
                  int newIndex2 = min(index2 + k / 2 - 1, n - 1);
                  int pivot1 = nums1[newIndex1];
                  int pivot2 = nums2[newIndex2];
                  if (pivot1 <= pivot2) {
                      k -= newIndex1 - index1 + 1;
                      index1 = newIndex1 + 1;
                  }
                  else {
                      k -= newIndex2 - index2 + 1;
                      index2 = newIndex2 + 1;
                  }
              }
          }
      
          double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
              int totalLength = nums1.size() + nums2.size();
              if (totalLength % 2 == 1) {
                  return getKthElement(nums1, nums2, (totalLength + 1) / 2);
              }
              else {
                  return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
              }
          }
      };
      

2021/08/06

在这里插入图片描述

  • 解析:动态规划,核心是建立状态与找到状态转移方程
  • keypoints:
    • 状态转移方程的分析与建立
  • my code:None
  • official code: None

2021/08/07

    1. Container With Most Water

      在这里插入图片描述

    • 解析:贪心算法,利用双指针求解

    • keypoints:

      • 分析贪心合理性,每次尝试改善容器最弱边界,证明该步骤合理即可
      • 使用双指针实现代码
    • my code:

      class Solution {
      public:
          int maxArea(vector<int>& height) {
              int left_ptr= 0, right_ptr = height.size()-1;
              int cur_left = height[left_ptr], cur_right = height[right_ptr];
              int most_water = (right_ptr-left_ptr)*min(cur_left,cur_right);
              for(;left_ptr<right_ptr;){
                  if(height[left_ptr]< height[right_ptr]){
                      while(left_ptr<=right_ptr && height[++left_ptr]<cur_left){}
                      cur_left=height[left_ptr];
                  }else{
                      while(left_ptr<=right_ptr && height[--right_ptr]<cur_right){}
                      cur_right = height[right_ptr];
                  }
                  int cur_water = (right_ptr-left_ptr)*min(cur_right,cur_left);
                  if(cur_water>most_water) most_water=cur_water;
              }
              return most_water;
          }
      };
      
  • 12. Integer to Roman

    在这里插入图片描述

    • 解析:储存好digits number即可,解析完特殊情况后合成字符串。

    • my code:

      class Solution {
      public:
          string intToRoman(int num) {
              int base[7]={1,5,10,50,100,500,1000};
              int digits[7];
              int subtraction[7]={0};
              char roman_base[7]={'I','V','X','L','C','D','M'};
              string result="";
              for(int i=6;i>=0;i--){
                  digits[i] = num/base[i];
                  num = num%base[i];
                  if(i<6 && i%2==0){
                      if(digits[i]==4){
                          if(digits[i+1]>=1){
                              digits[i+1]--;
                              subtraction[i+1]=1;
                          }else{
                              subtraction[i]=1;
                          }
                      }
                  }
              }
              for(int i=6;i>=0;i--){
                  if(subtraction[i]>0){
                      result.append(1,roman_base[(i>>1)<<1]);
                      result.append(1,roman_base[i+1]);
                      i=((i>>1)<<1);
                  }else{
                      if(digits[i]>0)
                          result.append(digits[i],roman_base[i]);
                  }
              }
              return result;
          }
      };
      

2021/08/08

    1. 3Sum[x]

      在这里插入图片描述

    • 解析:三数之和,关键在于发现排序后循环的意义
    • keypoints:
      • 排序,发现了排序的第一重作用(加快搜寻),没有发现排序的第二重作用(使得检出的三元组天然有序)
      • 排序后多重循环时,后两重循环实际上可以变为一重,因为是有序的

2021/08/09

    1. 3Sum Closest

      在这里插入图片描述

    • 解析:排序+双指针,和3SUM一样

    • keypoints:

      • 一些小的优化技巧,target==0 时可以直接返回,枚举a,b,c时,对于重复元素可以直接跳过枚举过程。
    • my code:

      class Solution {
      public:
          int threeSumClosest(vector<int>& nums, int target) {
              sort(nums.begin(),nums.end());
              int sum = nums[0]+nums[1]+nums[2]-target;
              int a,b,c,tmp_sum;
              for(int i=0;i<nums.size();i++){
                  a = nums[i];
                  int left=i+1, right = nums.size()-1;
                  while(left<right){
                      b = nums[left]; c = nums[right];
                      tmp_sum = a+b+c-target;
                      if(abs(tmp_sum)<abs(sum))
                          sum = tmp_sum;
                      if(tmp_sum<0)
                          left++;
                      else
                          right--;
                  }
              }
              return sum+target;
          }
      };
      
    1. Letter Combinations of a Phone Number[x]

      在这里插入图片描述

    • 解析:就是树的深度优先搜索,基础不牢固

    • keypoints:

      • 递归,也可以使用非递归解法,但是要自己定义stack来代替递归栈

2021/08/10

    1. 4Sum

      在这里插入图片描述

    • 解析:与3Sum 类似,解法一样,但是需要一些trick来加速操作

    • keypoints:

      • 需要剪枝操作来加快速度,例如排序后,若前四个数和大于target则可直接返回[x]

2021/08/11

    1. Remove Nth Node From End of List

      在这里插入图片描述

    • 解析:链表基础操作,
    • keypoints:
      • 处理链表时一定要考虑头尾,格外注意每一步程序的意思;
      • 使用双指针可以避免计算列表长度,即为此right-left=n,直到right==NULL,此时left为对应的删除节点[x]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值