当前位置: 首页 > news >正文

算法基础五

移除元素

给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

解题思路:这里数组的删除并不是真的删除,只是将要删除的元素移动到数组的后面,然后返回数组实际有效的长度即可。

public int removeElement(int[] nums, int val) {if (nums == null || nums.length == 0) {return 0;}int len = nums.length;int index = 0;for(int i = 0; i < len; i++) {if (nums[i] != val) {nums[index++] = nums[i];                }}return index;}

两数相除

给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用 乘法、除法和取余运算。
整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。
返回被除数 dividend 除以除数 divisor 得到的 商 。

示例 1:
输入: dividend = 10, divisor = 3 输出: 3 解释: 10/3 = 3.33333… ,向零截断后得到 3 。
示例 2:
输入: dividend = 7, divisor = -3 输出: -2 解释: 7/-3 = -2.33333… ,向零截断后得到 -2 。

解题思路:可以用二分搜索来做。把除法运算后的商作为搜索目标,商的取值范围是[0,dividend]。利用二分好到(商 + 1)* 除数>被除数并且 商 * 除数 < 被除数或者 (商 + 1)* 除数 > 被除数 并且 商 * 除数 < 被除数的时候,就算找到商了,其余情况继续二分即可。


public int divide(int dividend, int divisor) {// 考虑被除数为最小值的情况if (dividend == Integer.MIN_VALUE) {if (divisor == 1) {return Integer.MIN_VALUE;}if (divisor == -1) {return Integer.MAX_VALUE;}}// 考虑除数为最小值的情况if (divisor == Integer.MIN_VALUE) {return dividend == Integer.MIN_VALUE ? 1 : 0;}// 考虑被除数为 0 的情况if (dividend == 0) {return 0;}// 一般情况,使用二分查找// 将所有的正数取相反数,这样就只需要考虑一种情况boolean rev = false;if (dividend > 0) {dividend = -dividend;rev = !rev;}if (divisor > 0) {divisor = -divisor;rev = !rev;}int left = 1, right = Integer.MAX_VALUE, ans = 0;while (left <= right) {// 注意溢出,并且不能使用除法int mid = left + ((right - left) >> 1);boolean check = quickAdd(divisor, mid, dividend);if (check) {ans = mid;// 注意溢出if (mid == Integer.MAX_VALUE) {break;}left = mid + 1;} else {right = mid - 1;}}return rev ? -ans : ans;}// 快速乘public boolean quickAdd(int y, int z, int x) {// x 和 y 是负数,z 是正数// 需要判断 z * y >= x 是否成立int result = 0, add = y;while (z != 0) {if ((z & 1) != 0) {// 需要保证 result + add >= xif (result < x - add) {return false;}result += add;}if (z != 1) {// 需要保证 add + add >= xif (add < x - add) {return false;}add += add;}// 不能使用除法z >>= 1;}return true;}

串联所有单词的子串

给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。
例如,如果 words = [“ab”,“cd”,“ef”], 那么 “abcdef”, “abefcd”,“cdabef”, “cdefab”,“efabcd”, 和 “efcdab” 都是串联子串。 “acdbef” 不是串联子串,因为他不是任何 words 排列的连接。
返回所有串联子串在 s 中的开始索引。你可以以 任意顺序 返回答案。

示例 1:
输入:s = “barfoothefoobarman”, words = [“foo”,“bar”] 输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。 子串 “barfoo” 开始位置是 0。它是 words 中以 [“bar”,“foo”] 顺序排列的连接。 子串 “foobar” 开始位置是 9。它是words 中以 [“foo”,“bar”] 顺序排列的连接。 输出顺序无关紧要。返回 [9,0] 也是可以的。
示例 2:
输入:s = “wordgoodgoodgoodbestword”, words = [“word”,“good”,“best”,“word”] 输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。 s 中没有子串长度为 16 并且等于 words的任何顺序排列的连接。 所以我们返回一个空数组。
示例 3:
输入:s = “barfoofoobarthefoobarman”, words = [“bar”,“foo”,“the”],输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。 子串 “foobarthe” 开始位置是 6。它是 words 中以 [“foo”,“bar”,“the”] 顺序排列的连接。 子串 “barthefoo” 开始位置是 9。它是 words 中以[“bar”,“the”,“foo”] 顺序排列的连接。 子串 “thefoobar” 开始位置是 12。它是 words 中以 [“the”,“foo”,“bar”] 顺序排列的连接。

解题思路:这道题有两个前提条件。1,字符串数组里面的字符串长度都是一样的;2,字符串数组中的字符串都要连续连在一起,前后顺序可以任意组合。
先将字符串数组中所有字符串都存到map中,并累计出现的次数,然后从源字符串从头开始遍历,每次判断字符串数组里面的字符串是否全部用完,如果全部用完并且长度正好是字符串数组任意排列组合的长度,就记录这个组合的起始下标。

public List<Integer> findSubstring(String s, String[] words) {List<Integer> res = new ArrayList<Integer>();int wordNum = words.length;if (wordNum == 0) {return res;}int wordLen = words[0].length();//HashMap1 存所有单词,记录出现的次数HashMap<String, Integer> allWords = new HashMap<String, Integer>();for (String w : words) {int value = allWords.getOrDefault(w, 0);allWords.put(w, value + 1);}//遍历所有子串for (int i = 0; i < s.length() - wordNum * wordLen + 1; i++) {//HashMap2 存当前扫描的字符串含有的单词HashMap<String, Integer> hasWords = new HashMap<String, Integer>();int num = 0;//判断该子串是否符合while (num < wordNum) {String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);//判断该单词在 HashMap1 中if (allWords.containsKey(word)) {int value = hasWords.getOrDefault(word, 0);hasWords.put(word, value + 1);//判断当前单词的 value 和 HashMap1 中该单词的 valueif (hasWords.get(word) > allWords.get(word)) {break;}} else {break;}num++;}//判断是不是所有的单词都符合条件if (num == wordNum) {res.add(i);}}return res;}

搜索旋转排列数组

整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

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

解题思路:给定一个升序排列的数组,并且无重复值。需要把后面随机一段有序数组放到数组前面,形成前后两段有序的子数组。因为数组有序,可以使用二分搜索算法来实现。

public int search(int[] nums, int target) {int len = nums.length;if (len == 0) {return - 1;}if (len == 1) {return nums[0] == target ? 0 : -1;}int l = 0;int r = len - 1;while(l <= r) {int mid = (l + r) / 2;if (nums[mid] == target) {return mid;}if(nums[0] <= nums[mid]) {if (nums[0] <= target && target < nums[mid]) {r = mid - 1;} else {l = mid + 1;}} else {if (nums[mid] < target && target <= nums[len - 1]) {l = mid + 1;} else {r = mid - 1;}}}return -1;}

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

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。

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

解题思路:二分搜搜算法

public int[] searchRange(int[] nums, int target) {int leftIdx = binarySearch(nums, target, true);int rightIdx = binarySearch(nums, target, false) - 1;if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {return new int[]{leftIdx, rightIdx};}return new int[] {-1, -1};}public int binarySearch(int[] nums, int target, boolean lower) {int left = 0;int right = nums.length - 1;int ans = nums.length;while(left <= right ) {int mid = (left + right) / 2;if (nums[mid] > target || (lower && nums[mid] >= target)) {right = mid - 1;ans = mid;} else {left = mid + 1;}}return ans;}

相关文章:

  • flink安装与配置-脚本一键安装(超简单)
  • 类和对象(上篇)
  • mysql面试题——日志与MVCC
  • 数据链路层之VLAN基本概念和基本原理
  • Excel导入组件的封装以及使用页面点击弹出该弹框
  • 营销互动类小游戏策划与开发
  • 【Ratis】Grpc.proto文件里定义的一些RPC
  • Mysq8l在Centos上安装后忘记root密码如何重新设置
  • windows系统mobaxterm远程执行linux上ssh命令
  • Sublime text 添加到鼠标右键菜单,脚本实现
  • 【大模型】更强的 ChatGLM3-6B 来了,开源可商用
  • 虚假IP地址攻击的溯源方法
  • MDK5改造之格式化以及文件函数注释插件和主题应用
  • C/C++内存管理(含C++中new和delete的使用)
  • SpringCloud 微服务全栈体系(十八)
  • 【Linux系统编程】快速查找errno错误码信息
  • 【RocksDB】TransactionDB源码分析
  • 【附node操作实例】redis简明入门系列—字符串类型
  • JavaScript 一些 DOM 的知识点
  • Javascript编码规范
  • Joomla 2.x, 3.x useful code cheatsheet
  • JS+CSS实现数字滚动
  • KMP算法及优化
  • Meteor的表单提交:Form
  • Next.js之基础概念(二)
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 正则与JS中的正则
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • ( 10 )MySQL中的外键
  • (2.2w字)前端单元测试之Jest详解篇
  • (8)STL算法之替换
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)spring boot校园拼车微信小程序 毕业设计 091617
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转)visual stdio 书签功能介绍
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (转)项目管理杂谈-我所期望的新人
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • .Net6 Api Swagger配置
  • .Net高阶异常处理第二篇~~ dump进阶之MiniDumpWriter
  • .NET框架
  • [].slice.call()将类数组转化为真正的数组
  • [14]内置对象
  • [AIGC] MySQL存储引擎详解
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法