介绍

   本篇介绍的是标记元素的使用,很多需要找到正确元素的问题都可以通过将正确元素应该插入的位置单独放置一个标记来记录,这样可以达到原地解决问题的效果。

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 = 3

    Your 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 (二)的更多相关文章

  1. leetCode没那么难啦 in Java (一)

    前言    感觉写博客是一个很耗心力的东西T_T,简单的写了似乎没什么用,复杂的三言两语也只能讲个大概,呸呸...怎么能有这些消极思想呢QAQ!那想来想去,先开一个leetcode的坑,虽然已经工作了 ...

  2. .NET C#到Java没那么难,Servlet篇

    前言 .NET C#到Java没那么难,都是面向对向的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都 ...

  3. .NET C#到Java没那么难,MVC篇

    最典型的JAVA MVC就是JSP + servlet + javabean的模式.比较好的MVC,老牌的有Struts.Webwork.新兴的MVC 框架有Spring MVC.Tapestry.J ...

  4. .NET C#到Java没那么难,DB篇

    前言 .NET C#到Java没那么难,都是面向对象的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都 ...

  5. .NET C#转Java没那么难,开发环境篇

    .NET C#转Java没那么难,都是面向对向的语言,而且语法还是相似的,先对比一下开发环境,再到Servlet,再到MVC,都是一样一样的,只是JAVA的配制项比较多而已,只要配好一个,后面都是co ...

  6. block没那么难(二):block和变量的内存管理

    本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> 了解了 block的实现,我们接着 ...

  7. block没那么难(一):block的实现

    本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> block 顾名思义就是代码块,将 ...

  8. php实现把数组排成最小的数(核心是排序)(看别人的代码其实也没那么难)(把php代码也看一下)(implode("",$numbers);)(usort)

    php实现把数组排成最小的数(核心是排序)(看别人的代码其实也没那么难)(把php代码也看一下)(implode("",$numbers);)(usort) 一.总结 核心是排序 ...

  9. 没那么难,谈CSS的设计模式

    没那么难,谈CSS的设计模式 来源: 灵感的小窝  发布时间: 2016-09-09 16:46  阅读: 8949 次  推荐: 27   原文链接   [收藏]   什么是设计模式? 曾有人调侃, ...

随机推荐

  1. AndroidTv Home界面实现原理(二)——Leanback 库的主页卡位缩放动画源码解析

    先看个效果图: 上一篇中,我们留了问题,在 Tv Home 界面这种很常见聚焦卡位放大动画效果,我们这一篇就来看看 Leanback 库是怎么实现的. 如果要我们自己实现的话,思路应该不难,就是写个放 ...

  2. this和super的运用

    这几天看到类在继承时会用到this和super,这里就做了一点总结,与各位共同交流,有错误请各位指正~ this this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针. this ...

  3. h5drag事件

    在拖动目标上触发事件 (源元素):ondragstart - 用户开始拖动元素时触发ondrag - 元素正在拖动时触发ondragend - 用户完成元素拖动后触发释放目标时触发的事件:ondrag ...

  4. 【笔记】shellcode相关整理

    0x01:shellcode定义 Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限.另外,Shellcode一般是作为数据发送给受攻击服务器 ...

  5. Python Keras module 'keras.backend' has no attribute 'image_data_format'

    问题: 当使用Keras运行示例程序mnist_cnn时,出现如下错误: 'keras.backend' has no attribute 'image_data_format' 程序路径https: ...

  6. PHP(Math的调用)

    <script> //数学函数(用Math来调用)://round=四舍五入最接近的整数// var l = 1.1;// var y1 = Math.round(l);// docume ...

  7. 201521123111《Java程序设计》第14周学习总结

    本次作业参考文件 MySql操作视频与数据库相关jar文件请参考QQ群文件. 1. 本周学习总结 1.1 以你喜欢的方式(思维导图.Onenote或其他)归纳总结多数据库相关内容. 连接数据库前,应先 ...

  8. java课程设计-算术运算测试

    1. 团队名称.团队成员介绍 团队名称:cococo 团队成员 组长:网络1514叶城龙 201521123109 组员:网络1514余腾鑫 201521123108 2. 项目git地址 http: ...

  9. Mybatis学习(二)常用对象SqlSessionFactory和SqlSession

    1.SqlSessionFactory SqlSeesionFactory对象是MyBatis的关键对象,它是一个数据库映射关系经过编译后的内存镜像. SqlSeesionFactory对象的实例可以 ...

  10. Cheat sheet for Jupyter Notebook

    近期,DataCamp发布了jupyter notebook的 cheat sheet,[Python数据之道]第一时间与大家一起来分享下该cheat sheet的内容. 以下是该cheat shee ...