本文已参与「新人创作礼」活动,一起开启掘金创作之路。
有序数组去重算法
有序数组去重相对来说更简单,因为本身就是有序的,假设从小到大有序,那么给定x元素,一定满足 left <= x <= right,也就是x元素的左边或者右边,要么小于/大于x,要么相等,如果相等,我们就可以认为x重复。同理,如果假设从大到小有序,那么给定x元素,一定满足 left >= x >= right,也就是x元素的左边或者右边,要么大于/小于x,要么相等。
算法实现
因为是有序数组,为了提高去重效率,取一个元素往后一直比对,也就是上面说的,如果相邻的相等表示是重复的,继续往后,直到不相等,也就是遇到一个不重复的为止,将这个不重复的元素移动到该元素的下一个,然后用这个不重复的元素为起始,重复上述操作。直到最后。
因为只循环一遍,有序数组去重算法的复杂度是O(n)。
代码如下,只进行一次循环,复杂度O(n)
完整代码在github上面,只需要clone下来执行composer install然后执行 php artisan test:sortDeduplicate 就可以看到结果了
/**
* 因为是有序数组,为了提高去重效率,取一个元素往后一直比对,如果相邻的相等表示是重复的
* 继续往后,直到不相等,也就是遇到一个不重复的为止,将这个不重复的元素移动到该元素的下一个
* 然后用这个不重复的元素为起始,重复上述操作。直到最后
* 删除最后一个不重复元素后面所有的元素
* 因为只循环一遍,复杂度O(n)
*
* @return mixed
*/
public function handle()
{
$a = [3,4,4,4,5,6,6,8,10,10,34];
dump($a);
//分别代表第一个数据,和要比对的数据
$i = 0;$j=0;
$len = count($a);
dump("长度:".$len);
while ((++$j) < $len) { //循环全部数据
//从$i往后寻找,如果相邻的相等表示是重复的,继续往后,直到不相等,也就是遇到一个不重复的为止,将这个不重复的元素移动到该元素的下一个
if($a[$i] != $a[$j]) {
$a[++$i] = $a[$j];
}
}
//截取前面去重过的数据
$a = array_slice($a, 0, ++$i);
dd($a);
}
无序数组去重算法
无序数组处理起来就很麻烦了,因为你不知道重复的那个元素在哪里,必须整个遍历完才能把所有重复的元素去掉,所以是两层循环,在第一层循环里面找到给定x元素,在第二层循环里面找到x的重复元素并删除。即完成去重。这么来看,去重之前先进行排序,可以有效提升去重的效率。
算法实现
无序数组去重算法的复杂度是O(n2)。
代码如下,首先进行外层循环,复杂度O(n),然后查找这个元素之前的元素中有没有重复的,复杂度O(n),如果有就删除,复杂度O(1),没有就下一个元素,复杂度O(1)。加起来复杂度O(n2)。
完整代码在github上面,只需要clone下来执行composer install然后执行 php artisan test:unsortDeduplicate 就可以看到结果了
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$a = [4,5,4,3,8,6,6,10,34,10,4];
dump($a);
$i = 1;
$len = count($a);
dump("长度:".$len);
while ($i < $len) { //循环全部数据
//在整个数组中寻找这个值,如果找到了就删除他,如果没找到就下一个
$preIndex = $this->find($i, $a);
if ($preIndex!==false) {unset($a[$preIndex]);}
else $i++;
}
dd($a);
}
private function find($i, array $a) {
$index = 0;
//循环从0到这个下标
while ($index < $i) {
//不存在说明被删除了
if (!array_key_exists($index, $a)) {$index++;continue;}
//如果找到了返回下标
if ($a[$i] == $a[$index]) return $index;
else $index++;
}
return false;
}