目录
题目链接:189. 轮转数组 - 力扣(LeetCode)
题目描述
示例
提示:
知识补充ArrayDeque ()
ArrayDeque 的特点:
常用方法:
详细示例:
运行结果:
总结:
解法一:使用队列模拟
Java写法:
运行时间
C++写法:
运行时间
编辑
解法二:三次翻转数组+双指针
Java写法:
运行时间
C++写法
运行时间
总结

题目链接:189. 轮转数组 - 力扣(LeetCode)
注:下述题目描述和示例均来自力扣
题目描述
给定一个整数数组 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 <=-<= nums[i] <=
- 1
0 <= k <=
知识补充ArrayDeque ()
 
 
        ArrayDeque 是 Java 提供的一个基于数组实现的双端队列(deque),全名是 "Array Double Ended Queue"。它是 Deque 接口的实现之一,既可以用作队列(FIFO,先进先出),也可以用作栈(LIFO,后进先出)。它底层基于循环数组实现,具有较高的效率,可以在队列两端进行快速的插入和删除操作。
ArrayDeque 的特点:
 
-  
双端操作:
ArrayDeque允许我们在队列的头部和尾部同时进行元素的添加和删除操作,这使得它既能模拟队列,又能模拟栈。- 由于它是一个双端队列,因此可以灵活使用栈和队列的典型操作。
 
 -  
动态数组扩展:
- 底层是基于循环数组实现的。当数组满了时,
ArrayDeque会自动扩容(通常会加倍数组大小),因此无需担心容量问题。 - 相比于 
LinkedList,ArrayDeque的扩容代价相对较低,因为它直接在数组中操作数据,而无需额外的节点对象和指针。 
 - 底层是基于循环数组实现的。当数组满了时,
 -  
高效操作:
- 插入和删除操作的时间复杂度都是 O(1),不涉及复杂的指针操作,因此效率非常高。
 - 不建议使用 
ArrayDeque来做随机访问(比如通过索引访问某个元素),因为这是线性时间操作 O(n),适合的场景是作为队列或栈使用。 
 -  
线程不安全:
ArrayDeque不是线程安全的,如果在多线程环境中使用,需要自行实现同步机制,比如通过synchronized块或使用并发队列如ConcurrentLinkedDeque。
 
常用方法:
-  
添加元素:
offer(E e):将元素添加到队列的尾部(队列特性)。offerFirst(E e):将元素添加到队列的头部(双端队列特性)。offerLast(E e):将元素添加到队列的尾部,和offer相同。
 -  
移除元素:
poll():移除并返回队列头部的元素(队列特性),如果队列为空,返回null。pollFirst():移除并返回队列头部的元素(双端队列特性)。pollLast():移除并返回队列尾部的元素。
 -  
获取元素:
peek():返回队列头部的元素但不移除它(队列特性),如果队列为空,返回null。peekFirst():返回队列头部的元素但不移除它(双端队列特性)。peekLast():返回队列尾部的元素但不移除它。
 -  
栈操作:
push(E e):将元素压入栈顶(相当于addFirst操作,栈特性)。pop():移除并返回栈顶元素(相当于pollFirst操作,栈特性)。
 
详细示例:
import java.util.ArrayDeque;
public class ArrayDequeExample {
    public static void main(String[] args) {
        // 创建一个ArrayDeque
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        // 队列操作
        deque.offer(10); // 添加到尾部
        deque.offer(20); // 添加到尾部
        deque.offer(30); // 添加到尾部
        System.out.println("队列内容: " + deque);
        // 出队操作(FIFO,先进先出)
        int firstElement = deque.poll(); // 移除并返回头部元素
        System.out.println("出队元素: " + firstElement);
        System.out.println("队列剩余内容: " + deque);
        // 栈操作
        deque.push(40); // 将元素压入栈顶
        deque.push(50); // 将元素压入栈顶
        System.out.println("栈内容: " + deque);
        // 出栈操作(LIFO,后进先出)
        int topElement = deque.pop(); // 移除并返回栈顶元素
        System.out.println("出栈元素: " + topElement);
        System.out.println("栈剩余内容: " + deque);
    }
}
 
 
运行结果:
队列内容: [10, 20, 30]
出队元素: 10
队列剩余内容: [20, 30]
栈内容: [50, 40, 20, 30]
出栈元素: 50
栈剩余内容: [40, 20, 30]
 
 
总结:
ArrayDeque非常适合作为队列和栈使用,因为它提供了高效的双端插入、删除操作。ArrayDeque相较于LinkedList来说在大多数场景下更高效,尤其是在不涉及频繁的随机访问时。- 它是 
Deque接口的常用实现,灵活且高效。 

解法一:使用队列模拟
Java写法:
class Solution {
    public void rotate(int[] nums, int k) {
        /**
                        1,2,3,4,5,6,7
         向右轮转 1 步: [7,1,2,3,4,5,6]
         向右轮转 2 步: [6,7,1,2,3,4,5]
         向右轮转 3 步: [5,6,7,1,2,3,4]
         */
         // 创建一个队列准备模拟这个过程
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        // 将数组中的值都添加进入队列中
        for (int num : nums) {
            deque.offer(num);
        }
        // 轮转K次
        for (int i = 1; i <= k; i++) {
            Integer last = deque.pollLast();
            deque.offerFirst(last);
        }
        // 再重新更新数组中的值
        int i = 0;
        for (Integer integer : deque) {
            nums[i] = integer;
            i++;
        }
    }
} 
运行时间

C++写法:
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
// 如果k大于数组长度,则取模简化问题  
        k %= nums.size();  
        if (k == 0) return; // 如果k为0,则无需旋转  
  
        // 创建一个deque准备模拟这个过程  
        std::deque<int> deque(nums.begin(), nums.end());  
  
        // 轮转K次  
        for (int i = 0; i < k; i++) {  
            int last = deque.back(); // 取出最后一个元素  
            deque.pop_back(); // 从deque尾部删除元素  
            deque.push_front(last); // 将取出的元素插入到deque的头部  
        }  
  
        // 再重新更新数组中的值  
        int i = 0;  
        for (int num : deque) {  
            nums[i++] = num;  
        }  
    }  
}; 
运行时间
挺慢的但你就说过没过吧

解法二:三次翻转数组+双指针
Java写法:
class Solution {
    public void rotate(int[] nums, int k) {
        /**
            1,2,3,4,5,6,7
            7,6,5,4,3,2,1
            分别翻转
            5,6,7,   1,2,3,4
         */
        int len = nums.length;
        // 如果k>len了那么只需要取余,因为轮转len个位置等于没有轮转
        if(k > len){
            k %= len;
        }
        // 先整体的翻转一下
        reverseArray(nums,0,len - 1);
        // 翻转前k位
        reverseArray(nums,0,k - 1);
        // 翻转剩下的
        reverseArray(nums,k,len - 1);
        
    }
    /**
     * 翻转数组的指定位置
     * @param arr 目标数组
     * @param start 翻转起始索引
     * @param end 翻转结束索引
     */
    public void reverseArray(int[] arr,int start,int end){
        // 利用前后两个指针来交换(也就是翻转数组)
        while (start < end){
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            // 移动指针
            start++;
            end--;
        }
    }
} 
运行时间

C++写法
class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len = nums.size();  
        // 如果k>len了那么只需要取余,因为轮转len个位置等于没有轮转  
        k = k % len;  
          
        // 先整体的翻转一下  
        reverseArray(nums, 0, len - 1);  
        // 翻转前k位  
        reverseArray(nums, 0, k - 1);  
        // 翻转剩下的  
        reverseArray(nums, k, len - 1);  
    }  
  
    /**  
     * 翻转数组的指定位置  
     * @param arr 目标数组  
     * @param start 翻转起始索引  
     * @param end 翻转结束索引  
     */  
    void reverseArray(std::vector<int>& arr, int start, int end) {  
        // 利用前后两个指针来交换(也就是翻转数组)  
        while (start < end) {  
            std::swap(arr[start], arr[end]);  
            // 移动指针  
            start++;  
            end--;  
        }  
    }  
}; 
运行时间


总结
其实我觉得直接使用队列还是更好理解更加的简单,虽然优化只是一个翻转数组但是也是极其的简单的操作。




















