LeetCode 79单词搜索&80删除排序数组中的重复项Ⅱ&81.搜索旋转排序数组Ⅱ

简介: 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

LeetCode79.单词搜索



给定一个二维网格和一个单词,找出该单词是否存在于网格中。


单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。


示例:


board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]
给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false


提示:


board 和 word 中只包含大写和小写英文字母。
1 <= board.length <= 200
1 <= board[i].length <= 200
1 <= word.length <= 10^3


分析


经典迷宫搜索题,这里我用dfs实现,对于迷宫搜索题需要注意以下几点:


  • 定义四个方向数组,用来坐标模拟行走
  • 走过的坐标需要临时标记(回溯),回来的时候在清除标记
  • 注意起始和结尾情况,尽量避免使用String频繁创建新的对象


而本题在搜索途中判断当前字符没有被使用且和目标位置字符相等即可模拟遍历。有一个注意的是一旦找到答案则不需要再进行搜索(需要停止)。


具体代码为:


int x[]= {-1,0,1,0};
int y[]= {0,-1,0,1};
boolean isvalue=false;
boolean jud[][];
public boolean exist(char[][] board, String word) {
  jud=new boolean[board.length][board[0].length];
  if(word.length()>board.length*board[0].length)return false;
  char words[]=word.toCharArray();
  for(int i=0;i<board.length;i++)
  {
    for(int j=0;j<board[i].length;j++)
    {
      jud[i][j]=true;
      if(words[0]==board[i][j])
        dfs(board, 1, words, i, j);
      jud[i][j]=false;
      if(isvalue)return true;
    }
  } 
  return false;
   }
private void dfs(char[][] board,int index, char words[],int x1,int y1) {
  // TODO Auto-generated method stub
  if(isvalue)return;
  if(index==words.length)
  {
    isvalue=true;
  }
  else 
  {
    for(int i=0;i<4;i++)
    {
      int xnew=x1+x[i],ynew=y1+y[i];
      if(xnew>=0&&xnew<board.length&&ynew>=0&&ynew<board[0].length)
      {
        if(!jud[xnew][ynew]&&words[index]==board[xnew][ynew])
        {
          jud[xnew][ynew]=true;
          dfs(board, index+1, words, xnew, ynew);
          jud[xnew][ynew]=false;
        }
      }
    }
  }
}


效率有待优化。


LeetCode80. 删除排序数组中的重复项 II



给定一个增序排列数组 nums ,你需要在 原地 删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。


不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。


说明:


为什么返回数值是整数,但输出的答案是数组呢?


请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。


你可以想象内部操作如下:


// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}


示例 1:


输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。


示例 2:


输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。


提示:


0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums 按递增顺序排列


分析:


刷这题前回顾26题 删除排序数组中的重复项

而这一题同样也是要用双指针原地修改数组。但是每个元素最多出现两次。对于两次我们应该如何考虑呢?


枚举每个数的个数?然后大于二只使用两个?其实更好的方式是这个数和它前面第二个数进行比较,如果相等那么可以使用,如果不相等那么无法使用。如下图的比较有效数字就可以看出:


20201206180346599.png


但是我们在原地如何操作这个数组呢?- 双指针

可以用一个fast用来遍历,用slow来表示存储真实有效的区域。


  • 在具体的比较上,fast只需要和slow前面第二个元素比较即可(因为slow是真实储存的结果标记),如果相等,那么直接fast继续,如果不等,那么将数据复制到slow且slow右移一位。
  • slow 初始为2,fast初始为2(前面两个即使相同也会放到结果中)


具体看一次模拟过程:


20201206180427872.png20201206180513544.png


实现代码:


public int removeDuplicates(int[] nums) {
  if(nums.length<2)
    return nums.length;
  int slow=2;
  for(int fast=2;fast<nums.length;fast++)
  {
    if(nums[fast]==nums[slow-2])
    {
      continue;
    }
    else {
      nums[slow++]=nums[fast];
    }
  }
  return slow;
  }


LeetCode81.搜索旋转排序数组 II



题目描述


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

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

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。


示例 1:


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


示例 2:


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


进阶:

这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。

这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?


分析


在做之前还是要 看下这道题:搜索旋转排序数组 。这题和那题的区别就是需要考虑重复的情况

而这题最好的方法当然也是二分,但是二分的过程还是需要分类考虑一些情况的。


20201206180301505.png


这题我的方法和其他人的题解不太一样吧,大部分人是按照几种情况进行分类。但是核心也都很相似,要指导当前的mid在那个区间里面(每个区间可能出现的结果)。进行分类讨论情况。


个人ac的代码为:


public static boolean search(int[] nums, int target) {
   if(nums.length==0)return false;
   if(nums[0]==target||nums[nums.length-1]==target)return true;
   int l=0,r=nums.length-1;
   while (l<r) {
     //处理重复数字
     while (l<r&&nums[l]==nums[l+1]) {
      l++;
    }
     while (r>l&&nums[r]==nums[r-1]) {
      r--;
    }
    int mid=(l+r)/2;
    //System.out.println(l+" "+r+" "+mid);
    if(nums[mid]==target)
      return true;
    else if (nums[mid]>target) {//中间的比目标值大
      if(target>nums[r])//只可能出现在左侧
      {
        r=mid;
      }
      else if(target<nums[r])//目标小于最右侧  当前位置可能在左半区域 也可可能在右半区域
      {
        if(nums[mid]>=nums[r])//在左侧区域
        {
          l=mid+1;
        }
        else//在右侧区域
        {
          r=mid;
        }
      }
    }
    else if(nums[mid]<target){//nums[mid]<target 中间的比目标值小
      if(target<nums[r])//目标值比最右侧的小
      {
        l=mid+1;
      }
      else if(target>nums[r])//目标值比最右侧大 只能往左侧才有希望
      {
        //但是需要分析当前在哪里
        if(nums[mid]<nums[l])//在左侧区域
        {
          r=mid;
        }
        else//在右侧区域
        {
          l=mid+1;
        }
      } 
    }
  }
   return false;
  }


大众方法为:


public boolean search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int left = 0;
        int right = nums.length - 1;
        while (left <= right) {
            int mid = left + (right-left) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[left] == nums[mid]) {
                left++;
                continue;
            }
            //前半部分有序
            if (nums[left] < nums[mid]) {
                //target在前半部分
                if (nums[mid] > target && nums[left] <= target) {
                    right = mid - 1;
                } else {  //否则,去后半部分找
                    left = mid + 1;
                }
            } else {
                //后半部分有序
                //target在后半部分
                if (nums[mid] < target && nums[right] >= target) {
                    left = mid + 1;
                } else {  //否则,去后半部分找
                    right = mid - 1;
                }
            }
        }
        //一直没找到,返回false
        return false;     
 }


结语



原创不易,bigsai请你帮两件事帮忙一下:


  1. star支持一下, 您的肯定是我在平台创作的源源动力。
  2. 微信搜索「bigsai」,关注我的公众号,不仅免费送你电子书,我还会第一时间在公众号分享知识技术。加我还可拉你进力扣打卡群一起打卡LeetCode。

记得关注、咱们下次再见!

目录
相关文章
|
29天前
【LeetCode-每日一题】 删除排序数组中的重复项
【LeetCode-每日一题】 删除排序数组中的重复项
18 4
|
29天前
|
机器学习/深度学习
Leetcode第48题(旋转图像)
这篇文章介绍了LeetCode第48题“旋转图像”的解题方法,通过原地修改二维矩阵实现图像的顺时针旋转90度。
25 0
Leetcode第48题(旋转图像)
|
29天前
|
索引
Leetcode第三十三题(搜索旋转排序数组)
这篇文章介绍了解决LeetCode第33题“搜索旋转排序数组”的方法,该问题要求在旋转过的升序数组中找到给定目标值的索引,如果存在则返回索引,否则返回-1,文章提供了一个时间复杂度为O(logn)的二分搜索算法实现。
17 0
Leetcode第三十三题(搜索旋转排序数组)
|
29天前
|
算法 C++
Leetcode第53题(最大子数组和)
这篇文章介绍了LeetCode第53题“最大子数组和”的动态规划解法,提供了详细的状态转移方程和C++代码实现,并讨论了其他算法如贪心、分治、改进动态规划和分块累计法。
51 0
|
29天前
|
C++
【LeetCode 12】349.两个数组的交集
【LeetCode 12】349.两个数组的交集
15 0
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
54 6
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
107 2
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
10 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口