zoukankan      html  css  js  c++  java
  • 日常

    专题一:挑战字符串

    题目1:无重复字符的最长子串

    • 通过时间:40min
    • 解题思路:滑动窗口,依靠unordered_map实现

    题目描述

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

    示例 1:

    输入: "abcabcbb"
    输出: 3 
    解释: 因为无重复字符的最长子串是`"abc"`,所以其长度为`3`。
    

    示例 2:

    输入: "bbbbb"
    输出: 1
    解释: 因为无重复字符的最长子串是`"b"`,所以其长度为`1`。
    

    示例 3:

    输入: "pwwkew"
    输出: 3
    解释: 因为无重复字符的最长子串是`"wke"`,所以其长度为`3`。请注意,你的答案必须是`子串`的长度,"pwke" 是一个子序列,不是子串。
    

    算法

    1. brute force:通过两个循环,每次从位置i开始向后遍历,如果出现了一个字符s[j]已经出现过,那么计算j-i的长度是否比已经保存的长度更长,若更长,则替换;并且再次从位置i+1开始向后遍历。
    2. 滑动窗口:位置i遍历过后到j停下,则[i,j)皆是无重复的子串。故再次从i+1遍历时,[i+1,j)这一段无须重复遍历,从j开始即可。
    3. 优化滑动窗口:位置i遍历过后到j停下,说明[i,j)有出现和s[j]一样的字符。找到[i,j)中和s[j]一样字符的位置last_j,将i移到last_j+1,j接着从上次的位置往后一步开始

    代码

    1. brute force :略

    2. 滑动窗口: 略

    3. 优化滑动窗口

      class Solution {
      public:
          int lengthOfLongestSubstring(string s) {
              // parameters
              int len = s.length();
              int last_j = 0;
              int longestLen = 0, i = 0, j = 0;
      
              // 滑动窗口
              unordered_map<char, int> myMap;         // key - 字符,value - 在字符串s中的下标
              while (i < len && j < len)
              {
                  for (j = last_j; j < len; j++)
                  {
                      if (myMap.find(s[j]) == myMap.end())
                          myMap[s[j]] = j;
                      else
                      {
                          // 如果找到的字符是在位置i之前出现过的,只需更新位置,无需理会
                          int index_repeat = myMap[s[j]];
                          if (index_repeat < i)
                          {
                              myMap[s[j]] = j;
                              continue;
                          }
                          
                          // 是否需要更新最长子串的长度
                          int lenOfSub = j - i;
                          longestLen = lenOfSub > longestLen ? lenOfSub : longestLen;
      
                          // 更新i
                          i = index_repeat + 1;
      
                          // 更新map中的映射
                          myMap[s[j]] = j;
                          last_j = j + 1;
                          break;
                      }
                  }
                  if (j == len)
                  {
                      int lenOfSub = j - i;
                      longestLen = lenOfSub > longestLen ? lenOfSub : longestLen;
                  }
              }
              return longestLen;
          }
      };
      

    题目2:简化路径

    • 通过时间:20min
    • 解题思路:栈

    题目描述

    以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。

    在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点(..)表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径

    请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能以 / 结尾。此外,规范路径必须是表示绝对路径的最短字符串。

    示例 1:

    输入:"/home/"
    输出:"/home"
    解释:注意,最后一个目录名后面没有斜杠。
    

    示例 2:

    输入:"/../"
    输出:"/"
    解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。
    

    示例 3:

    输入:"/home//foo/"
    输出:"/home/foo"
    解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
    

    示例 4:

    输入:"/a/./b/../../c/"
    输出:"/c"
    

    示例 5:

    输入:"/a/../../b/../c//.//"
    输出:"/c"
    

    示例 6:

    输入:"/a//b////c/d//././/.."
    输出:"/a/b/c"
    

    算法

    粗看题目时并没有什么特别好的想法,看了几个样例后发现了一些端倪。核心思想:将两个斜杠之间出现的‘字符串’压栈但是获取的子字符串不一定是合法的目录名,需要对获得的子字符串判断:

    1. 若字符串为".."进行出栈操作
    2. 若字符串为"."跳过
    3. 若字符串为合法的目录名,则压栈
    4. 当然,过程中多余的/要去掉

    代码

    1. canonical path.h

      //
      // Created by shayue on 2019-04-11.
      // 利用栈
      
      #ifndef EXPLORE_BYTEDANCE_CANONICAL_PATH_H
      #define EXPLORE_BYTEDANCE_CANONICAL_PATH_H
      
      #include <string>
      #include <iostream>
      #include <vector>
      using namespace std;
      
      class Solution {
      public:
          string simplifyPath(string path) {
              vector<string> myStack;
              string ret;
      
              // 1. 规范化处理(这步可能有些多余,输入的path应该本身就是合法的,所以不会出现第一个字符不是'/'的情况)
              if (path[0] != '/') 
                  path = '/' + path;
      
              // 2. LOOP
              int len = path.size(), i = 0, j;
              while (i < len)
              {
                  if (path[i] == '/')
                  {
                      i++;
                      continue;
                  }
                  else
                  {
                      string sub;
                      // 获取两个斜杠之间的子字符串
                      for (j = i; j < len && path[j] != '/'; j++)
                          sub += path[j];
                      // 更新i
                      i = j;
      
                      // 判断子字符串的意义
                      if (sub == ".")
                          continue;
                      else if (sub == "..")
                      {
                          if (!myStack.empty())
                              myStack.pop_back();
                      }
                      else
                          myStack.push_back(sub);
                  }
              }
              // 3. return
              if (myStack.empty())
                  return "/";
              else
              {
                  for (i = 0; i < myStack.size(); i++)
                  {
                      string adds = "/" + myStack[i];
                      ret += adds;
                  }
                  return ret;
              }
          }
      };
      
      #endif //EXPLORE_BYTEDANCE_CANONICAL_PATH_H
      
      
    2. main.cpp

      #include <iostream>
      #include "Questions/canonical path.h"
      using namespace std;
      
      int main() {
          Solution s;
          string s1 = "/home/";
          string s2 = "/../";
          string s3 = "/home//foo/";
          string s4 = "/a/./b/../../c/";
          string s5 = "/a/../../b/../c//.//";
          string s6 = "/a//b////c/d//././/..";
      
          cout << s.simplifyPath(s1) << endl;
          cout << s.simplifyPath(s2) << endl;
          cout << s.simplifyPath(s3) << endl;
          cout << s.simplifyPath(s4) << endl;
          cout << s.simplifyPath(s5) << endl;
          cout << s.simplifyPath(s6) << endl;
          
          return 0;
      }
      
    3. 结果
      Snip20190411_11

    题目3:复原IP地址

    • 通过时间:25min
    • 解题思路:枚举所有可能

    题目描述

    给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

    示例:

    输入: "25525511135"
    输出: ["255.255.11.135", "255.255.111.35"]
    

    算法

    这题一开始我打算用深搜做,但是后来转念一想。取点的位置就那么几个,再怎么复杂都不会超时。下面给出合法IP地址的几个点:

    1. 总的字符串长度len属于[4,12]
    2. 在取3个点将字符串分成4部分,分别判断每部分是不是合法。

    那么怎么判断各个部分是不是合法的:

    1. 各个部分的长度小于等于3
    2. 各个部分转成10进制小于等于255
    3. 各个部分如果长度不为1,不能出现0在首位的情况。形如“00”,“010”就是不合法的。而“0”是合法的

    代码

    1. Longest Substring Without Repeating Characters.h

      //
      // Created by shayue on 2019-04-11.
      // 给定一串数字,找到所有可能的IP地址。
      // IP地址最多12个数字,分成4部分,且每部分最多为不大于255的数字
      
      #ifndef EXPLORE_BYTEDANCE_RESTORE_IP_ADDRESSES_H
      #define EXPLORE_BYTEDANCE_RESTORE_IP_ADDRESSES_H
      
      #include <string>
      #include <vector>
      using namespace std;
      
      class Solution {
      public:
          vector<string> restoreIpAddresses(string s) {
              vector<string> ret;
              int len = s.length();
      
              // 若传入的数字长度不符合,则不可能形成合法的IP地址
              if (len < 4 || len > 12)
                  return ret;
      
              // 随机对这些数字取3个点的位置并判断每部分是否合法,进而选择是否加入返回的序列中
      
              for (int i = 1; i < len && i <= 3; i++)
              {
                  // 255.255.11.135
                  string part1 = s.substr(0, i);
                  if (!isLegal(part1))
                      continue;
                  for (int j = 1; i+j < len && j <= 3; j++)
                  {
                      string part2 = s.substr(i, j);
                      if (!isLegal(part2))
                          continue;
                      for (int z = 1; i+j+z < len && z <= 3; z++)
                      {
                          string part3 = s.substr(i+j, z);
                          if (!isLegal(part3))
                              continue;
                          string part4 = s.substr(i+j+z);
                          if (!isLegal(part4))
                              continue;
                          else
                          {
                              string IP;
                              IP.append(part1);
                              IP.append(".");
                              IP.append(part2);
                              IP.append(".");
                              IP.append(part3);
                              IP.append(".");
                              IP.append(part4);
                              ret.push_back(IP);
                          }
                      }
                  }
              }
      
              return ret;
          }
      
          bool isLegal(string subs)
          {
              int len = subs.size();
              if (len > 3)
                  return false;
              if (len == 2 || len == 3)
              {
                  if (subs[0] == '0')
                      return false;
              }
              int inter = 0;
              for (int i = 0; i < len; i++)
                  inter = inter * 10 + (subs[i] -'0');
      
              return inter <= 255;
          }
      };
      
      #endif //EXPLORE_BYTEDANCE_RESTORE_IP_ADDRESSES_H
      
    2. main.cpp

      #include <iostream>
      #include "Questions/Restore IP Addresses.h"
      using namespace std;
      
      int main() {
          string str = "00000";
          Solution s;
          vector<string> rec = s.restoreIpAddresses(str);
          for (int i = 0; i < rec.size(); i++)
              cout << rec[i] << endl;
          return 0;
      }
      

    专题二:数组与排序

    题目1:三数之和

    • 通过时间:40min
    • 解题思路:a+b=-c,双指针

    题目描述

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

    注意:答案中不可以包含重复的三元组。

    例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
    
    满足要求的三元组集合为:
    [
      [-1, 0, 1],
      [-1, -1, 2]
    ]
    

    算法

    代码

    //
    // Created by shayue on 2019-04-11.
    //
    
    #ifndef EXPLORE_BYTEDANCE_3SUM_H
    #define EXPLORE_BYTEDANCE_3SUM_H
    
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    #define MIN_INF 0x80000000
    class Solution {
    public:
        vector<vector<int>> threeSum(vector<int>& nums) {
            sort(nums.begin(), nums.end());
    
            int len = int(nums.size()), last_num_i = MIN_INF;
            int i, j, k;
            vector<vector<int>> ret;
    
            for (i = 0; i < len - 2; i++)
            {
                if (nums[i] == last_num_i)
                    continue;
                else
                    last_num_i = nums[i];
    
                /* 在i之后的位置中寻找nums[j]+nums[k]=-nums[i] */
                k = len - 1;
                j = i + 1;
                while (j < k)
                {
                    if (nums[j] + nums[k] == -nums[i])
                    {
                        ret.push_back(vector<int>{nums[i], nums[j], nums[k]});
                        int mid = nums[j];
                        last_num_i = nums[k];
                        while (j < k && nums[j] == mid)
                            j++;
                        while (j < k && nums[k] == last_num_i)
                            k--;
    
                    }
                    else if (nums[j] + nums[k] < -nums[i])
                        j++;
                    else
                        k--;
                }
                while (i+1 < len && nums[i] == nums[i+1])
                    i++;
            }
            return ret;
        }
    };
    
    
    #endif //EXPLORE_BYTEDANCE_3SUM_H
    
    /*
     * TEST
     * Solution s;
        vector<int> vec = {-1, 0, 1, 2, -1, -4};
        vector<vector<int> > rec = s.threeSum(vec);
        for (int i = 0; i < rec.size(); i++)
        {
            for (int j = 0; j < rec[i].size(); j++)
                cout << rec[i][j] << ' ';
            cout << endl;
        }
        return 0;
     */
    
    

    题目2:岛屿的最大面积

    • 通过时间:跪了
    • 解题思路:DFS

    题目描述

    给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

    找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

    示例 1:

    [[0,0,1,0,0,0,0,1,0,0,0,0,0],
     [0,0,0,0,0,0,0,1,1,1,0,0,0],
     [0,1,1,0,1,0,0,0,0,0,0,0,0],
     [0,1,0,0,1,1,0,0,1,0,1,0,0],
     [0,1,0,0,1,1,0,0,1,1,1,0,0],
     [0,0,0,0,0,0,0,0,0,0,1,0,0],
     [0,0,0,0,0,0,0,1,1,1,0,0,0],
     [0,0,0,0,0,0,0,1,1,0,0,0,0]]
    对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。
    

    示例 2:

    [[0,0,0,0,0,0,0,0]]
    对于上面这个给定的矩阵, 返回 0。
    

    注意: 给定的矩阵grid 的长度和宽度都不超过 50。

    算法

    这题做了我2个小时。一开始傻傻地以为两次dp应该没啥问题,调bug调到后来都怀疑人生了。如果用dp做,应该要4次,从左上到右下,从右下到左上,从右上到左下,从左下到右上。最终某个较大值应该在“岛屿”的“中心”获得。我考虑2两个小时只考虑到前两种,真的是给跪了。考虑到dp还有状态转移方程的判断,这样的话还不如老实用DFS做。事实证明这题用DFS做居然:
    屏幕快照 2019-04-12 00.23.54-w521
    用DFS做应该要设置一个访问标记数组,因为连在一起的岛屿铁定一次都访问掉了。假设没有进行标记,然后从之前深搜到过位置再进行DFS,肯定走之前相同的路。设置访问标记数组能大大降低时间复杂度。
    这题让我反思了很多,做算法题和数学题一样,一定要找准第一步。想到用什么方法,然后去验证可行性,一定能做到事半功倍。千万不能死扣在一种思维上。我的2个小时。。。

    代码

    //
    // Created by shayue on 2019-04-11.
    // DFS
    
    #ifndef EXPLORE_BYTEDANCE_MAX_AREA_OF_ISLAND_H
    #define EXPLORE_BYTEDANCE_MAX_AREA_OF_ISLAND_H
    
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    class Solution {
    public:
        int visited[60][60];      // 0 - 为访问,1 - 访问
        int maxAreaOfIsland(vector<vector<int>>& grid) {
            int maxArea = 0;
            int row = grid.size();
            int col = grid[0].size();
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < col; j++)
                {
                    if (grid[i][j] == 1 && visited[i][j] == 0)
                    {
                        visited[i][j] = 1;
                        int area = 1;
                        DFS(i, j, row, col, area, grid);
                        if (area > maxArea)
                            maxArea = area;
                    }
                }
            }
            return maxArea;
        }
    
        void DFS(int x, int y, int row, int col, int &area, vector<vector<int>>& grid)
        {
            if (x+1 < row && grid[x+1][y] == 1 && visited[x+1][y] == 0)
            {
                visited[x+1][y] = 1;
                area += 1;
                DFS(x+1, y, row, col, area, grid);
            }
    
            if (y+1 < col && grid[x][y+1] == 1 && visited[x][y+1] == 0)
            {
                visited[x][y+1] = 1;
                area += 1;
                DFS(x, y+1, row, col, area, grid);
            }
    
            if (x-1 >= 0 && grid[x-1][y] == 1 && visited[x-1][y] == 0)
            {
                visited[x-1][y] = 1;
                area += 1;
                DFS(x-1, y, row, col, area, grid);
            }
    
            if (y-1 >= 0 && grid[x][y-1] == 1 && visited[x][y-1] == 0)
            {
                visited[x][y-1] = 1;
                area += 1;
                DFS(x, y-1, row, col, area, grid);
            }
        }
    };
    #endif //EXPLORE_BYTEDANCE_MAX_AREA_OF_ISLAND_H
    
    

    题目3:搜索旋转排序数组

    • 通过时间:30min
    • 二分查找

    题目描述:

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

    你可以假设数组中不存在重复的元素。

    你的算法时间复杂度必须是 O(log n) 级别。

    示例 1:

    输入: nums = [4,5,6,7,0,1,2], target = 0
    输出: 4
    

    示例 2:

    输入: nums = [4,5,6,7,0,1,2], target = 3
    输出: -1
    

    算法

    虽然数组从某个点被打乱,但是还是局部有序的,这就为使用二分查找提供了前提。还是和一般的二分查找相似,先判断中点和target的关系,然后判断target和两个端点的关系。

    代码

    //
    // Created by shayue on 2019-04-12.
    // 二分搜索
    
    #ifndef EXPLORE_BYTEDANCE_SEARCH_IN_ROTATED_SORTED_ARRAY_H
    #define EXPLORE_BYTEDANCE_SEARCH_IN_ROTATED_SORTED_ARRAY_H
    
    #include <vector>
    #include <iostream>
    using  namespace std;
    
    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int len = nums.size();
            int left = 0, right = len - 1;
            while (left <= right)
            {
                int mid = (left + right) / 2;
                if (nums[mid] == target)
                    return mid;
                if (nums[left] <= nums[mid] && nums[mid] <= nums[right])
                {
                    if (target > nums[mid])
                        left = mid + 1;
                    else
                        right = mid - 1;
                }
                else if (nums[left] <= nums[mid] && nums[mid] >= nums[right])
                {
                    // 左边是有序的,如target大于nums[mid]只有可能在右边
                    if (target > nums[mid])
                        left = mid + 1;
                    else
                    {
                        if (target >= nums[left])
                            right = mid - 1;
                        else
                            left = mid + 1;
                    }
                }
                else if (nums[left] >= nums[mid] && nums[right] >= nums[mid])
                {
                    // 右边是有序的,如target小于nums[mid]只有可能在左边
                    if (target < nums[mid])
                        right = mid - 1;
                    else
                    {
                        if (target <= nums[right])
                            left = mid + 1;
                        else
                            right = mid - 1;
                    }
                }
            }
    
            return -1;
        }
    };
    #endif //EXPLORE_BYTEDANCE_SEARCH_IN_ROTATED_SORTED_ARRAY_H
    
    

    题目4:朋友圈

    • 通过时间:25min
    • 解题思路:并查集

    题目描述

    班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

    给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

    示例 1:

    输入: 
    [[1,1,0],
     [1,1,0],
     [0,0,1]]
    输出: 2 
    说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
    第2个学生自己在一个朋友圈。所以返回2。
    

    示例 2:

    输入: 
    [[1,1,0],
     [1,1,1],
     [0,1,1]]
    输出: 1
    说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
    

    注意:

    N 在[1,200]的范围内。
    对于所有学生,有M[i][i] = 1。
    如果有M[i][j] = 1,则有M[j][i] = 1。
    

    算法

    使用并查集为每位学生找到一个圈子,这个圈子由一个学生编号确定。比如学生1,2,3属于同一个圈子,那么这个圈子有个带头人-编号1,则对编号1,2,3寻找带头人,最终都能指向编号1.这样就形成了一个圈子。

    由于输入的矩阵是对称矩阵,遍历的时候只需要遍历对角线上半部分即可。

    代码

    //
    // Created by shayue on 2019-04-12.
    //
    
    #ifndef EXPLORE_BYTEDANCE_FRIEND_CIRCLES_H
    #define EXPLORE_BYTEDANCE_FRIEND_CIRCLES_H
    
    #include <vector>
    #include <iostream>
    using namespace std;
    
    class Solution {
    public:
        int findCircleNum(vector<vector<int>>& M) {
            // parameters
            int numOfStu = M.size();        // 学生的编号为[0, numOfStu-1]
            int Set[numOfStu];
    
            // 1. 将并查集初始化,即每个学生的带头人都是其本身
            for (int i = 0; i < numOfStu; i++)
                Set[i] = i;
    
            // 2. 遍历矩阵M,只需遍历对角线上方即可。当M[i][j]==1时,视情况而定处理编号i和编号j所处的圈子
            for (int i = 0; i < numOfStu; i++)
            {
                for (int j = i+1; j < numOfStu; j++)
                {
                    if (M[i][j] == 1)
                    {
                        int headi = findHead(i, Set);
                        int headj = findHead(j, Set);
                        if (headi != headj)
                            Set[headj] = headi;         // 当学生i和学生j的圈子未相连时,将他们连在一起
                    }
                }
            }
            
            // 确定有几个带头人
            int visited[300] = {0};
            int ret = 0;
            for (int i = 0; i < numOfStu; i++)
            {
                int head = findHead(i, Set);
                if (visited[head] == 0)
                {
                    visited[head]++;
                    ret++;
                }
            }
            return ret;
        }
    
        /* 这个函数用来寻找每个学生对应的带头人 */
        int findHead(int pos, const int Set[])
        {
            // 如果这个条件不满足,说明当前编号pos并不是最终的带头人
            while (Set[pos] != pos)
                pos = Set[pos];
    
            return pos;
        }
    };
    
    #endif //EXPLORE_BYTEDANCE_FRIEND_CIRCLES_H
    
    
  • 相关阅读:
    c++(递归和堆栈)
    Halcon算子翻译——dev_set_line_width
    Halcon算子翻译——dev_set_draw
    Halcon算子翻译——dev_set_colored
    Halcon算子翻译——dev_set_color
    Halcon算子翻译——dev_open_window
    Halcon算子翻译——dev_open_tool
    Halcon算子翻译——dev_open_file_dialog
    Halcon算子翻译——dev_open_dialog
    Halcon算子翻译——dev_map_var
  • 原文地址:https://www.cnblogs.com/shayue/p/zi-jie-tiao-dong-ji-shi-zhun-bei.html
Copyright © 2011-2022 走看看