一、移除元素
大概读一下题就是,现在给你一个整型数组,整型数组里有一系列值,然后我给你一个特定的值val,需要你把数组里面等于val的值全部删去,最后得到的数组的顺序无所谓,返回数组中除了val值以外的元素的个数,不在乎数组中元素顺序。
1.暴力解决
比较暴力的话就是你说啥我做啥,你不是让我删去嘛,我就一个一个比较,由于你要求前面k个是不同于val的元素,我每次比较完,如果相同了,那我就删去并后面全部前移,不同就不管。
外层循环遍历整个数组的元素,内层循环检查每次遍历到的元素是否和val一致。
2.空间换时间
如果就直接按照题设的话,两层循环一嵌套,直接干成O(n^2),干脆创建一个临时数组,如果跟val相等,那就不复制,如果不相等,那就复制,最后覆盖nums前k个值即可:
最外层if就是因为给的案例有nums为空,不加的话不能通过全部案例,这里就是典型空间换时间,临时数组temp储存不等的值,最后返回去。
3.双指针法
其实我们上面暴力算法就类似于顺序表中的查找+指定位置删除,这种数据连续的空间我们也可以尝试双指针法,当然,不是真的指针,因为内存空间是连续的,只是下标而已,但是效果像指针一样。
说了这么多,给人整懵了,来看图:
对于此题src和dest都先指向数组起始位置,src的作用是找到不等同于val的值,dest是站岗,为了接收后面不用于val的值。
可以看出来dest仅仅是站岗,不需要检测指向的值,src指向的值需要判断是否是val,目的在于把src指向的不同于val的值依次复制到dest所指的位置。
对于这个样例就是这样的:
代码:
就是用俩指针,一个站岗,一个寻找(好像一个描点一样,遇到危险就传送回去)。
二、删除有序数组中的重复项
双指针法
不再去写暴力和空间换时间,没啥太大用处,直接想想怎么用俩指针去遍历:
这里双指针初始化的时候一个给数组起始位置,一个给数组的起始位置后面的一个位置,原因是自己跟自己肯定相等,那么就直接让src从第二个位置开始去探路。
如果nums[src] == nums[dest]那么src++,因为src只为找到和nums[dest]所不同的
如果nums[src]!=nums[dest]那么先dest++,因为题目要求原顺序不能变,题目所给是有序数组,有序数组找到相等的肯定是在dest后面,如果想保持顺序不变,替换的应该是dest后面的那个值,因为如果有相同必定相邻,比如这里替换后面的1,顺序还是1,2)即nums[dest] = nums[src],再由src去探路,看看有没有和现在的dest相等的值。
思路写完,代码表达:
三、合并两个有序数组
双指针法
还是直接考虑双指针法,因为比较暴力的方法,比如我们可以只合并再排序,目前学过的排序有两种,一个是冒泡排序,时间复杂度是O(n^2),还有一个是快速排序,当然,因为没有自己完整实现过,后面学排序再讨论时间复杂度。
不如想想怎样用双指针,使得可以一个循环结束问题。
稍微读读题就知道,nums1里面最后存的是两个数组的元素,所以dest指针遍历nums1,src指针遍历nums2。
如果我们还按照上面的思想,从前往后这么来的话,肯定是不行的,因为如果dest指向的比src大的话我们就会后移dest开始后面所有的数据,循环嵌套不就成O(n^2)了嘛,所以这里就不从前往后遍历了,从后往前遍历找大的数据呢,就不需要关心移动的问题了。
但是这样的话俩指针貌似不够了,因为你还得有一个指针专门去记录放数据的位置,因此:
自己写写就发现问题了,如果dest指针移动,那么说明产生空位了,你flag只遍历初始的空位是不够的,所以我们将循环的条件改成src全部复制过来就停手,因为src完了就移完了;dest完了,src没有完不还得移嘛:
提交通过,做这些题前面那些都是防御性的代码,给我的示例里面都有某个数组为空的。
后面第一个while循环去做我们上面的思路,但是如果dest首先越界了,而src并没有越界,那就会导致数据并没有完全移过去,所以最后再写一个while循环防止这种事发生:
两层循环,基本可以看作O(n)了。