算法学习-数组元素移动

前言
学习需要一步一个脚印。
刷题是每个程序员的必经之路。但是单单停留在刷过之后就算了,或者不懂查看别人的代码之后那一小段时间的理解是远远不够的,这种短时的记忆带来的负面效果是极其巨大的,比如我刷了很多题了,但是每次遇到新的题目总觉得以前遇到过,但重新编写的话却望而止步。为了避免这种情况,我们需要真正的理解每道题后面的原理,及时做出总结,这样才能永久的记在我们脑海里。
感谢您百忙之中抽出时间阅读此文章,接下来我会根据文章标题给出LeetCode上的相应有关这类话题的题目,并做出详细解析,再次感谢您的阅读。

提示 : 希望您在阅读完之后先尝试思考之后在看解析,这样效果会更好一点

LeetCode26

题目要求是这样的:给你一个排序之后的数组,需要删除数组中重复的元素,且不能开辟新的数据。
也就是说空间复杂度为O(1),我们只能在给的数组上操作。

思路
我们可以使用双指针进行扫描,定义指针i = 0 指向数组开头,j = 1 指向i的下一位,如果第 j 个元素不等于就把 i + 1 位置的数替换成 j 位置的元素. i 和 j 就同时往后移一位,如果第 j 个元素等于第 i 个元素,说明遇到重复的了我们就把 j 往后移动直到遇到不重复为止
我们使用一组数据来模拟一下这个过程
arr = {0 , 1 , 1 , 2 , 3}

  • 1: i = 0 , j = 1 ,进入循环 arr[ i ] = 0 , arr[ j ] = 1 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++
  • 2: i = 1 , j = 2 ,arr[ i ] = 1 , arr[ j ] = 1 两个元素相等 j ++
  • 3: i = 1 , j = 3 ,arr[ i ] = 1 , arr[ j ] = 2 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++. 此时的数组为
    arr = { 0 , 1 , 2 , 2 , 3}
  • 4 i = 2 , j = 4 ,arr[ i ] = 2 , arr[ j ] = 3 两个元素不相等 i ++ , arr[ i ] = arr[ j ] , j++. 此时的数组为
    arr = { 0 , 1 , 2 , 3 , 3}
    此时 j 已经扫描完整个数组,我们就可以退出循环了,然后i的位置就是我们不重复元素的最后一个下标

下面试代码

//消除数组中的重复元素
public class LeetCode26 {
	public int removeDuplicates(int[] nums) {
		if (nums.length == 0)
			return 0;
		int i = 0;
		//用后面不重复的数字替换前面重复的数字    i记录重复数字的下标   j则扫描不重复的数字
		//这样只能解决以排序的数组
		for (int j = 1; j < nums.length; j++) {
			if (nums[j] != nums[i]) {
				i++;
				nums[i] = nums[j];
			}
		}
		return i + 1;
	}
	public static void main(String[] args) {
		int[] nums = { 1, 2 ,3, 4 , 5};
		LeetCode26 test = new LeetCode26();
		System.out.println(test.removeDuplicates(nums));
	}
}

LeetCode80

现在我们在26的基础上提升一下难度,如果我们允许出现次数为2次,那应该怎么解决呢。

我们只要设置一个计数器就可以啦,计数器在每次遇到不重复时就清0,这样我们就可以确保每次的新数字都是从0开始算,如果计数器达到2时,我们后面如果在扫描到这个数,我们就把这个数删掉

下面的是代码

//消除数组中的重复元素
//重复元素允许出现两次  本题如同26题  只是重复的元素可以出现两次  所以下面设置一个flag标识元素出现的次数
public class LeetCode80 {
	public int removeDuplicates(int[] nums) {
		int flag = 0;
		if (nums.length == 0)
			return 0;
		int i = 0;
		// 两个指针i,j分别重起点开始 j先走如果没有重复 则替换i+1个 (本题容重复个数为2) 设置flag标识重复数字的个数 如果遇到不同的数字则重置
		for (int j = 1; j < nums.length; j++) {
			if (nums[j] != nums[i]) {
				i++;
				nums[i] = nums[j];
				flag = 0;
			} else {
				if (++flag < 2) {
					i++;
					nums[i] = nums[j];
					continue;
				}

			}
		}
		return i + 1;
	}
}

LeetCode27

要求: 删除数组中给定的数,要求不能开辟新的空间,且数组不排序.

思路:我们定义两个指针 l , r 其中 l 指向数组开头 r 指向数组末尾,我们用 i 遍历数组,如果 l 的位置是我们要删除的元素,我们就把 l 和 r 的元素交换,那么我们是不是就把要删除的元素放到最后去了,但是你放到后面完之后就不能在继续做交换了,所以 r 就往前移动一位,但是这时候 l 的位置还不能忘后移动,因为此时交换后的数还需要做比较所以继续比较。依次类推 我们当 l 碰到 r 时就退出循环。

下面是代码

	//使用双指针  把满足的数放到数组的末尾
	public int removeElement(int[] nums, int val) {
		int l = 0, r = nums.length -1;
		while(l <= r) {
			if(nums[l] == val) {
				int temp = nums[l];
				nums[l] = nums[r];
				nums[r] = temp;
				r--;
			}else {
				l++;
			}
		}
		return l;
	}

总结:我们根据上面的题目可以看到,数组类的题目很多是运用双指针技术加上给定的特殊条件进行解决,至于指针的位置(是两个都从头开始走还是一个在头一个在尾进行指针碰撞)的设定还是需要我们进行仔细的思考。如果后续还有这样的题目我会一一补充,感谢阅读,加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值