leetcode 算法之 轮转数组

0 阅读4分钟

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

 

示例 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]

 

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

解题思路

题目都意思是我们要转动的我们的数组 就是里面的元素从左到右移动 循环往复 最后一个元素移动到第一个其他的依次移动一个下标 还要根据我们的参数移动几次 去处理转几次

  • 第一种办法暴力枚举

public void test(int[] nums, int k) {
    // 添加边界检查
    if (nums == null || nums.length <= 1 || k <= 0) {
        return;
    }

    // 处理k大于数组长度的情况
    k = k % nums.length;
    if (k == 0) {
        return;
    }

    // 通过k次循环,每次将数组整体向右移动一位
    for (int i = 0; i < k; i++) {
        // 保存最后一个元素
        int lastElement = nums[nums.length - 1];
        // 从后往前移动元素(避免覆盖问题)
        for (int j = nums.length - 1; j > 0; j--) {
            nums[j] = nums[j - 1];
        }
        // 将最后一个元素放到第一位
        nums[0] = lastElement;
    }
}

这里的思路我们通过for 循环交换我们相邻的元素的位置 j和 j-1的位置还有我们外层有一个循环就是我们的k是多少我们就循环转几次 这种写法看着代码比较容易理解 但是我们的时间复杂度很高是o(n平方) 所以这种办法不是最优解 我们还要注意的就是我们最后一个元素和第一个元素 交换位置我们要注意数组越界的问题

单元测试

public static void main(String[] args) {
   Leetcode6 leetcode6 = new Leetcode6();

   // 测试用例1:正常数组旋转
   System.out.println("=== 测试用例1:正常数组旋转 ===");
   int[] nums1 = {1, 2, 3, 4, 5, 6, 7};
   int k1 = 3;
   System.out.print("原数组: ");
   printArray(nums1);
   leetcode6.test(nums1, k1);
   System.out.print("旋转后: ");
   printArray(nums1);
   System.out.println("期望结果: [5, 6, 7, 1, 2, 3, 4]");
   System.out.println();

   // 测试用例2:边界情况测试
   System.out.println("=== 测试用例2:边界情况测试 ===");

   // 空数组测试
   System.out.println("空数组测试:");
   int[] nullArray = null;
   System.out.println("执行前: null");
   leetcode6.test(nullArray, 3);
   System.out.println("执行后: " + (nullArray == null ? "null" : "not null"));
   System.out.println();

   // 单元素数组测试
   System.out.println("单元素数组测试:");
   int[] singleElement = {1};
   System.out.print("执行前: ");
   printArray(singleElement);
   leetcode6.test(singleElement, 5);
   System.out.print("执行后: ");
   printArray(singleElement);
   System.out.println();

   // k为0测试
   System.out.println("k为0测试:");
   int[] zeroKArray = {1, 2, 3};
   System.out.print("执行前: ");
   printArray(zeroKArray);
   leetcode6.test(zeroKArray, 0);
   System.out.print("执行后: ");
   printArray(zeroKArray);
   System.out.println();

   // 测试用例3:k大于数组长度
   System.out.println("=== 测试用例3:k大于数组长度 ===");
   int[] nums3 = {1, 2, 3, 4, 5};
   int k3 = 7; // 7 % 5 = 2
   System.out.print("原数组: ");
   printArray(nums3);
   leetcode6.test(nums3, k3);
   System.out.print("旋转后: ");
   printArray(nums3);
   System.out.println("期望结果: [4, 5, 1, 2, 3]");
}

// 辅助方法:打印数组
private static void printArray(int[] arr) {
   if (arr == null) {
       System.out.println("null");
       return;
   }
   System.out.print("[");
   for (int i = 0; i < arr.length; i++) {
       System.out.print(arr[i]);
       if (i < arr.length - 1) {
           System.out.print(", ");
       }
   }
   System.out.println("]");
}

image.png

第二种解题方法

public void test2(int[] nums, int k){
    // 添加边界检查
    if (nums == null || nums.length <= 1 || k <= 0) {
        return;
    }
    // 处理k大于数组长度的情况
    k = k % nums.length;
    if (k == 0) {
        return;
    }
    // 使用三次反转法实现数组旋转,时间复杂度O(n),空间复杂度O(1)
    reverse(nums, 0, nums.length - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, nums.length - 1);
}
// 辅助方法:反转数组指定范围内的元素
private void reverse(int[] nums, int start, int end) {
    while (start < end) {
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
        start++;
        end--;
    }
}

第二种方法我们其实思路也是互相交互元素但是我们 分为3端 第一个后我们最后一个元素交互 然后第一个元素到k-1 然后k 到最后一个元素 通过我们的reverse 方法3次反转实现数组的旋转,时间复杂度O(n),空间复杂度O(1) 对比第一种解法我们的时间复杂度下降很多,也更合理了。

  • 单元测试

public static void main(String[] args) {
    Leetcode6 leetcode6 = new Leetcode6();

    // 测试用例1:正常数组旋转
    System.out.println("=== 测试用例1:正常数组旋转 ===");
    int[] nums1 = {1, 2, 3, 4, 5, 6, 7};
    int k1 = 3;
    System.out.print("原数组: ");
    printArray(nums1);
    leetcode6.test2(nums1, k1);
    System.out.print("旋转后: ");
    printArray(nums1);
    System.out.println("期望结果: [5, 6, 7, 1, 2, 3, 4]");
    System.out.println();

    // 测试用例2:边界情况测试
    System.out.println("=== 测试用例2:边界情况测试 ===");

    // 空数组测试
    System.out.println("空数组测试:");
    int[] nullArray = null;
    System.out.println("执行前: null");
    leetcode6.test2(nullArray, 3);
    System.out.println("执行后: " + (nullArray == null ? "null" : "not null"));
    System.out.println();

    // 单元素数组测试
    System.out.println("单元素数组测试:");
    int[] singleElement = {1};
    System.out.print("执行前: ");
    printArray(singleElement);
    leetcode6.test2(singleElement, 5);
    System.out.print("执行后: ");
    printArray(singleElement);
    System.out.println();

    // k为0测试
    System.out.println("k为0测试:");
    int[] zeroKArray = {1, 2, 3};
    System.out.print("执行前: ");
    printArray(zeroKArray);
    leetcode6.test2(zeroKArray, 0);
    System.out.print("执行后: ");
    printArray(zeroKArray);
    System.out.println();

    // 测试用例3:k大于数组长度
    System.out.println("=== 测试用例3:k大于数组长度 ===");
    int[] nums3 = {1, 2, 3, 4, 5};
    int k3 = 7; // 7 % 5 = 2
    System.out.print("原数组: ");
    printArray(nums3);
    leetcode6.test2(nums3, k3);
    System.out.print("旋转后: ");
    printArray(nums3);
    System.out.println("期望结果: [4, 5, 1, 2, 3]");
}

// 辅助方法:打印数组
private static void printArray(int[] arr) {
    if (arr == null) {
        System.out.println("null");
        return;
    }
    System.out.print("[");
    for (int i = 0; i < arr.length; i++) {
        System.out.print(arr[i]);
        if (i < arr.length - 1) {
            System.out.print(", ");
        }
    }
    System.out.println("]");
}

测试结果

image.png