力扣初级算法(一)— 数组(1)

初级算法
LeetCode 官方推出的经典面试题目清单 —— 「初级算法 - 帮助入门」
通过力扣的这个卡片 ,入门算法。
下面是个人刷题的记录与总结,这里会记录比较有代表性和比较好的题目

首先来看第一题,删除排序重复值

删除排序数组中的重复项 给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素。

看到这题,比较好想的思路是,定义一个记录指针,直接遍历一遍数组,每次保存上一个遍历的值,当遍历指针发现指向的数字不等于上个保存的值时,把上一个值保存到记录指针的位置。

public static int removeDuplicates(int[] nums) {
        if(nums==null||nums.length==0){
            return 0;
        }
        //count为记录指针
        int tmp=nums[0],count=0;
        for(int i=0;i<nums.length;i++){//i为遍历指针
            //tmp用于记录相同元素
            if(tmp==nums[i]){
                continue;
            }
            //遇到不相同元素 先保存之前的元素
            nums[count]=tmp;
            tmp=nums[i];
            count++;
        }
        nums[count]=tmp;//记得保存最后一个值
        return count+1;
    }

但一种更好的解决办法是双指针,也比较好理解,双指针是很多数组题/字符串题都比较好的解决方法。

    public int removeDuplicates1(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        //保存结果的指针
        int i = 0;
        for (int j = 1; j < nums.length; j++) {
            if (nums[j] != nums[i]) {//j为遍历指针,此时为遍历到不同的元素
                i++;//结果指针右移
                nums[i] = nums[j];
            }
        }
        return i + 1;
    }

再看第二题,买卖股票的最佳时机II(注意,这里是可以尽可能的完成更多交易)
这题其实还有动态规划解,到后面讲到动态规划的时候再讲。
买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 =
5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 示例 2:

输入: [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 =
5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

/**
     * 个人双指针+贪心解决
     * 思路 start代表当天,end代表未来某天
     * start必须满足小于end
     * 如果当天比未来价格高 不买 ,start++ 到下一天
     * 只要碰到未来任何一天比start代表的价格高 直接卖出
     * @param prices
     * @return
     */
    public static int maxProfit(int[] prices) {

        int start=0,end=1,allMoney=0,money;
        for(end=1;end<prices.length;end++){
            //
            if(start>=end){
                continue;
            }
            //当天比未来价格高 左指针右移
            if(prices[start]>=prices[end]){
                start++;
                continue;
            }
            //计算赚的钱
            money=prices[end]-prices[start];
            start++;
            allMoney+=money;
        }
        return allMoney;
    }

第三题是旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右旋转 1 步:
[7,1,2,3,4,5,6] 向右旋转 2 步: [6,7,1,2,3,4,5] 向右旋转 3 步: [5,6,7,1,2,3,4] 示例
2:

输入: [-1,-100,3,99] 和 k = 2 输出: [3,99,-1,-100] 解释: 向右旋转 1 步:
[99,-1,-100,3] 向右旋转 2 步: [3,99,-1,-100]

我的想法是遍历一遍所有数字,计算所有数字的新下标,放在等大小的新数组里,如果下标超出数组限制,则将下标取余。

    public static void rotateThink2(int[] nums, int k) {
        int index;
        int tmp[]= Arrays.copyOf(nums, nums.length);
        for(int i=0;i<nums.length;i++){
            index=i+k;//计算对应元素新位置的下标
            if(index>nums.length-1){
                index=index%(nums.length);
            }
            nums[index]=tmp[i];
        }
    }

但是题解的解法非常让人震撼,我们可以想一下,对于一个数组,向右边移动几个数字,就等于将超出边界的所有数字翻转放在数组首部,那我们换个思路想,先翻转整个数组,再将前k个数组翻转回来, 这时前k个元素就是倒数k个元素移动的新对应位置,最后再把 k到nums.length-1 个元素翻转回去则得到正确的结果(翻转数组只要定义两个指针,指向的位置元素互换则可)

    public static void rotateSlowReverse(int[] nums, int k){
        //当k比nums的长度大时 k的长度取余数
        k %= nums.length;
        //先反转整个数组
        reverse(nums, 0, nums.length-1);
        //反转前k个数字
        reverse(nums, 0, k-1);
        //反转数组后length-k
        reverse(nums, k, nums.length-1);

    }
    public static void reverse(int[] nums,int start,int end){
        int tmp;
        while(start<end){
            tmp=nums[start];
            nums[start]=nums[end];
            nums[end]=tmp;
            start++;end--;
        }
    }

第四题 存在重复元素

给定一个整数数组,判断是否存在重复元素。 如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回
false 。

最容易想到的思路往往是排序,先排序,然后两个指针遍历数组,如果遇到相同值,则直接返回True ,最后返回false.

public static boolean containsDuplicate(int[] nums) {
        //先排序 123345
        Arrays.sort(nums);
        int lastIndex=0;
        for(int i=1;i<nums.length;i++){
            if(nums[i]!=nums[lastIndex]){
                lastIndex++;
            }else{
                return true;
            }
        }
        return false;
    }

当然也可以运用语言自带的API解决,比如我用的java,可以调用集合Set,直接把所有元素添加进集合,如果集合的长度与数组的长度不同,则返回true,否则返回false,这里就不发出代码了。

第五题 只出现一次的数字

只出现一次的数字 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例 1:

输入: [2,2,1] 输出: 1 示例 2:

输入: [4,1,2,1,2] 输出: 4

最容易想到的思路和上题一样,就是先排序(java8默认排序是快排),排序后每两个一组进行遍历,最后如果只剩下最后一个元素,说明最后一个元素是唯一元素。 但快排的复杂度为nlogn 是非线性的。

    public static int singleNumber(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }
        //1 1 2 2 3 3 4 5 5 6 6
        Arrays.sort(nums);
        for (int i = 0; i < nums.length - 1; i += 2) {
            if (nums[i] != nums[i + 1]) {
                return nums[i];
            }
        }
        return nums[nums.length - 1];
    }

第二个思路也和上一题类似,用Set集合解决,将Nums数组里的元素依次加入到Set中,每次加入的时候检查集合里是否存在该元素,如果存在,则移除集合中的元素,最后留在集合里的元素就是所要找的数字,这里就不提供代码了。

然后第三个思路,题解的一个很厉害的思路! 用异或符号来寻找题解,可能会有很多人连异或这个运算符都没有接触过,对于刚刷算法的我也一样,异或是比较二进制位,如果二进制位相同 则为0,不同则为1,举个例子

3^4 转化为二进制

011 ^ 100 = 111 = 7

异或满足结合律,a ^ b ^ c ^ d ^ e= a ^ e ^ b ^ d ^c

相同的数字异或运算后得0,所以直接将数组中所有数字进行异或操作,最后得到的就是唯一的那一个数字(任何数字和0异或都是该数字本身).

    public static int t(int[] nums){

        if(nums.length==1){
            return nums[0];
        }
        int res = 0;
        for(int t:nums){
            res ^= t;
        }

        return res;
    }

这是我第一次发博客,如果大家有什么问题可以在评论下面提出来,从今天开始养成记录下自己所学的内容的好习惯,以后会发布更多学习的笔记 ,下次再见!。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值