算法入门第2天 - 双指针(1个简单+1个中等)

1. leetcode977. 有序数组的平方

1.1 题目描述

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

示例 3:

输入:nums = [-1]
输出:[1]

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 已按 非递减顺序 排序

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1.2 代码实现

1、我的提交
思路:首先找到负数和非负数的边界,指针 j 指向负数,指针 i 指向正数,每次比较 j 和 i 对应 数字平方后大小,选择小的放入结果数组,指针 j 和 i 分别向左向右挪动。由于两指针是从中间向两边走,因此最后需要判断两指针是否都走到数组边界,保证所有数字都被遍历到。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] res = new int[nums.length];
        int i = 0;
        while(i < nums.length && nums[i] < 0){
            i++;
        }
        int j = i == 0 ? 0 : (i-1);
        int t = 0;
        while(j >= 0 && i < nums.length){
            int a = (int)Math.pow(nums[j], 2);
            int b = (int)Math.pow(nums[i], 2);
            if(a > b){
                res[t++] = b;
                i++;
            }else if(a < b){
                res[t++] = a;
                j--;
            }else{
                // a == b
                if(i != j){
             		// i != j 时需要放入两个a
                    res[t++] = a;
                }
                res[t++] = a;
                i++;
                j--;                                                                        
            }
        }
        while(j >= 0){
            res[t++] = (int)Math.pow(nums[j--], 2);
        }
        while(i < nums.length){
            res[t++] = (int)Math.pow(nums[i++], 2);
        }
        return res;
    }
}

时间: O(n)。

2、推荐解法 - 参考官答
思路:指针 i 和 j 分别指向数组的首尾,每次比较两指针对应数字的平方,选择较大的逆序放入结果数组。由于两指针从首尾向中间走,因此该方法不需要判断指针是否走到数组边界的问题。

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] res = new int[nums.length];
        int i = 0, j = nums.length - 1;
        int t = j;
        while(i <= j){
            int a = nums[i] * nums[i];
            int b = nums[j] * nums[j];
            if(a > b){
                res[t--] = a;
                i++;
            }else{
                res[t--] = b;
                j--;
            }
        }
        return res;
    }
}

时间:O(n)。

2. leetcode189. 旋转数组

2.1 题目描述

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

进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

示例 1:

输入: nums = [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:

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

示例3:

输入:nums[-1], k = 2
输出:[-1]

提示:

1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.2 代码实现

1、我的提交
思路:先对前 n-k 和 k 个元素分别进行翻转,然后再对整个数组进行翻转。

class Solution {
    public void rotate(int[] nums, int k) {
    	//注意k的取值
        if(k > nums.length){
            k %= nums.length;
        }
        if(k == 0){
            return;
        }
        swap(nums, 0, nums.length-k-1);
        swap(nums, nums.length-k, nums.length-1);
        swap(nums, 0, nums.length-1);
    }

    public void swap(int[] nums, int start, int end){
        while(start < end){
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}

时间:O(n).

2、其他思路
(1) 额外数组:开辟额外数组存放旋转后结果,位置对应关系为 i -> (i+k) mod n;
(2) 环状替换:为了省去额外数组的开销,可以使用临时变量存储目前被替换的元素,变量temp存储 nums[0],nums[0]应该放到 k 位置,temp存储nums[k],而nums[k] 应该放到 (k+k) mod n的位置上,依次类推,直至再到nums[0],一轮替换完成;因为一轮可能没有遍历到所有数字,因此从下个数字开始重复上述过程;用count记录被替换的元素个数控制替换过程的停止。

其他收获:求两个数字的最大公约数:

public int gcd(int x, int y) {
        return y > 0 ? gcd(y, x % y) : x;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值