• leetcode刷题笔录2


    6.字梯游戏

    • 给定两个单词start和end,以及一本字典,找到由start到end的最短变换路径,每一次变换只允许改变一个字母,且变换后的单词必须出现在字典中。比如给出start为"hit"而end为"cog",字典为["hot","dot","dog","lot","log"],那么应当返回5,因为最短路径是:"hit" -> "hot" -> "dot" -> "dog" -> "cog"
    • 思路:函数参数中unordered_set类型参数说明字典是用散列表维护的。题目就是图论中最短路径算法的简单应用,同样使用散列表为词典中的每一个单词维护一个域m_ladderLens,记录与start顶点的距离(即变换的次数):具体的,对某个顶点,遍历所有存在于字典中的变换(邻接顶点),挑选出m_ladderLens值小于顶点自己的m_ladderLens值+1的,对这些顶点重复该过程。
    • 实现:
      class Solution {
      public:
          int ladderLength(string start, string end, unordered_set<string> &dict) {
              m_wordLen = start.length();
              for (unordered_set<string>::iterator i=dict.begin(); i!=dict.end(); i++){
                  m_ladderLens[*i]=INT_MAX;
              }
              m_ladderLens[start]=1;        
      
              _ladderLength(start, end, dict);
      
              if (m_ladderLens[end] == INT_MAX){
                  m_ladderLens[end] = 0;
              }
              return m_ladderLens[end];
          }
      private:
          void _ladderLength(string start, string end, unordered_set<string> &dict){
              for (int i=0; i<=m_wordLen-1; i++){
                  for (int j=0; j<=26-1; j++){
                      string s = start;
                      s[i] = 'a'+j;
                      if (s!=start && 
                          dict.find(s)!=dict.end() && 
                          m_ladderLens[s]>m_ladderLens[start]+1)
                      {
                          m_ladderLens[s] = m_ladderLens[start]+1;
                          if (s != end){
                              _ladderLength(s, end, dict);
                          }
                      }
                  }
              }
          }
          int m_wordLen;
          unordered_map<string, int> m_ladderLens;
      }; 

    7.字梯游戏2

    • 给定两个单词start和end,以及一本字典,找到由start到end的最短变换路径,每一次变换只允许改变一个字母,且变换后的单词必须出现在字典中。但是需要输出所有最短的路径(即使有多条,也要一起输出)。比如给出start为"hit"而end为"cog",字典为["hot","dot","dog","lot","log"],那么需要输出:
      [
          ["hit","hot","dot","dog","cog"],
          ["hit","hot","lot","log","cog"]
      ]
    • 思路:与6几乎一致,但是输出不一样,需要在迭代过程中添加一个量currentLadders,对某个顶点,记录从start点到该点的最短路径。(如果有多条最短的路径,此时currentLadders中只有一条,它表示迭代到该函数的过程中产生的那一条。)
    • 实现:
      class Solution {
      public:
          vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
              m_ladders = vector<vector<string>>();
              m_wordLen = start.size();
              for (unordered_set<string>::iterator i=dict.begin(); i!=dict.end();i++){
                  m_ladderLens[*i]=INT_MAX;
              }
              m_ladderLens[start] = 1;
      
              _ladderLength(start, end, dict);
              shortestLen = m_ladderLens[end]==INT_MAX ? 0 : m_ladderLens[end];
              _findLadders(start, end, vector<string>(), dict);
      
              return m_ladders;
          }
      
      private:
          void _findLadders(const string& start, const string& end, vector<string> currentLadders, unordered_set<string>& dict){
              currentLadders.push_back(start);
              if (start==end && currentLadders.size()==shortestLen){
                  m_ladders.push_back(currentLadders);
                  return;
              }
              for (int i=0; i<=m_wordLen-1; i++){
                  for (int j=0; j<=26-1; j++){
                      string s = start;
                      s[i] = 'a'+j;
      
                      if (s!=start && dict.find(s)!=dict.end() && m_ladderLens[s]>=m_ladderLens[start]+1){
                          m_ladderLens[s]=m_ladderLens[start]+1;
                          _findLadders(s, end, currentLadders, dict);
                      }
                  }
              }
          }
      
          void _ladderLength(string start, string end, unordered_set<string> &dict){
              for (int i=0; i<=m_wordLen-1; i++){
                  for (int j=0; j<=26-1; j++){
                      string s = start;
                      s[i] = 'a'+j;
                      if (s!=start && 
                          dict.find(s)!=dict.end() && 
                          m_ladderLens[s]>m_ladderLens[start]+1){
                          m_ladderLens[s] = m_ladderLens[start]+1;
                          if (s != end){
                              _ladderLength(s, end, dict);
                          }
                      }
                  }
              }
          }    
          int m_wordLen;
          unordered_map<string, int> m_ladderLens;
          int shortestLen;
          vector<vector<string>> m_ladders;    
      }; 

    8.回文验证

    • 给定一个字符串,判断其是否是回文,只考虑数字和字母部分,标点符号略去。同样,大小写一致的字母也认为是一样的。比如字符串"A man, a plan, a canal: Panama"就是回文,而
      "race a car"则不是回文。
    • 思路:很简单。
    • 实现:
      class Solution {
      public:
          bool isPalindrome(string s){
              for (int i=0, j=s.size()-1; i<=j; ){
                  if (!isAlphanumeric(s[i])){
                      i++;
                      continue;
                  }
                  if (!isAlphanumeric(s[j])){
                      j--;
                      continue;
                  }
                  if (tolower(s[i])==tolower(s[j])){
                      i++;
                      j--;
                      continue;
                  }
                  return false;
              }
              return true;
          }
      private:
          bool isAlphanumeric(char c){
              if (c>='a' && c<='z'){return true;}
              if (c>='A' && c<='Z'){return true;}
              if (c>='1' && c<='9'){return true;}
              if (c=='0'){return true;}
              return false;
          }
      }; 

     9.二叉树最大路径和

    • 给出一棵最大二叉树,找到其“最大路径和”。一条路径,可以从二叉树的任何一个节点开始,并到任何一个节点结束。路径和就是这一条路径上所有节点的值的和。需要考虑负数节点。比如,给出这样一棵二叉树:
             1
            / \
           2   3

       需要返回6,因为路径2-3-1的路径和为6(2+3+1)。

    • 思路:这个题的陷阱是,可能出现负数值的节点,我已开始就漏考虑了,而认为最大路径至少有一个端点是叶子节点,另一个端点是根节点或叶子节点。可能有负数值的节点的情况下,这样考虑:
      • 为每个节点考虑两个属性,最大路径和(即要求的,记为S)和有一个端点为根节点的最大路径和(记为P)。
      • 某个节点的S是以下这几个值当中最大的:
        • 左节点的S(如果左节点存在的话)
        • 右节点的S(如果右节点存在的话)
        • 左节点的P(如果左节点存在且P>0,否则算0,就当没取它,以根节点为一个端点)+右节点的P(同左节点,若两者都不去,就是根节点一个端点组成的路径)+节点自身的值
      • 某个节点的P是以下这几个值中最大的:
        • 左节点的P(如果左节点存在且P>0,否则算0,就当没取它,仅有根节点一个节点组成的路径)+节点自身的值
        • 右节点的P(同上)+节点自身值
    • 实现:
      /**
       * Definition for binary tree
       * struct TreeNode {
       *     int val;
       *     TreeNode *left;
       *     TreeNode *right;
       *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
       * };
       */
      class Solution {
      public:
          int maxPathSum(TreeNode *root) {
              int sum = INT_MIN;
              if (root->left != NULL){
                  int sum_left = maxPathSum(root->left);
                  sum = max(sum, sum_left);
              }
              if (root->right != NULL){
                  int sum_right = maxPathSum(root->right);
                  sum = max(sum, sum_right);
              }
              int sum_root = (root->left==NULL?0:max(0,maxPathSumToRoot(root->left))) + 
                              root->val + 
                             (root->right==NULL?0:max(0,maxPathSumToRoot(root->right)));
              sum = max(sum, sum_root);
              return sum;        
          }
      private:
          int maxPathSumToRoot(TreeNode *root){        
              int sum = INT_MIN;
              if (root->left != NULL){
                  int sum_left = root->val + max(0, maxPathSumToRoot(root->left));
                  sum = max(sum, sum_left);
              }
              if (root->right != NULL){
                  int sum_right = root->val + max(0, maxPathSumToRoot(root->right));
                  sum = max(sum, sum_right);
              }
              sum = max(sum, root->val);
              return sum;
          }
      };

    10.股价

    • 给定一个数组prices,表示第 i 天的股价,你总共只能买入并卖出1股,请确定买入和卖出的时机,使收益最大。
    • 思路:简单,从前向后遍历数组,维护一个域记录已遍历的天数中股价最低的那一天,然后用那一天的股价减去股价最低的那天的股价,获得一个收益。取最大的收益。
    • 实现:
      class Solution {
      public:
          int maxProfit(vector<int> &prices) {
              int minIndex = 0;
              int maxProfitValue = 0;
              for (int i=0; i<=int(prices.size())-1; i++)
              {
                  if (prices[i]<prices[minIndex])
                  {
                      minIndex = i;
                  }
                  int profit = prices[i]-prices[minIndex];
                  maxProfitValue = maxProfitValue<profit ? profit : maxProfitValue;
              }
              return maxProfitValue;
          }
      }; 

    11.股价2

    • 给定一个数组prices,表示第 i 天的股价,你每天都可以进行一次交易(买入或卖出一股),请确定买入和卖出的时机,使收益最大。
    • 思路:简单,直观地看,如果第 i 天的股价低于第 i+1 天,那么就在第 i 天买进,第 i+1天卖出。
    • 实现:
      class Solution {
      public:
          int maxProfit(vector<int> &prices) {
              int maxProfitValue = 0;
              for (int i=0; i<=int(prices.size())-2; i++){
                  if (prices[i]<prices[i+1]){
                      maxProfitValue+=prices[i+1]-prices[i];
                  }
              }
              return maxProfitValue;
          }
      }; 

    12.股价3

    • 给定一个数组prices,表示第 i 天的股价,你只能依次进行两次交易(买入-卖出-买入-卖出),请确定买入和卖出的时机,使收益最大。
    • 思路:一开始的想法是,把10中的代码拿过来稍作修改,然后将数组分割成[0...i]和[i...n-1]两个部分,最大收益就是两个部分最大收益之和(每个部分进行一次交易)。遍历 i 找出最大的。但是这样太耗时间了,后来看到uniEagle提供的思路,才恍然大悟。其实10中找出一段时间[i...j]内最大收益的过程就已经解决了[i...k]的子问题,只要维护两个一维数组,一个记录[0...i]的最大收益,一个记录[i...n-1]的最大收益即可。这两个数组都是可以在一次遍历中得出的。然后再考虑两个部分收益之和。
    • 实现:
      class Solution {
      public:
          int maxProfit(vector<int> &prices) {
              vector<int> profitToEnd(prices.size(), 0);
              {
                  int minIndex = 0;
                  int maxPft = 0;
                  for (int i=0; i<prices.size(); i++){
                      int tmpPft = prices[i]-prices[minIndex];
                      if (tmpPft > maxPft){
                          maxPft = tmpPft;
                      }
                      if (prices[i]<prices[minIndex]){
                          minIndex = i;
                      }
                      profitToEnd[i]=maxPft;
                  }
              }
              vector<int> profitFromStart(prices.size(), 0);
              {
                  int maxIndex = prices.size()-1;
                  int maxPft = 0;
                  for(int i=prices.size()-1; i>=0; i--){
                      int tmpPft = prices[maxIndex]-prices[i];
                      if (tmpPft > maxPft){
                          maxPft = tmpPft;
                      }
                      if (prices[maxIndex] < prices[i]){
                          maxIndex = i;
                      }
                      profitFromStart[i]=maxPft;
                  }
              }
              int maxPft = 0;
              for (int i=0; i<prices.size(); i++)
              {
                  int tmpPft = profitFromStart[i]+profitToEnd[i];
                  if (maxPft<tmpPft){
                      maxPft = tmpPft;
                  }
              }
              return maxPft;
          }
      };
    作者:一叶斋主人
    出处:www.cnblogs.com/yiyezhai
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Java中字符串的学习(四)基本数据类型包装类的学习与使用
    Java中字符串的学习(三)StringBuffer与StringBuilder
    Java中字符串的学习(二)String类的小练习
    Java中字符串的学习(一)String类的概述及常见方法使用
    ios开发之解决重用TableViewCell导致的界面错乱的问题
    SpringBoot+Redis形成数据 缓存
    SpringBoot logback slf4j 的理解和使用
    Sring IOC的用途和理解
    SpringBoot AOP 理解和用途
    centos 安装遇到的问题
  • 原文地址:https://www.cnblogs.com/yiyezhai/p/2969211.html
Copyright © 2020-2023  润新知