前提:给定一个有序的数组,移除掉重复的数据保证每一个元素只出现一次
例子1:比如整型数组[1,1,2],输入整理后的输出结果为[1,2]
例子2:输入数组:[0,0,1,1,1,2,2,3,3,4],输出数组:[0,1,2,3,4]
数组的删除操作,常规操作如下:
// 初始化的长度就是容量(length == capacity) int length = nums.length; // 假设最后一个元素总是唯一的 // 对于每一元素,如果它跟他后面的元素相同,就把它删除掉 for (int i = length - 2; i >= 0; i--) { if (nums[i] == nums[i + 1]) { // 删除掉下标为i的元素,i后面的元素整体前移一位 for (int j = i + 1; j < length; j++) { nums[j - 1] = nums[j]; } // 删除后长度length减1 length--; } } // 返回删除重复数据后的新得长度 return length;
这实际上是一个原地操作算法,因为它并不需要额外的空间,它的空间复杂度为O(1)。空间复杂度看起来非常亮眼,但是对比起来,时间复杂度就没那么优秀了,由于存在嵌套循环的缘故,这个算法的时间复杂度达到了O(N*N)。
- 通过新建一个数组的方式解决时间复杂度问题:如果我们采用直接的方式,而不是使用原地操作算法,那么可以这样做:首先迭代该数组,然后把唯一的元素添加到新的数组中。 能这样做的原因是因为数组有序,所以我们可以很容易的通过比较第一个元素跟它前一个元素不同的方式找出每一个唯一数。比如1,1,3,3,3,拿第二个唯一元素3来举例子,3这个重复数字的第一元素3的前面是1,通过这种方式找到每一个数字。
- 潜在的问题是如何定义这个数组的长度?现实情况是我们并不知道到底这个新数组应该定义多长,而这时一个数组在创建时必须要考虑的。方案就是做一次初始迭代,找出唯一数字的个数。然后根据个数来找到长度创建一个新数组,然后把数据插入到这个数组中。
// 通过一次迭代找出数组中唯一元素的个数 int uniqueNumbers = 0; for (int i = 0; i < nums.length; i++) { // 数组中的第一个元素和不同于它之前元素的元素为唯一数 if (i == 0 || nums[i] != nums[i - 1]) { uniqueNumbers++; } } // 通过计算出的唯一元素个数来创建一个新数组用来存放不重复的数据 int[] result = new int[uniqueNumbers]; // 将唯一元素写入到新数组中 int positionInResult = 0; for (int i = 0; i < nums.length; i++) { // 执行插入操作,其实和上一个迭代的条件一致,只是后续操作不同 if (i == 0 || nums[i] != nums[i - 1]) { result[positionInResult] = nums[i]; positionInResult++; } } return result;
这并不是一个好的方案,因为它抛弃了原地操作算法,导致既需要创建新数组,增加了空间复杂度,而且时间复杂度一点没少,还是两次迭代。
这就引申出一个问题,我们如何实现一个空间复杂度为O(1),时间复杂度为O(N)的方案呢?