刷题笔记

4.24

剑指 Offer 03. 数组中重复的数字

题目

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

解1:哈希表

import java.util.HashSet;
class Solution {
    public int findRepeatNumber(int[] nums) {
        Set<Integer> dic = new HashSet<>();
        for(int num : nums) {
            if(dic.contains(num)) return num;
            dic.add(num);
        }
        return -1;
    }
}

解2:原地交换

注意到,题目说明尚未被充分使用,即 在一个长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内
此说明含义:数组元素的 索引一对多 的关系。 因此,可遍历数组并通过交换操作,使元素的 索引 与 值
一一对应(即 nums[i] = inums[i]=i )。因而,就能通过索引映射对应的值,起到与字典等价的作用

作者:jyd
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/solution/mian-shi-ti-03-shu-zu-zhong-zhong-fu-de-shu-zi-yua/

class Solution {
    public int findRepeatNumber(int[] nums) {
        for(int i = 0;i<=nums.length;i++){
            if(nums[i] == i){
                continue;
            }
            if(nums[nums[i]]==nums[i]){
                return nums[i];
            }
            else{
                int tmp = nums[nums[i]];
                nums[nums[i]] = nums[i];
                nums[i] = tmp;
            }
        }
        return -1;
    }
}

4.23

啊漫长痛苦的期中周总算结束了,我应该有时间睡觉也有时间刷题了XD(虽然刷题也好痛苦…),接下来要备战期末了,但我开始得早就总有时间富余吧…希望接下来一个月能刷够100题……消失期间也在笔面试上做了几道题,回头整理下(靠笔面试刷题也是没谁了)~

LeetCode7. 整数反转

题目

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。
如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。
假设环境不允许存储 64 位整数(有符号或无符号)

示例 1:
输入:x = 123
输出:321
示例 2:
输入:x = -123
输出:-321

解:取模

class Solution {
    public int reverse(int x) {
        int rst = 0;
        while(x != 0){
            int tmp = x % 10;
            if(rst > Integer.MAX_VALUE/10 || rst< Integer.MIN_VALUE/10){
                return 0;
            }
            rst = rst * 10 + tmp;
            x/=10;
        }
        return rst;
    }
}

comment
哇我竟然一开始就想出最佳解法了可喜可贺!只是不知道怎么判断0的条件然后看了看题解。

//针对本题的精简判断:
if ((ans * 10) / 10 != ans) {
			ans = 0;
			break;
		}
//针对类似题型的通用判断:
if(rev > Integer.MAX_VALUE / 10 || (rev == Integer.MAX_VALUE / 10 && pop > Integer.MAX_VALUE % 10)){
	return 0;
}else if(rev < Integer.MIN_VALUE / 10 || (rev == Integer.MIN_VALUE / 10 && x < Integer.MIN_VALUE % 10)){
	return 0;

4.5

LeetCode69. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

解1 exp ln 拆解O(1)

官方题解官方题解

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }
        int ans = exp(0.5 * log(x));
        return ((long long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans);
    }
};

解2 二分法 O(logx)

题解区思路:
作者:liweiwei1419链接:https://leetcode-cn.com/problems/sqrtx/solution/er-fen-cha-zhao-niu-dun-fa-python-dai-ma-by-liweiw/

class Solution {
    public int mySqrt(int x) {
         // 特殊值判断
        if(x==0){
            return 0 ;
        }
        if(x==1){
            return 1;
        }
        int left = 1, right = x/2;
        while(left<right){//退出时left == right 返回任一即可 
            int mid = left + (right - left + 1)/2; //+1是防止mid向下取整丢失
            //防止乘法整型溢出
            if(mid > x/mid){
            // [left..mid - 1] 只要mid^2>x,结果一定比mid小
                right = mid - 1;
            }
            else{
            // [mid..right]
                left = mid;
            }
        }
        return left;
    }
}

comment
二分查找的典型应用场景:查找一个有确定范围的整数,根据 单调性 逐渐缩小搜索范围。
注意left right mid边界、特殊情况讨论、避免溢出。

3变形 精确到几位小数 待更新

听说很多面试会考这道题的改版。贴个题解区代码。

public class Solution {
    private static double epsilon = 1e-15;
    public static void main(String[] args)throws IOException{
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String numStr = bf.readLine().trim();
        int x = Integer.parseInt(numStr);
        double ans = mySqrt(x,epsilon);
        System.out.printf(String.format("%.3f",ans));
    }

    public static double mySqrt(double x , double epsilon){
        double left = 0 , right = x;
        if(x == 0 || x == 1){
            return x;
        }
        while(left < right){
            double mid = left + (right - left) / 2;
            if(Math.abs(mid * mid - x) < epsilon){//重点在这,确保mid^2和目标的差值小于精确度
                return mid;
            }else if(mid * mid < x){
                left = mid;
            }else{
                right = mid;
            }
        }
        return left;
    }

}

作者:xrui-m
链接:https://leetcode-cn.com/problems/sqrtx/solution/java-er-fen-fa-qiu-fu-dian-shu-de-ping-f-k9s5/
来源:力扣(LeetCode)

4.3

206. 反转链表

反转一个单链表。示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

普通循环

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre=null, curr=head, tmp;//分别为第一二个节点
        while(curr!=null){
            tmp=curr.next;//保存第三个节点
            curr.next=pre;//反转一二节点
            pre=curr;//分别向前移动
            curr=tmp;
        }
        return pre;//注意提交的是pre,curr已经为null
    }
}

递归版

class Solution {
    //递归版 仅把原循环改为递归
    //注意要把定义从函数中拿出来(整个函数会循环执行)
    ListNode pre=null,tmp;//分别为第一二个节点
    public ListNode reverseList(ListNode head) {
        //head即为原来的curr
        //原循环终止条件即为递归出口
        if(head==null){
            return pre;
        }
        //以下内容不变
        tmp=head.next;//保存第三个节点
        head.next=pre;//反转一二节点
        pre=head;//分别向前移动
        head=tmp;
        return reverseList(head);
    }
}

还有一种递归版但是自己很难想到,贴题解区解法,原链接的幻灯片逐步演示非常形象。

class Solution {
	public ListNode reverseList(ListNode head) {
		//递归终止条件是当前为空,或者下一个节点为空
		if(head==null || head.next==null) {
			return head;
		}
		//这里的cur就是最后一个节点
		ListNode cur = reverseList(head.next);
		//这里请配合动画演示理解
		//如果链表是 1->2->3->4->5,那么此时的cur就是5
		//而head是4,head的下一个是5,下下一个是空
		//所以head.next.next 就是5->4
		head.next.next = head;
		//防止链表循环,需要将head.next设置为空
		head.next = null;
		//每层递归函数都返回cur,也就是最后一个节点
		return cur;
	}
}

作者:wang_ni_ma
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/dong-hua-yan-shi-206-fan-zhuan-lian-biao-by-user74/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.1

拖延晚期患者的行动自救,开始刷算法题

LeetCode53. 最大子序和

题目

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 5:
输入:nums = [-100000]
输出:-100000

解1 暴力求解

O(n^2),可略过

class Solution {
    public int maxSubArray(int[] nums) {
        int sum = -100000 ,tmp = -100000;
        for(int i = 0; i < nums.length; i++){
            tmp=nums[i];
            if(tmp>sum){
                    sum=tmp;
                }
            for(int j = i+1; j<nums.length; j++){
                tmp += nums[j];
                if(tmp>sum){
                    sum=tmp;
                }
            }
        }
        return sum;
    }
}

解2 O(n) 累计思想 (贪心算法)

在所有 Java 提交中击败了100% 的用户,内存消耗:38.4 MB。

来自评论区思路。

  • 从头开始遍历计算数组的和,一旦和小于0便抛弃。从下一个位置开始计数,重新计算和。
  • 有个麻烦是要是全部为负数这个计算规则就失效了,于是记录下这个数组中的最大值,遍历完后如果最大值为负数,则证明整个数组都是负数,把return最大值就行
class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0 , tmp = 0, max=nums[0];
        for(int i = 0; i < nums.length; i++){
            tmp+=nums[i];
            if(max<nums[i]){
                max=nums[i]; //找数组中最大值max
            }
            if(tmp>sum){
                sum=tmp;  //存最大sum值
            }
            if(tmp<0){
                tmp=0;//*关键步骤* 若累加值小于0则抛去,从当前位置开始重新计算
            }
        }
        if(max<0){
            sum=max;//若数组全部为负,返回最大的那个负数
        }
        return sum;
    }
}

comment

一开始想不清楚为什么一遍就可以囊括所有情况,后来想到这是一种积分思想,即求积分函数的极值点(这种说法可能不够严谨),那么的确遍历一次即可,只要tmp达到新高度就记录下来。

同时要注意需分为两种情况讨论,一种是数组值全为负,一种是不全为负。在不全为负的情况下,只要能为sum增值便可以收入囊中。

题解区的贪心算法,感觉和上一种差不多,只是代码精简了,但是运行速度稍稍下降。

import java.lang.*;
class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0 ,  ans=nums[0];
        for(int num: nums){
            if(sum>0){
                sum += num;
            }
            else{//如果sum为负就舍去,从当前值开始
                sum = num;
            }
            ans = Math.max(ans,sum); //也就保证了加入全为时能存下最大值
        }
        return ans;
    }
}

解3 分治 待更新

解4 动态规划 待更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值