leetCode没那么难啦 in Java (二)
介绍
本篇介绍的是标记元素的使用,很多需要找到正确元素的问题都可以通过将正确元素应该插入的位置单独放置一个标记来记录,这样可以达到原地解决问题的效果。
Start
27.RemoveElement 删除指定元素
描述
Given an array and a value, remove all instances of that value in place and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.
The order of elements can be changed. It doesn't matter what you leave beyond the new length.
Example:
Given input array nums = [3,2,2,3], val = 3Your function should return length = 2, with the first two elements of nums being 2.
翻译: 给定一个数组一个目标值,移除那个数组中所有等于这个目标值的元素,然后返回剩下元素的个数
不要分配额外的空间给另一个数组,你需要在常数级别的内存空间完成这些操作,元素的顺序可以被改变并且在你留下什么元素并不重要
只要在你返回的元素的个数前面就好
举个例子, 给定的数组是 [3,2,2,3],目标值是 3 ,那么返回的元素个数就应该是2,原来数组的前2位也应该是2
参考思路
- 思路: 使用介绍所说的设立一个标记来标记正确元素的位置,只有碰到正确的元素了(value != target)时,标记位置才会更新元素并且移动
代码如下
public int removeElement(int[] nums, int val) {
// i表示当前正在遍历的元素的位置,j表示下一个正确元素应该插入的位置的位置
int j = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == val){
}else{
nums[j] = nums[i];
j++;
}
}
return j;
}
这是最直观的思维写出的,使用j来标记,如果碰到3了(example中的目标值3),那么什么也不做直接移动i,如果没碰到3,那么就把j位置的元素更新一下,然后移动j,最后返回的时候直接返回j即可(因为j是下一个元素应该插入的下标,假设最后遍历结束了,有5个正确元素,那么j的当前下标就是5,代表下一个应该插入的位置是数组第6个元素)
使用标记之后,时间复杂度是\(O(n)\),空间复杂度是\(O(1)\),符合要求
细微优化与简化后的代码如下
增加了一个 i != j
,意思是当i和j是一样的时候就没有必要再赋值了(i = j 时是自己给自己赋值),这样可以减少无意义赋值的操作
public int removeElement(int[] nums, int val) {
int j = 0;
for (int i = 0; i < nums.length; i++)
if (nums[i] != val && i != j++) nums[j-1] = nums[i];
return j ;
}
283. Move Zeroes 移动0元素
描述
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements.
For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].
翻译, 给定一个数组nums,编写一个函数将所有0移动到它的末尾,同时保持非零元素的相对顺序。
例如,给定nums =[0,1,0,3,12],在调用函数后,nums应该是[1,3,12,0,0]。
参考思路
- 思路一,最直观的解法,使用一个等大的数组空间,进行一轮遍历,把非0的数字都放进去,剩下的元素补0,而java int类型的数组开辟出来的元素默认就是0,连补0的操作都可以省去了。时间复杂度为\(O(n)\),空间复杂度为\(O(n)\)
代码 第一步将值赋到aux数组中,第二步将aux数组的内容再赋回给nums
public void moveZeros(int[] nums) {
int[] aux = new int[nums.length];
for (int i = 0, j = 0; i < nums.length; i++)
if (nums[i] != 0) aux[j++] = nums[i];
for (int i = 0; i < nums.length; i++)
nums[i] = aux[i];
}
- 思路2,使用我们前面提到的标记元素,使用一个标记来记录正确的位置,最后将标记后面的元素全部赋成0,这样代码时间复杂度为\(O(n)\),空间复杂度为\(O(1)\)
代码如下
public void moveZero(int[] nums){
int k = 0;//nums[0,k) 都不为0
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) nums[k++] = nums[i];
}
for (int i = k; i < nums.length; i++) {
nums[i] = 0;
}
}
同样的上面解决思路大体的是对的,然而细节之处还可以优化
- 例如
[0,1,0,3,12]
这组数据,我们的做法是将前面三位赋值成为[1,3,12]
,然后将后面两位赋值为0,然而真的一定要这样吗,显然我们需要的0本身已经存在了,所以只需要交换相应的元素即可。 - 再有上面的交换操作,大家都知道交换操作需要三步和一个中间变量,即使是使用位运算可以减免一个变量但是也依旧逃不过需要三步,所以算法中一个常见的小技巧就是用赋值来代替交换,例如将1和0进行交换,只需要将0赋值成1,再将1赋值成0即可(因为我们交换的某一个元素永远是0)
- 还有就是上面第一题中的类似优化,如果i和k的下标是相同的,那么显然这次赋值就是自己给自己赋值,也是没有必要的,因此在这里也可以进行优化
最终代码如下
public void moveZeroes(int[] nums) {
int k = 0; //nums[0,k)范围内元素都不等于0
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
//如果k和i相等,那么就不必进行无意义的赋值,直接将k往后推一位即可
if (k != i) {
nums[k] = nums[i];
nums[i] = 0;
k++;
} else k++;
}
}
}
简化后如下
public void moveZeroes(int[] nums) {
int k = 0; //nums[0,k)范围内元素都不等于0
for (int i = 0; i < nums.length; i++)
if (nums[i] != 0 && k++ != i) {//如果k和i相等,那么就不必进行无意义的赋值
nums[k - 1] = nums[i];
nums[i] = 0;
}
}
26.RemoveDuplicatesFromSortedArray 移除有序数组的重复元素
描述
Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.
For example,
Given input array nums = [1,1,2],Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the new length.
翻译 给定一个排序的数组,移除里面重复的元素这样每一个元素只出现一次,并且返回新的长度
不要使用为新的数组开辟额外的内存空间,你必须在原数组的内存空间上操作
举个例子,给定一个输入数组[1,1,2]
你的方法应该返回 2 ,并且nums数组的前两位应该是[1,2]
ps:对于java语言来说,在不使用额外的内存空间的情况下数组长度是不能改变滴,所以这道题
可以转变为例如给定数组为[1,2,3,3,5,5],返回结果长度应该为4,数组可以变为[1,2,3,5,x,x] x代表任意字符即可
参考思路
- 注意题目中的条件是有序数组,这条信息很重要,要充分利用,依旧是使用标记非重复的元素的位置来完成
- k为正确的元素应该插入的位置,i为当前正在遍历的元素,如果当前遍历的元素等于应该插入的元素的前一个位置的元素(因为数组是有序的,所以相同的数字是连在一起的),那么就证明当前遍历的元素已经在前面存在过了,直接跳过,去看下一个,否则的话就赋值,然后看下一个
代码如下
public int removeDuplicates(int[] nums) {
//特殊情况的判断,如果数组为null或者0,直接返回0,如果是1个元素显而易见肯定是不重复的返回1
if (nums == null || nums.length == 0) return 0;
if (nums.length == 1) return 1;
int k = 1; //arr[1,k)正确元素应当插入的位置,这里从1开始算,从0开始算也应该维护0的定义
for (int i = 1; i < nums.length; i++) {
//k是应当插入的位置,所以k-1就是应当插入的位置的前一个元素,k和k-1位置的元素不应当相同
if (nums[i] != nums[k - 1]) {
//如果判断通过,说明当前遍历的nums[i]是一个独立的数字(因为数字是有序的),将nums[k]的值变为nums[i]
nums[k] = nums[i];
k++;//k标记往后推一位
}
}
return k;
}
简化后代码如下
public int removeDuplicates(int[] nums) {
if (nums == null || nums.length ==0 ) return 0;
int k = 1;
for (int i = 1; i < nums.length; i++)
if (nums[i] != nums[k - 1]) nums[k++] = nums[i];
return k;
}
80.RemoveDuplicatesFromSortedArrayII 移除有序数组的重复元素II
描述
Follow up for "Remove Duplicates":
What if duplicates are allowed at most twice?For example,
Given sorted array nums = [1,1,1,2,2,3],Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3. It doesn't matter what you leave beyond the new length.
翻译:承接移出重复的数字
如果每个数字要求保留最多出现两次呢?例如,给定一个排序好的数组 nums=[1,1,1,2,2,3]
你的方法应该返回长度为5 , 并且nums前5个元素是 [1,1,2,2,3]
参考思路
- 这道题是上一道题的一点升级,意思是每个元素要保留2次,其余还是一样,其实懂得了上面那面的思路十分容易,加一个计数的变量来计算重复元素出现的次数,第一次重复出现了就继续赋值,第二次或者更多的次数出现了就跳过,直到遇到一个新的元素清空这个计数器即可。
代码如下
public int RemoveDuplicatesfromSortedArrayII(int[] nums) {
if (nums == null || nums.length == 0) return 0;
int k = 1;
int count = 1;//思路与移出重复数字I的题目一样,只是这里多加一个变量用于计数,默认为1,因为我们是从第二个数开始遍历的
for (int i = 1; i < nums.length; i++) {
if ( nums[i] != nums[k - 1]) {
nums[k++] = nums[i];
count = 1;//每次寻找到新的数字的时候将count赋值为1
} else {
if (count >= 2) {//允许重复的数字出现2次,如果超过2次了,就跳过
continue;
}
nums[k++] = nums[i];
count++;
}
}
return k;
}
简化后如下
public int RemoveDuplicatesfromSortedArrayII(int[] nums) {
if (nums == null) return 0;
int k = 0;
int count = 0;
for (int i = 0; i < nums.length; i++) {
if ( k==0 || nums[i] != nums[k - 1]) {
nums[k++] = nums[i];
count = 1;
} else if(++count <= 2) nums[k++] = nums[i];
}
return k;
}
End
- 总结一下,本篇讲述的思想是使用一个标记来标记需要当前需要插入的元素的位置,并且举了几道题的例子来讲述如何使用,为了方便理解,举的例子都比较简单,都使用一个标记,但是相信如果理解了这种解决思路,遇到复杂的需要使用多个标记的,只要定义清楚,解决起来也不会困难。
- 另外中间有几道还讲了一些简单的优化,例如看看能不能将交换变成赋值, 或者减少无意义的赋值,这样也可以对算法进行一些常数级别的优化,也是不错的。下篇再见。
leetCode没那么难啦 in Java (二)的更多相关文章
- leetCode没那么难啦 in Java (一)
前言 感觉写博客是一个很耗心力的东西T_T,简单的写了似乎没什么用,复杂的三言两语也只能讲个大概,呸呸...怎么能有这些消极思想呢QAQ!那想来想去,先开一个leetcode的坑,虽然已经工作了 ...
- .NET C#到Java没那么难,Servlet篇
前言 .NET C#到Java没那么难,都是面向对向的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都 ...
- .NET C#到Java没那么难,MVC篇
最典型的JAVA MVC就是JSP + servlet + javabean的模式.比较好的MVC,老牌的有Struts.Webwork.新兴的MVC 框架有Spring MVC.Tapestry.J ...
- .NET C#到Java没那么难,DB篇
前言 .NET C#到Java没那么难,都是面向对象的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都 ...
- .NET C#转Java没那么难,开发环境篇
.NET C#转Java没那么难,都是面向对向的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都是co ...
- block没那么难(二):block和变量的内存管理
本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> 了解了 block的实现,我们接着 ...
- block没那么难(一):block的实现
本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> block 顾名思义就是代码块,将 ...
- php实现把数组排成最小的数(核心是排序)(看别人的代码其实也没那么难)(把php代码也看一下)(implode("",$numbers);)(usort)
php实现把数组排成最小的数(核心是排序)(看别人的代码其实也没那么难)(把php代码也看一下)(implode("",$numbers);)(usort) 一.总结 核心是排序 ...
- 没那么难,谈CSS的设计模式
没那么难,谈CSS的设计模式 来源: 灵感的小窝 发布时间: 2016-09-09 16:46 阅读: 8949 次 推荐: 27 原文链接 [收藏] 什么是设计模式? 曾有人调侃, ...
随机推荐
- C# 三层架构之系统的登录验证与添加数据的实现
利用三层架构体系,实现学生管理系统中用户的登录与添加班级信息的功能,一下代码为具体实现步骤的拆分过程: 一.用户登录界面功能的实现 1.在数据访问层(LoginDAL)进行对数据库中数据的访问操作 u ...
- [js高手之路]深入浅出webpack教程系列3-配置文件webpack.config.js详解(下)
本文继续接着上文,继续写下webpack.config.js的其他配置用法. 一.把两个文件打包成一个,entry怎么配置? 在上文中的webpack.dev.config.js中,用数组配置entr ...
- 浅谈java中==与equals的区别
今天做了一个业务模块,需要简单的遍历比较值,所以习惯性的用了 "==" ,但是结果没有达到预想的结果是什么鬼? 看到这里,有人一定会指出这俩货不是基本变量! "关系操作符 ...
- this的用法 – JavaScript深入浅出(二)
写在前面 上一篇中,我们对于JavaScript中原始值.复杂值以及内存空间进行了一个深入浅出的总结,这次我们来聊一聊JavaScript中this关键字的深入浅出的用法. 在 JavaScript ...
- dreamweaver代码提示失效
原文地址:dreamweaver代码提示失效作者:云中雁 2007-03-23 12:19:22| 分类: 编程手记 | 标签:web2.0 javascript |字号大中小 订阅 吴庆民 ...
- 【Beta】阶段 第四次Daily Scrum Meeting
每日任务 1.本次会议为第四次 Meeting会议: 2.本次会议在周四下午16:40,课间休息时间在陆大楼召开,召开本次会议为10分钟. 一.今日站立式会议照片 二.每个人的工作 (有work it ...
- 201521123091 《Java程序设计》第7周学习总结
Java 第七周总结 第七周的作业. 目录 1.本章学习总结 2.Java Q&A 3.码云上代码提交记录及PTA实验总结 1.本章学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内 ...
- 201521123003《Java程序设计》第7周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 Q1.ArrayList代码分析 1.1 解释ArrayList的contains源 ...
- 201521123036 《Java程序设计》第6周学习总结
本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 1.2 可选:使用常规方法总结其他上课内容. 对象克隆:Clon ...
- 201521123080《Java程序设计》第14周学习总结
本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 书面作业 MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) ...