代码随想录算法训练营第一天| 704. 二分查找,27. 移除元素

题目与题解

参考资料:数组理论基础

704. 二分查找

题目链接:704. 二分查找

代码随想录题解:704. 二分查找

视频讲解:

解题思路:

        二分法属于算法题中的基础,原理很简单,在一个已经排序且无重复元素的序列中查找一个给定值,只需要设置高低两个指针,每次用两个指针中间的值和目标值对比,如果目标值比中间值大,则目标值在中间指针和高指针之间;如果目标值比中间值小,则目标值在低指针和中间指针之间;如果找到目标,则返回中间指针所在。 
        初次解题通过的代码如下:
        
class Solution {
	public int search(int[] nums, int target) {
		int len = nums.length;
		int low = 0, high = len - 1;
		while (low <= high) {
			int mid = (low + high) / 2;
			if (nums[mid] == target) {
				return mid;
			} else if (nums[mid] < target) {
				low = mid + 1;
			} else {
				high = mid - 1;
			}
		}
		return -1;
	}
}

看完代码随想录之后的想法 

        题目的思路非常容易想到,但实现起来确实如随想录解答所说,对区间的定义没有想清楚。不仅是这道题,许多需要考虑边界条件的问题都很容易出错。

Tips:

  • 如果区间是左闭右闭,那么就需要考虑low == high的情况,此时high = mid - 1

       如果区间是左闭右开,low 一定不等于 high,此时high = mid

  •    代码细节
    • 为了防止溢出,最好用low + (high - low)/2 代替 (low + high)/2
    • 除以2可以用右移符号 >> 1代替
    • 为提高算法效率,可以在一开始加上简单的前提条件避免极端情况
// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
        if (target < nums[0] || target > nums[nums.length - 1]) {
            return -1;
        }

遇到的困难

        第一次写的时候,没有考虑区间的问题,用了low < high作为条件,但是high = mid - 1,于是就华丽丽的出错了。

        还有就是很久不从头写代码,在Java中一般都习惯用List方法,突然要用数组类型有点不记得用法,后面题目做多了应该就习惯了。

27. 移除元素

题目链接:27. 移除元素

代码随想录题解:27. 移除元素

视频讲解:

解题思路

        数组相关的题,想要提高效率,要么是空间换时间,要么是时间换空间。

        首先,最容易想到的就是暴力方法,额外申请一个大小为n的新数组,遍历原数组,将符合要求的数塞入新数组,那么算法空间复杂度为n,时间复杂度为n。

        但是题目明确要求,只能原地修改数组,那么就只能用当前数组修改元素,最简单的暴力方法是使用两层for循环,当遍历到的元素等于目标值,则把该元素后面的所有元素往前挪一格,这样时间复杂度就是n2。

        如果想将时间复杂度降到n,则要考虑如何在每次循环判断时,只修改少量元素。由于元素的顺序可以修改,那么,可以设置前后两个指针,将当前等于val的元素与后指针指向的元素交换,并将后指针向前挪一格,直到循环到前后指针相遇。

初次解题结果如下:

class Solution {
    public int removeElement(int[] nums, int val) {
		// 暴力法
		int result = nums.length;
		int i = 0;
		while (i < result) {
			if (val == nums[i]) {
				result--;
				for (int j = i; j < result; j++) {
					nums[j] = nums[j+1];
				}
			} else {
				i++;
			}
		}
 		return result;
    }
}
class Solution {
    public int removeElement(int[] nums, int val) {
		// 双指针法之交换
		int i = 0, j = nums.length - 1;
		int result = nums.length;
		while (i <= j) {
			if (val == nums[i]) {
				int temp = nums[i];
				nums[i] = nums[j];
				nums[j] = temp;
				j--;
			} else {
				i++;
			}
		}
		return i;
    }
}

看完代码随想录之后的想法

        最优的快慢指针方法比我的交换法减少了交换的步骤,非常有巧思,既节约了时间,又节约了空间。但是感觉不容易想到,属于看过就会,不看就忘的方法,根本原因还是自己不太理解。

        我先尝试看了思路以后自己写,但是不是结果出错,就是边界溢出,纯属没想好瞎碰。快慢指针的关键在于理解两个指针的用处:快指针就是用来遍历数组的,而慢指针用来表示更新数组的下标。换个角度想,结合一开始想到的O(n)空间复杂度的暴力算法,慢指针可以理解为一个新数组的下标,只不过不需要开辟额外的空间,而是直接用新数组覆盖了旧的数组。这种方法还有一个好处:更新后的数组除了去除掉的元素外,顺序不变。

        欣赏一下优雅的快慢指针解法:

class Solution {
    public int removeElement(int[] nums, int val) {
        // 快慢指针
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;
    }
}

遇到的困难

        题目难度是不高的,关键还是在于代码实现时要注意细节,什么时候该++,什么时候该赋值,数组下标不要过界,修改了元素定义的范围return的时候也要记得相应修改。

今日收获

        看了一下时间,上一次刷题已经是两年前了,那会儿在尝试跳槽,只不过最后没有跳成功,一方面有代码基础薄弱的原因,另一方面也是自己自信不足,在面试中表现不佳。

        这两年也断断续续刷过一点题,代码随想录只看了一点,leetcode每日也没有坚持几天。打算从今天开始重拾刷题的脚步,希望后面有机会再去市场上估一估自己的价值。

        idea的leetcode插件非常有趣且好用,给解题增添了极大的便利,后面可以考虑上班摸鱼的时候使用:)。

        以前没有写博客的时候不觉得,现在写完都要复盘思路和代码实现,就会发现自己非常的粗心,也缺乏耐心,有一点眼高手低,想的太多,做的太少。希望以后补上自己的短板。

        今天做题花费的时间大约一小时不到,但是设置和熟悉idea+leetcode插件、分析代码随想录的思路以及写这篇博客,前后加起来也得快两个多小时了。写完第一次有了经验,下一次应该可以提高效率。

 

 

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值