LeetCode 34, 202, 46

07-10 1301阅读

目录

  • 34. 在排序数组中查找元素的第一个和最后一个位置
    • 题目链接
    • 标签
    • 思路
    • 代码
    • 202. 快乐数
      • 题目链接
      • 标签
      • Set
        • 思路
        • 代码
        • 判圈
          • 思路
          • 代码
          • 46. 全排列
            • 题目链接
            • 标签
            • 思路
            • 代码

              34. 在排序数组中查找元素的第一个和最后一个位置

              题目链接

              34. 在排序数组中查找元素的第一个和最后一个位置

              LeetCode 34, 202, 46
              (图片来源网络,侵删)

              标签

              数组 二分查找

              思路

              本题需要查找目标区间(值)的左端点和右端点,使用 二分查找 即可,不过不能在 target == nums[mid] 时直接返回 mid,而应该记录这个 mid 作为候选的端点值,继续查找是否有更优的端点值。对于查找 左端点,相等时去 左子区间 查找;对于查找 右端点,相等时去 右子区间 查找。

              代码

              class Solution {
                  public int[] searchRange(int[] nums, int target) {
                      int left = leftBinarySearch(nums, target);
                      int right = rightBinarySearch(nums, target);
                      return new int[]{left, right};
                  }
                  // 查询最左边的目标值的索引
                  private int leftBinarySearch(int[] nums, int target) {
                      int left = 0, right = nums.length - 1;
                      int candidate = -1; // 候选索引
                      while (left 
                          int mid = left + (right - left > 1);
                          if (target  nums[mid]) { // 如果 target > nums[mid]
                              left = mid + 1; // 则去右子区间查找
                          } else { // 如果 target == nums[mid]
                              candidate = mid; // 更新候选索引的值
                              right = mid - 1; // 去左子区间查找
                          }
                      }
                      return candidate; // 返回候选索引,如果能找到目标值,则返回最左边的目标值索引;否则返回-1
                  }
                  // 查询最右边的目标值的索引
                  private int rightBinarySearch(int[] nums, int target) {
                      int left = 0, right = nums.length - 1;
                      int candidate = -1; // 候选索引
                      while (left 
                          int mid = left + (right - left > 1);
                          if (target  nums[mid]) { // 如果 target > nums[mid]
                              left = mid + 1; // 则去右子区间查找
                          } else { // 如果 target == nums[mid]
                              candidate = mid; // 更新候选索引的值
                              left = mid + 1; // 去右子区间查找
                          }
                      }
                      return candidate; // 返回候选索引,如果能找到目标值,则返回最右边的目标值索引;否则返回-1
                  }
              }
              

              202. 快乐数

              题目链接

              202. 快乐数

              标签

              哈希表 数学 双指针

              Set

              思路

              在求n的每一位的平方之和时,如果发现这个平方和 curr 与之前某个平方和 past 相等,则n就不是快乐数,因为再对 curr 求 有限次 平方和,就会得到 past,而无法得到 1,即它不是快乐数。

              可以使用一个集合存储所有的平方和,每次先求平方和,然后判断在集合中是否存在这个平方和,如果存在,就说明 n 不是快乐数,返回 false。 由于不仅要保存 n ,还要保存所有的平方和,所以可以把保存的操作放在求平方和之前。

              代码

              class Solution {
                  public boolean isHappy(int n) {
                      Set set = new HashSet(); // 存储平方和的集合
                      while (n != 1) { // 直到 n为1(是快乐数) 才退出循环
                          set.add(n); // 将当前数字存入集合
                          n = getNext(n); // 获取平方和
                          if (set.contains(n)) { // 集合中包含n(不是快乐数)
                              return false; // 返回false
                          }
                      }
                      return true;
                  }
                  // 获取n的每位的平方之和
                  private int getNext(int n) {
                      int next = 0; // 保存n的每位的平方之和
                      while (n > 0) { // 直到n为0才退出循环
                          int d = n % 10; // 获取n的最后一位
                          n /= 10; // 将n向后移一位
                          next += d * d; // 对平方求和
                      }
                      return next;
                  }
              }
              

              判圈

              思路

              本题解的 getNext() 的方法名是有一定意义的:在链表中经常使用 next 作为下一个节点的指针,本题中 根据数字获取其平方和作为下一个数字 的操作很像链表的 next 指针,所以本题引申一下就是 判断链表是否存在环,这里使用的是 Floyd判圈算法 ,不懂的可以去看。

              具体操作就是:使用快慢指针,快指针每次走两步,慢指针每次走一步,如果快指针能遍历到链表尾部(对应到本题就是快指针的值为1),那就说明n是快乐数,返回true;如果不能遍历到链表尾部,则快慢指针一定会相遇,说明n不是快乐数,返回false。

              代码

              class Solution {
                  public boolean isHappy(int slow) { // slow 为慢指针
                      int fast = getNext(slow); // 快指针
                      while (fast != 1) { // 直到 平方和为1(是快乐数) 才退出循环,相当于到链表尾部
                          slow = getNext(slow); // 更新慢指针
                          fast = getNext(getNext(fast)); // 更新快指针
                          if (slow == fast) { // 如果快慢指针相遇,则说明有环,不是快乐数
                              return false; // 返回false
                          }
                      }
                      return true;
                  }
                  // 获取n的每位的平方之和
                  private int getNext(int n) {
                      int next = 0; // 保存n的每位的平方之和
                      while (n > 0) { // 直到n为0才退出循环
                          int d = n % 10; // 获取n的最后一位
                          n /= 10; // 将n向后移一位
                          next += d * d; // 对平方求和
                      }
                      return next;
                  }
              }
              

              46. 全排列

              题目链接

              46. 全排列

              标签

              数组 回溯

              思路

              本题是一个 针对数组的 记忆的 深度优先搜索,在搜索时,通常有五步操作:

              1. 判断是否已经完成搜索,如果完成搜索,则直接返回;否则针对数组中的全部元素进行下列操作。
              2. 判断是否遍历过当前元素,如果遍历过,则跳过它,遍历数组中的下一个元素;否则进行下列操作。
              3. 标记当前元素已被遍历,对当前元素进行要求的操作。
              4. 进行更深一层的递归。
              5. 取消当前元素被遍历的标记,消除对当前元素操作而产生的影响。

              第五步 是很关键的,如果没有第五步,则本次的遍历会影响下一次的遍历。

              对应到具体题目,使用 boolean[] vis 记录每个元素是否遍历过;在搜索时传入一个栈,这个栈保存了本次搜索遍历过的元素,在 栈的元素数 等于 数组的元素数 时,完成本次的排列,具体的五步操作如下:

              1. 判断 栈的元素数 是否等于 数组的元素数,如果等于,则将栈中的所有元素作为链表加入到结果中,并返回;否则对数组中的全部元素进行下列操作。
              2. 判断是否遍历过这个元素,如果遍历过,则跳过它,遍历数组中的下一个元素;否则进行下列操作。
              3. 标记当前元素已被遍历,将当前元素放到栈中。
              4. 进行更深一层的递归。
              5. 取消当前元素被遍历的标记,将当前元素从栈中弹出。

              代码

              class Solution {
                  public List permute(int[] nums) {
                      vis = new boolean[nums.length];
                      dfs(nums, new LinkedList());
                      return res;
                  }
                  private boolean[] vis; // 用于判断本次搜索是否遍历过nums中的值
                  private final List res = new ArrayList(); // 保存结果的链表
                  /**
                   * 对nums进行深度优先搜索
                   * @param nums 待搜索数组
                   * @param stack 已搜索的元素组成的栈
                   */
                  private void dfs(int[] nums, LinkedList stack) {
                      if (stack.size() == nums.length) { // 如果 栈的元素个数 与 nums的元素个数 相等,即搜索完整个nums
                          res.add(new ArrayList(stack)); // 将栈中的元素作为链表存入结果链表
                          return; // 返回
                      }
                      for (int i = 0; i 
                              
                              
                              
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]