day59-day60【代码随想录】二刷数组

文章目录

  • 前言
  • 一、移动零(力扣283)【双指针】
  • 二、轮转数组(力扣189)
  • 三、寻找数组的中心下标(力扣728)
  • 四、和为 K 的子数组(力扣560)
  • 五、按奇偶排序数组 II(力扣922)【双指针】
  • 六、爱吃香蕉的珂珂(力扣875)【二分法】
  • 每日一题:按位与为零的三元组(力扣982)
  • 每日一题:经营摩天轮的最大利润(力扣1599)


前言

1、移动零
2、轮转数组
3、寻找数组的中心下标
4、和为 K 的子数组
5、按奇偶排序数组||
6、爱吃香蕉的珂珂


一、移动零(力扣283)【双指针】

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。
day59-day60【代码随想录】二刷数组_第1张图片

class Solution {
    public void moveZeroes(int[] nums) {
        int slowIndex =0;
        int fastIndex = 0;
        for(;fastIndex<nums.length;fastIndex++){
            if(nums[fastIndex]!=0){
                nums[slowIndex++]=nums[fastIndex];
            }
        }
        //剩下的补零
        for(int i=slowIndex;i<nums.length;i++){
            nums[i] = 0;
        }
    }
}

二、轮转数组(力扣189)

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

最简单的想法 开辟一个新的数组暂时存放元素 空间复杂度为O(n)
还要注意一点:本题还有一个小陷阱,题目输入中,如果k大于nums.length了应该怎么办? ------>取模运算

class Solution {
    public void rotate(int[] nums, int k) {
        int Index =0;
        int length = nums.length;
        int moveTme = k%length;
        int[] temp = new int[moveTme];

        if(nums.length==1) return ;
        for(int i=length-moveTme;i<length;i++){
            temp[Index++] = nums[i];
        }

        int fastIndex = length-1;
        for(int i=length-moveTme-1;i>=0;i--){
            nums[fastIndex--] = nums[i];
        }
        int slowIndex = 0;

        for(int i=0;i<=fastIndex;i++){
            nums[slowIndex++] = temp[i];
        }
    }
}

方法二:翻转数组
第一步:把整个数组进行翻转
第二步:分为两部分:【0:k-1】 【k:nums.length-1】分别一次翻转即可

class Solution {
    public void rotate(int[] nums, int k) {
        //翻转数组
        int moveTime = k%nums.length;
        reverse(nums,0,nums.length-1);
        reverse(nums,0,moveTime-1);
        reverse(nums,moveTime,nums.length-1);
    }
    public void reverse(int[] nums,int start,int end){
        while(start<end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start+=1;
            end-=1;
        }
    }
}

三、寻找数组的中心下标(力扣728)

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。
day59-day60【代码随想录】二刷数组_第2张图片
一开始会想到双指针,但是需要修补很多边边角角,较为繁琐

左求和*2+中心索引值 = 总和

class Solution {
    public int pivotIndex(int[] nums) {
        int sum =0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        int left =0;
        int right=0;
        for(int i=0;i<nums.length;i++){
            sum -=nums[i];
            if(sum==left) return i;
            left += nums[i];
        }
        return -1;
    }
}

四、和为 K 的子数组(力扣560)

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
day59-day60【代码随想录】二刷数组_第3张图片

分析:
第一感觉是滑动窗口,但是在试了很久之后发现并不可行。
为什么这题不可以用双指针/滑动窗口:因为nums[i]可以小于0,也就是说右指针i向后移1位不能保证区间会增大,左指针j向后移1位也不能保证区间和会减小。给定j,i的位置没有二段性,vice versa(反之亦然)。

暴力求解:

public class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
        for (int start = 0; start < nums.length; ++start) {
            int sum = 0;
            for (int end = start; end >= 0; --end) {
                sum += nums[end];
                if (sum == k) {
                    count++;
                }
            }
        }
        return count;
    }
}

前缀和+哈希表
基于方法一利用数据结构进行进一步的优化
day59-day60【代码随想录】二刷数组_第4张图片

五、按奇偶排序数组 II(力扣922)【双指针】

给定一个非负整数数组 nums, nums 中一半整数是 奇数 ,一半整数是 偶数 。

对数组进行排序,以便当 nums[i] 为奇数时,i 也是 奇数 ;当 nums[i] 为偶数时, i 也是 偶数 。

你可以返回 任何满足上述条件的数组作为答案 。
day59-day60【代码随想录】二刷数组_第5张图片

class Solution {
    public int[] sortArrayByParityII(int[] nums) {
        int jishuIndex = nums.length-1;
        int oushuIndex = 0;

        while(jishuIndex>0 && oushuIndex<nums.length-1){
            while(jishuIndex>0 && nums[jishuIndex]%2==1){
                jishuIndex-=2;
            }
            while(oushuIndex<nums.length-1 && nums[oushuIndex]%2==0){
                oushuIndex+=2;
            }
            //交换元素:
            if(jishuIndex>0 && oushuIndex<nums.length-1){
                int temp = nums[oushuIndex];
                nums[oushuIndex] = nums[jishuIndex];
                nums[jishuIndex] = temp;
                oushuIndex+=2;
                jishuIndex-=2;
            }
        }
        return nums;
    }
}

六、爱吃香蕉的珂珂(力扣875)【二分法】

珂珂喜欢吃香蕉。这里有 n 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 h 小时后回来。

珂珂可以决定她吃香蕉的速度 k (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 k 根。如果这堆香蕉少于 k 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 h 小时内吃掉所有香蕉的最小速度 k(k 为整数)。
day59-day60【代码随想录】二刷数组_第6张图片
分析:
珂珂可以一小时吃一根香蕉 也可以一小时吃两根香蕉,最多一小时可以吃香蕉堆的最大值,速度可以一直增加,增加速度太慢,所以使用二分法增加

class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        int maxBanana = 0;
        //先找到二分查找的边界值
        for(int i=0;i<piles.length;i++){
            if(piles[i]>maxBanana){
                maxBanana = piles[i];
            }
        }
        int left = 1;
        int mid = 0;
        int k = maxBanana;
        while(left<maxBanana){
            mid = (left+maxBanana)/2;
            //二分法 把此时的速度传进去,看看需要多长时间 比h大还是小
            long res = checkEat(mid,piles);
            if(res>h){
                //k应该增加;
                left=mid+1;
            }else{
                maxBanana = mid;
                k=mid;
            }
        }
        return k;
    }


    public long checkEat(int speed,int[] piles){
        long time = 0;
        for(int i=0;i<piles.length;i++){
            if(piles[i]<speed){
                time++;
            }else{
                int h = piles[i]/speed;
                if(piles[i]%speed==0){
                    time+=h;
                }else time+=(h+1);
            }
        }
        return time;
    }
}

每日一题:按位与为零的三元组(力扣982)

给你一个整数数组 nums ,返回其中 按位与三元组 的数目。

按位与三元组 是由下标 (i, j, k) 组成的三元组,并满足下述全部条件:

0 <= i < nums.length
0 <= j < nums.length
0 <= k < nums.length
day59-day60【代码随想录】二刷数组_第7张图片

class Solution {
    public int countTriplets(int[] nums) {
        int[] map = new int[1<<16];

        int count = 0;
        //nums[i] 和 nums[j]的结果

        for(int x:nums){
            for(int y:nums){
                ++map[x & y];
            }
        }
        for(int x:nums){
            for(int y=0;y<1<<16;y++){
                if((x&y)==0){
                    count += map[y];
                }
            }
        }
        return count;
    }
}

每日一题:经营摩天轮的最大利润(力扣1599)

正在经营一座摩天轮,该摩天轮共有 4 个座舱 ,每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱,但每次轮转都需要支付一定的运行成本 runningCost 。摩天轮每次轮转都恰好转动 1 / 4 周。

给你一个长度为 n 的数组 customers , customers[i] 是在第 i 次轮转(下标从 0 开始)之前到达的新游客的数量。这也意味着你必须在新游客到来前轮转 i 次。每位游客在登上离地面最近的座舱前都会支付登舱成本 boardingCost ,一旦该座舱再次抵达地面,他们就会离开座舱结束游玩。

你可以随时停下摩天轮,即便是 在服务所有游客之前 。如果你决定停止运营摩天轮,为了保证所有游客安全着陆,将免费进行所有后续轮转 。注意,如果有超过 4 位游客在等摩天轮,那么只有 4 位游客可以登上摩天轮,其余的需要等待 下一次轮转 。

返回最大化利润所需执行的 最小轮转次数 。 如果不存在利润为正的方案,则返回 -1 。

day59-day60【代码随想录】二刷数组_第8张图片

题目太费解。重新描述一下,为"最佳跑路时机"。 迪士尼把它的摩天轮游乐场卖给你了,约好明天付款,今天你先试运行一下。但你其实没有那么多钱,今天必须跑路。 摩天轮不能停,从早上开门就必须一直按固定的速率旋转。 电力公司不信任你,每旋转一格,电力公司就实时从你的账中划走一笔电费runningCost。 你有未卜先知的能力,知道每个时刻,达到的顾客数目有多少。而且这些顾客都是死忠,坐不上摩天轮就不走。 每个顾客在坐上去的同时,扫码转账给你boardingCost。 问:你什么时候拉闸跑路,钱最多?天上的顾客等救援机构来了再说。。。

class Solution {
    public int minOperationsMaxProfit(int[] customers, int boardingCost, int runningCost) {
        int wait =0;
        int person=0;
        int maxProfit = 0;
        int res = -1;
        int profit =0;
        //首先肯定需要遍历顾客数量
        for(int i=0;i<customers.length || person>0;i++){
            if(i<customers.length){
                person += customers[i];
            }
            profit += Math.min(person,4)* boardingCost-runningCost;
            person-=Math.min(person,4);
            if(profit>maxProfit){
                maxProfit = profit;
                res = i+1;
            }
        }
        return res;
    }
}

你可能感兴趣的:(代码随想录,算法,leetcode,数据结构,java,面试)