力扣初级算法-02-数组-旋转数组

学习目标:

本次学习目标为 力扣初级算法-数组,其中主要的LC如下:

  • 旋转数组
  • 存在重复元素

学习内容:

  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]

解题思路:

  • 解法一: 临时数组

    • 边界问题:数组为null 和数组大小为0的情况,直接return;
    • 代码逻辑过程:
      • 开辟一个新数组,将老数组的值复制到新数组中
      • 是否存在边界问题:否
      • 遍历老数组,从index == 0 开始循环,其中以新数组为参照,将 index + 偏移量的值重写到老数组中,完成替换。
      • 代码实现:
      public class Rotate {
      
      	// 解法一: 使用临时数组
      	public void rotate(int[] nums, int k) {
      		int length = nums.length;
      		int[] temp = new int[length];
      
      		// 备份原数组
      		System.arraycopy(nums, 0, temp, 0, length);
      
      		// 转移偏移后的数据到原数组 --  假设长度为 6 移动 3, 0+3/6 = 3
      		for (int i =0; i< length; i++){
      			int offsetIndex = (i + k ) % length;
      			nums[offsetIndex] = temp[i];
      		}
      
      	}
      }
      
  • 解法二:多次反转

    • 首先将数组内的所有元素反转,再反转前K个,最后反转剩下的几个
    • 是否存在边界问题:否
    • 注意点
      • 二次反转可保证原先的排序
    • 代码实现
    	/**
    	 *  解法二 多次反转
    	 */
    	public void rotate02(int[] nums, int k) {
    		int length = nums.length;
    		 k %= length;
    		//先反转前面的
    		reverse(nums, 0, length - k - 1);
    		//接着反转后面k个
    		reverse(nums, length - k, length - 1);
    		//最后在反转全部的元素
    		reverse(nums, 0, length - 1);
    
    	}
    
    	/**
    	 * 反转数组中的元素
    	 */
    	public void reverse(int[] nums, int start, int end){
    		if (nums.length != 0 ){
    			int temp;
    			while (start < end){
    				temp = nums[start];
    				nums[start++] = nums[end];
    				nums[end--] = temp;
    			}
    		}
    	}
    
  • 解法三:环形旋转

    • 解法类似约瑟夫环,把数组当作是环形,每一个都往后移动K位
    • 是否存在边界问题:是,考虑数组大小为偏移量整数倍的情况,即 nums.length%k=0,针对边界问题,为防止循环打转,可新增一个数组用来标识当前元素有没有被访问过,当已访问过时,则从下个元素继续开始。
    • 代码逻辑过程:
      • 声明单个临时变量 hold,用来存放当前取出的元素。
      • 声明与入参同大小的数组 existArray ,用来存放当前偏移量后的坐标是否已被操作过。
      • 循环遍历入参数组
      • 将当前坐标加上偏移量除于数组大小,得出的结果为 偏移后的坐标(此处应该注意偏移量整数倍的情况)— index = (index + k) % length;
      • 判断上述的偏移后的坐标 index 是否已存在于 existArray 。
        • 当 index 不存在于 existArray 时,先将当前 index 标记为已被操作 (existArray [index] = true),其次将即将被移动的 index 原有元素放入 temp 中,再将 hold 写入偏移后的 index 位,最后再将被替换的元素放入 hold中。
        • 当 index 存在于 existArray,将 index = index + 1, 先跳出当前的循环操作,其次计算当前 index 后的偏移量,再将下次需要便宜的元素放入 hold 中,同时减掉全量的循环次数,避免重复循环。
      • 判断完再继续for循环。
    • 代码实现
	/**
	 *  解法三 多次反转
	 */
	public void rotate03(int[] nums, int k) {
		int hold =nums[0];
		int index = 0;
		int length = nums.length;
		boolean[] existArray = new boolean[length];
		for (int i = 0; i < length; i++) {
			index = (index + k) % length;
			if (existArray[index]) {
				//避免现原地打转
				index = (index + 1) % length;
				hold = nums[index];
				i--;
			} else {
				existArray[index] = true;
				int temp = nums[index];
				nums[index] = hold;
				hold = temp;
			}
		}
	}

学习笔记:

  1. 什么是 约瑟夫环?
    约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。

学习总结:

  1. 多加边界问题
  2. 循环数组应该考虑重复操作问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值