【双指针】双指针算法详解两道经典OJ【力扣27,力扣26,力扣38】超详细算法教程

【双指针】双指针算法详解两道经典OJ【力扣27,力扣26,力扣38】超详细算法教程
在这里插入图片描述
今天又又到了我们刷力扣题的时间啦!
今天博主给大家带来的三道题是:
27. 移除元素
26. 删除有序数组中的重复项
88. 合并两个有序数组

这三道题,都是双指针算法题的一个入门题,掌握这三道题,认识双指针算法,是我们今天的目标!

前言

那么这里博主先安利一下一些干货满满的专栏啦!

作者: #西城s
这是我的主页:#西城s
在食用这篇博客之前,博主在这里介绍一下其它高质量的编程学习栏目:
数据结构专栏:数据结构 这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏:算法 这里可以说是博主的刷题历程,里面总结了一些经典的力扣上的题目,和算法实现的总结,对考试和竞赛都是很有帮助的!
力扣刷题专栏:Leetcode 想要冲击ACM、蓝桥杯或者大学生程序设计竞赛的伙伴,这里面都是博主的刷题记录,希望对你们有帮助!
C的深度解剖专栏:C语言的深度解剖 想要深度学习C语言里面所蕴含的各种智慧,各种功能的底层实现的初学者们,相信这个专栏对你们会有帮助的!
在这里插入图片描述


OJ27 移除元素

题目描述

在这里插入图片描述


算法分析

题目的意思其实很简单,其实就是把一个数在数组里面去掉。
那么很容易想到的思路就是:

  • 开一个空间,将符合条件的拷贝下来,但是按照这种方法我们时间虽然是O(n),但我们的空间就是O(n)了,不是最优解法。
  • 另一种比较好想到的方法就是,写一个删除单个数字的函数,遍历数组,但是这样的时间就不是O(n)了,因为我们删除元素的时候需要挪动数据,这样效率是非常低的。

这时候,我们可以想想双指针的方法。
双指针其实就是对一个数组用两个下标进行处理(或者两个指针)
所以这题我们可以采用覆盖的方法来解决:

定义两个指针prevheadhead跑在前面,prev跑在后面。
在这里插入图片描述


实现代码

int removeElement(int* arr, int numsSize, int val) {
	//1 2 2 2 3 4 5 6 2
	int* ahead = arr;
	int* end = arr + numsSize - 1;
	int* prev = arr;
	while (ahead <= end) {
		if (*ahead == val) {
			ahead++;
			numsSize--;
		}
		else {
			*prev = *ahead;
			prev++;
			ahead++;
		}
	}
	return numsSize;
}

OJ26 删除有序数组中的重复项

题目描述

在这里插入图片描述


算法分析

不用多说,我们的目标肯定一定要在时间O(n),空间O(1)内完成的。
同样,和上一道题类似的思路,使用双指针,覆盖元素的方式来解决。

  • 这道题唯一的两个区别,就是我们要留一个,不要完全删掉,那其实,我们只需要将上一题的prev,在覆盖之前,先++以下就行了。同时,因为是删除重复项,所以我们的prevahead在初始化的时候,ahead要先往前一个位置。

这个算法其实就是一个经典的去重算法。


实现代码

int removeDuplicates(int* nums, int numsSize) {
	//去重算法
	//双指针
	//1 2 2 3 4 5 6 2 2 2 2
	int* ahead = nums + 1;
	int* prev = nums;
	int* end = nums + numsSize - 1;
	while (ahead < end) {
		if (*ahead == *prev) {
			ahead++;
		}
		else {
			prev++;
			*prev = *ahead;
			ahead++;
		}
	}
	return prev-nums;
}

OJ88 合并两个有序数组

题目描述

在这里插入图片描述


算法分析

其实这个合并,我们最容易想到的,其实就是把两个数组搞在一起,再排序一下就行了。
但是,我们要意识到,排序的代价是比较大的。因此,我们要采用双指针,归并算法。
其实这个算法就是归并排序所用的思想。
这里博主提供两种思路:

第一种思路: 如图:
在这里插入图片描述
这种思路和归并排序的思路有点类似,最后我们再将新开数组的内容拷贝回原来的位置就行了。

第二种思路:
我们同样可以采用覆盖方法,思路和前面两题十分类似,这里就不赘述了。
注意:
这里的遍历要从后往前,这个画个图就能很好理解了,因为从前往后跑,会覆盖原来有用的元素,所以我们要从后往前覆盖,找大的放到nums1里面去。
第二种思路的实现我们可以达到空间复杂度可以到O(1),时间复杂度O(n),博主这里提供第二种思路的实现代码

实现代码

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
	int* end1 = nums1 + m - 1;
	int* end2 = nums2 + n - 1;
	int* end = nums1 + m + n - 1;
	while ((end1 >= nums1) && (end2 >= nums2)) {
		if (*end1 > *end2) {
			*end-- = *end1--;
		}
		else if (*end1 < *end2) {
			*end-- = *end2--;
		}
	}
	while (end2 - nums2 >= 0) {
		*end-- = *end2--;
	}
}

尾声

看到这里相信你对双指针算法的这两道OJ已经有一定的理解了,如果你觉得有帮助的话请不要吝啬你们的点赞收藏关注和转发噢!

posted @ 2022-04-23 21:10  背包Yu  阅读(23)  评论(0编辑  收藏  举报  来源