Leetcode刷题记录面试基础题day1(备战秋招)

hello,你好鸭,我是Ethan,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行!💪💪💪
目前博客主要更新Java系列、数据库、项目案例、计算机基础等知识点。感谢你的阅读和关注,在记录的同时希望我的博客能帮助到更多人。🤝🤝🤝
人生之败,非傲即惰,二者必居其一,勤则百弊皆除。你所坚持最终会回报你!加油呀!✔️💪🏃

刷题

1、合并两个有序数组

解题经验1
在这里插入图片描述

自己解题:

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        nums1.length=m+n;
        nums2.length=n;
     //扩容,将nums2追加到num1
     for(int i=m;i<nums1.length;i++)
        nums1[m]=nums2[0];
        sort(nums1);
    }

    // 先做一个统一的排序函数

    public void sort(int [] array){
        int k=array.length;
        int flag=0;
        for(int i=0;i<k-1;i--){
            for (int j=0;j<k-i-1;j++){
                flag=1;
                if(array[j]>array[j+1])
                {
                    swap(array[j],array[j+1]);
                }
            }
            if (!flag)
            {
                break;
            }          
        }

    }
    // 封装一个调转位置的函数

    public void swap(int [] array,int i,int j)
    {
        int temp=array[i];
        array[i]=array[j];
        array[j]=temp;
    }
}
//先理清思路,做出条例后再动手编码 

官方解答:

(1)方法一:直接合并后排序

在这里插入图片描述

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        for (int i = 0; i != n; ++i) {
            nums1[m + i] = nums2[i];
        }
        Arrays.sort(nums1);
    }
}

在这里插入图片描述

(2)方法二:双指针

在这里插入图片描述

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = 0, p2 = 0;
        int[] sorted = new int[m + n];
        
 //利用cur指明将排序得到的数
        int cur;
        while (p1 < m || p2 < n) {  //二者有一个结束了就结束了
 //先将m或n=0的情况排除,接下来再去比较二者的大小,赋值给cur
            if (p1 == m) {
                cur = nums2[p2++];
            } else if (p2 == n) {
                cur = nums1[p1++];
            } else if (nums1[p1] < nums2[p2]) {
                cur = nums1[p1++];
            } else {
                cur = nums2[p2++];
            }
            
   //这里再将得到的cur赋值给sorted数组
            sorted[p1 + p2 - 1] = cur;
        }

        for (int i = 0; i != m + n; ++i) {
            nums1[i] = sorted[i];
        }
    }
}

在这里插入图片描述

(3)* 方法三:逆向双指针

在这里插入图片描述

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m - 1, p2 = n - 1;
        int tail = m + n - 1;
        int cur;
        while (p1 >= 0 || p2 >= 0) {
            if (p1 == -1) {
                cur = nums2[p2--];
            } else if (p2 == -1) {
                cur = nums1[p1--];
            } else if (nums1[p1] > nums2[p2]) {
                cur = nums1[p1--];
            } else {
                cur = nums2[p2--];
            }
            nums1[tail--] = cur;
        }
    }
}

2、移除元素

在这里插入图片描述

自己解答:

解题经验
在这里插入图片描述

官方解答:

(1)方法一:双指针(一前一后,两者同时动)

在这里插入图片描述
在这里插入图片描述

(2)方法二:优化双指针(一头一尾,头动尾静)

在这里插入图片描述
在这里插入图片描述

3、删除有序数组中的重复项

在这里插入图片描述

(1)自己解答:

在这里插入图片描述

在这里插入图片描述

(2)官方题解:

在这里插入图片描述

4、删除有序数组中的重复项 II

在这里插入图片描述
注意挖掘隐藏信息与通解:

官方题解

在这里插入图片描述

5、多数元素

在这里插入图片描述

自己解答:

在这里插入图片描述

官方解答:

在这里插入图片描述

方法一:哈希表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法二:排序

在这里插入图片描述
在这里插入图片描述

方法三:随机化

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

方法五:Boyer-Moore 投票算法(同归于尽消杀法)

在这里插入图片描述
在这里插入图片描述
将众数记为1,非众数记为-1.最后的一定是众数。
在这里插入图片描述

6、轮转元素

在这里插入图片描述

自己解答:

在这里插入图片描述

官方解答:

方法一:使用额外的数组

在这里插入图片描述
在这里插入图片描述

这里%的用处是使得长度一定在i+k之间,超出就循环至开头!!!!!!

方法二:环状替换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2024年4月14日16:41:53

7.买卖股票的最好时机(动态规划I)

在这里插入图片描述

自己的解法:

在这里插入图片描述
提示:超出时间限制

官方解法:

动态规划——

//1.一次遍历就OK。
// 设定一个最小值。遍历所有的点,进行两种操作:
// 1.如果小于最小值就将其赋给最小值。
// 2.如果大于最小值则求后面的价格与最小值的差值。这样遍历完全之时,既完全考虑了又得到了最大差值。
//注意:——这种题目一定要避免循环嵌套

在这里插入图片描述

8.买卖股票的最好时机(动态规划II)

方法一:状态转移方程

根据上一题的思路,需要设置一个二维矩阵表示状态。

第 1 步:定义状态
状态 dp[i][j] 定义如下:
dp[i][j] 表示到下标为 i 的这一天,持股状态为 j 时,我们手上拥有的最大现金数
注意:限定持股状态为 j 是为了方便推导状态转移方程,这样的做法满足 无后效性
其中:
第一维 i 表示下标为 i 的那一天( 具有前缀性质,即考虑了之前天数的交易 );
第二维 j 表示下标为 i 的那一天是持有股票,还是持有现金。这里 0 表示持有现金(cash),1 表示持有股票(stock)。

第 2 步:思考状态转移方程
状态从持有现金(cash)开始,到最后一天我们关心的状态依然是持有现金(cash);
每一天状态可以转移,也可以不动。状态转移用下图表示: 在这里插入图片描述
说明:

由于不限制交易次数,除了最后一天,每一天的状态可能不变化,也可能转移;
写代码的时候,可以不用对最后一天单独处理,输出最后一天,状态为 0 的时候的值即可。

第 3 步:确定初始值

起始的时候:
如果什么都不做,dp[0][0] = 0
如果持有股票,当前拥有的现金数是当天股价的相反数,即 dp[0][1] = -prices[i]

第 4 步:确定输出值
终止的时候,上面也分析了,输出 dp[len - 1][0],因为一定有 dp[len - 1][0] > dp[len - 1][1]。

public class Solution {

    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }

        // 0:持有现金
        // 1:持有股票
        // 状态转移:0 → 1 → 0 → 1 → 0 → 1 → 0
        int[][] dp = new int[len][2];

        dp[0][0] = 0;
        dp[0][1] = -prices[0];

        for (int i = 1; i < len; i++) {
            // 这两行调换顺序也是可以的
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[len - 1][0];
    }
}
 
方法一:贪心算法
贪心算法的直觉:由于不限制交易次数,只要今天股价比昨天高,就交易。

下面对这个算法进行几点说明:

该算法仅可以用于计算,但 计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明等价性:以 [1, 2, 3, 4] 为例,这 4 天的股价依次上升,按照贪心算法,得到的最大利润是:

res =  (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])
    =  prices[3] - prices[0]

仔细观察上面的式子,按照贪心算法,在下标为 1、2、3 的这三天,我们做的操作应该是买进昨天的,卖出今天的,虽然这种操作题目并不允许,但是它等价于:在下标为 0 的那一天买入,在下标为 3 的那一天卖出。

为什么叫「贪心算法」
回到贪心算法的定义:(下面是来自《算法导论(第三版)》第 16 章的叙述)

在这里插入图片描述

贪心算法 在每一步总是做出在当前看来最好的选择。

「贪心算法」 和 「动态规划」、「回溯搜索」 算法一样,完成一件事情,是 分步决策 的;
「贪心算法」 在每一步总是做出在当前看来最好的选择,我是这样理解 「最好」 这两个字的意思:
「最好」 的意思往往根据题目而来,可能是 「最小」,也可能是 「最大」;
贪心算法和动态规划相比,它既不看前面(也就是说它不需要从前面的状态转移过来),也不看后面(无后效性,后面的选择不会对前面的选择有影响),因此贪心算法时间复杂度一般是线性的,空间复杂度是常数级别的;
这道题 「贪心」 的地方在于,对于 「今天的股价 - 昨天的股价」,得到的结果有 3 种可能:① 正数,② 000,③负数。
本题贪心算法的决策是: 只加正数

public class Solution {

    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len < 2) {
            return 0;
        }

        int res = 0;
        for (int i = 1; i < len; i++) {
            int diff = prices[i] - prices[i - 1];
            if (diff > 0) {
                res += diff;
            }
        }
        return res;
    }
}

 

2024年4月28日19:52:57

9.跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
在这里插入图片描述

自己的题解:双重嵌套循环

像这种双重嵌套循环式最差的,一定还有更优解

在这里插入图片描述

官方题解:贪心算法

在这里插入图片描述
在这里插入图片描述

public class Solution {
    public boolean canJump(int[] nums) {
        int n = nums.length;
        int rightmost = 0;
        for (int i = 0; i < n; ++i) {
        
            if (i <= rightmost) {//最大距离的条件
                rightmost = Math.max(rightmost, i + nums[i]);
                if (rightmost >= n - 1) {
                    return true;
                }
            }
        }
        return false;
    }
}
民间高手

class Solution {
    public boolean canJump(int[] nums) {
        int farthermost = 0;
        for (int i = 0; i <= farthermost && i < nums.length; i++) {
            farthermost = Math.max(farthermost, i + nums[i]);
        }
        return farthermost >= nums.length - 1;
    }
}

主要把思路理清了就好说,注意一点就是能到达的最大距离是有条件的,并不是每一个数组元素都能到达

10 .跳跃游戏 II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

在这里插入图片描述

自己根本不会做

官方题解:
解题思路

这道题是典型的贪心算法,通过局部最优解得到全局最优解。以下两种方法都是使用贪心算法实现,只是贪心的策略不同。
方法二:正向查找可到达的最大位置
方法一虽然直观,但是时间复杂度比较高,有没有办法降低时间复杂度呢?

如果我们「贪心」地进行正向查找,每次找到可到达的最远位置,就可以在线性时间内得到最少的跳跃次数。

例如,对于数组 [2,3,1,2,4,2,3],初始位置是下标 0,从下标 0 出发,最远可到达下标 2。下标 0 可到达的位置中,下标 1 的值是 3,从下标 1 出发可以达到更远的位置,因此第一步到达下标 1。

从下标 1 出发,最远可到达下标 4。下标 1 可到达的位置中,下标 4 的值是 4 ,从下标 4 出发可以达到更远的位置,因此第二步到达下标 4。

在这里插入图片描述
在具体的实现中,我们维护当前能够到达的最大下标位置,记为边界。我们从左到右遍历数组,到达边界时,更新边界并将跳跃次数增加 1。

在遍历数组时,我们不访问最后一个元素,这是因为在访问最后一个元素之前,我们的边界一定大于等于最后一个位置,否则就无法跳到最后一个位置了。如果访问最后一个元素,在边界正好为最后一个位置的情况下,我们会增加一次「不必要的跳跃次数」,因此我们不必访问最后一个元素。

class Solution {
    public int jump(int[] nums) {
        int length = nums.length;
        int end = 0;//记载边界位置
        int maxPosition = 0; 
        int steps = 0;
        for (int i = 0; i < length - 1; i++) {//遍历每一个元素
            maxPosition = Math.max(maxPosition, i + nums[i]); //更新能够到达的最大距离
            if (i == end) {
            //如果到达了边界则将边界更新为最大距离,这时候也该进行下一步跳跃了。就这样遍历完每一步跳跃之后就可以求出最小跳跃次数了
                end = maxPosition;
                steps++;
            }
        }
        return steps;
    }
}

2024年4月29日20:14:04

11.H指数

在这里插入图片描述

方法一:排序

在这里插入图片描述

class Solution {
    public int hIndex(int[] citations) {
   
    //java官方提供的库Arrays
        Arrays.sort(citations);
        int h = 0, i = citations.length - 1; 
        while (i >= 0 && citations[i] > h) {
            h++; 
            i--;
        }
        return h;
    }
}

方法二:计数排序

在这里插入图片描述

public class Solution {
    public int hIndex(int[] citations) {
        int n = citations.length, tot = 0;
        int[] counter = new int[n + 1];
        for (int i = 0; i < n; i++) {
            if (citations[i] >= n) {
                counter[n]++;
            } else {
                counter[citations[i]]++;
            }
        }
        
        for (int i = n; i >= 0; i--) {
        //确保了最大值的H
            tot += counter[i];
            if (tot >= i) {
                return i;
            }
        }
        return 0;
    }
}
方法三:二分搜索(看了还是不会)

在这里插入图片描述

class Solution {
    public int hIndex(int[] citations) {
        int left=0,right=citations.length;
        int mid=0,cnt=0;
        while(left<right){
            // +1 防止死循环
            mid=(left+right+1)>>1;
            cnt=0;
            for(int i=0;i<citations.length;i++){
                if(citations[i]>=mid){
                    cnt++;
                }
            }
            if(cnt>=mid){
                // 要找的答案在 [mid,right] 区间内
                left=mid;
            }else{
                // 要找的答案在 [0,mid) 区间内
                right=mid-1;
            }
        }
        return left;
    }
}

刷题总结

1、先写思路条例,再编码。

2、审题不够严谨。

3、注意极端情况。比如全部相等,长度为0

📣非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤ 分享👥 留言💬thanks!!!
📚愿大家都能学有所得,功不唐捐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值