力扣最热一百题——轮转数组

目录

题目链接: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 <= 10^{5}
  • -2^{31} <= nums[i] <= 2^{31} - 1
  • 0 <= k <= 10^{5}

知识补充ArrayDeque ()

        ArrayDeque 是 Java 提供的一个基于数组实现的双端队列(deque),全名是 "Array Double Ended Queue"。它是 Deque 接口的实现之一,既可以用作队列(FIFO,先进先出),也可以用作栈(LIFO,后进先出)。它底层基于循环数组实现,具有较高的效率,可以在队列两端进行快速的插入和删除操作。

ArrayDeque 的特点:

  1. 双端操作

    • ArrayDeque 允许我们在队列的头部和尾部同时进行元素的添加和删除操作,这使得它既能模拟队列,又能模拟栈。
    • 由于它是一个双端队列,因此可以灵活使用栈和队列的典型操作。
  2. 动态数组扩展

    • 底层是基于循环数组实现的。当数组满了时,ArrayDeque 会自动扩容(通常会加倍数组大小),因此无需担心容量问题。
    • 相比于 LinkedListArrayDeque 的扩容代价相对较低,因为它直接在数组中操作数据,而无需额外的节点对象和指针。
  3. 高效操作

    • 插入和删除操作的时间复杂度都是 O(1),不涉及复杂的指针操作,因此效率非常高。
    • 不建议使用 ArrayDeque 来做随机访问(比如通过索引访问某个元素),因为这是线性时间操作 O(n),适合的场景是作为队列或栈使用。
  4. 线程不安全

    • 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--;  
        }  
    }  
};
 运行时间


总结

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

  • 43
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值