轮转数组
给定一个整数数组 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
进阶: 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。 你可以使用空间复杂度为 O(1) 的 原地
算法解决这个问题吗?
第一种:申请同等长度的数组
static public void Rotate1(int[] nums, int k)
{
//通过额外的空间实现轮转
int n = nums.Length;
int[] ints2 = new int[n];
for (int i = 0; i < n; i++)
{
ints2[(i+k)%n] = nums[i];//通过求余的方式进行移位
}
//改变原来数组的值
for (int i = 0; i < n; i++)
{
nums[i] = ints2[i];
}
}
队列形式(在c#中没有自带的addlast函数,需要自己写,不方便这种解法,作为一种思路)
class Solution { public void rotate(int[] nums, int k) { int length; if (nums == null || (length = nums.length) == 0 || 0 == k) { return; }
Deque<Integer> queue = new LinkedList<>();
for (int n : nums) {
queue.addLast(n);
}
for (int i = 0; i < k; i++) {
Integer last = queue.pollLast();
queue.addFirst(last);
}
int index = 0 ;
for (Integer num : queue){
nums[index++] = num;
}
}
}`
复杂度分析(空间复杂度较大,面试官不青睐)
时间复杂度: O(n),其中 n 为数组的长度。
空间复杂度: O(n)。
第二种:环状替代(思路来自leetcode官方题解)
static public void Rotate2(int[] nums, int k)
{
//递归求最大公约数//递归求最大公约数
int gcd(int a, int b)//无顺序要求
{
return b > 0 ? gcd(b, a % b) : a;
}
int n = nums.Length;
k = k % n;
int count = gcd(k,n);//求出数量
for (int i = 0; i < count; i++)
{
int cur = i;//记录初始位置,如果回到初始位置说明遍历结束
int curValue = nums[i];
do
{
int nextIndex =(cur+k)%n;//获取要被覆盖的数的下标
int temp = nums[nextIndex];
nums[nextIndex] = curValue;
curValue = temp;//保存下一个值,下一次从这里开始
cur = nextIndex;
}
while(i != cur);
}
}
思路分析:
通过原地替换的方式,进行替换。并且通过一个中间变量保存被替换的值,防止数值丢失。
复杂度分析
时间复杂度:O(n),其中 n 为数组的长度。每个元素只会被遍历一次。
空间复杂度:O(1)。我们只需常数空间存放若干变量。
第三种:数组翻转(左右指针,reverse函数中)
static public void Rotate3(int[] nums, int k)
{
//旋转数组函数
void reverse(ref int[] nums1,int start,int end)
{
int left = start;
int right = end-1;
while (left <= right)
{
int temp = nums1[left];
nums1[left] = nums1[right];
nums1[right] = temp;
left++;
right--;
}
}
//根据规律进行旋转
k %= nums.Length;
reverse(ref nums,0,nums.Length);
reverse(ref nums,0,k);
reverse(ref nums,k,nums.Length);
}
思路分析:
复杂度分析
时间复杂度:O(n),其中 n 为数组的长度。每个元素被翻转两次,一共 n 个元素,因此总时间复杂度为 O(2n)=O(n)。
空间复杂度:O(1)。
总结:这类题目考验我们对于数据规律的分析。要跳出系统,不能被方法限制解题思路。
在unity游戏开发中使用到的场景
-
物品管理:
在游戏中,玩家可能会拥有一个物品列表(例如背包)。当玩家选择旋转物品列表时,可以使用此算法将物品顺序改变。例如,玩家可以选择将物品向右旋转,以便快速查看物品。(实现循环显示) -
角色技能或装备切换:(比如fps游戏中的切换道具,通过鼠标滚轮)
在角色技能或装备的选择界面中,玩家可能需要快速浏览可用的技能或装备。使用这个旋转算法可以实现快速切换当前选中的技能或装备。 -
动画或特效序列:
在某些情况下,游戏中的动画或特效可能需要按照特定顺序播放。使用数组旋转可以方便地改变播放顺序。 -
轮盘或转盘游戏:
在一些游戏中,例如轮盘或转盘,玩家可能需要旋转一个选项列表。此算法可以用于实现转盘的逻辑,确保选项能够按预期顺序循环。 -
数据处理:
在处理游戏中的数据(例如分数、等级等)时,可能需要对数据进行旋转以实现某种逻辑或效果。这个算法可以有效地实现这一点。 -
AI决策:
在某些AI系统中,可能需要在多个决策选项之间进行循环选择。旋转数组可以帮助AI在不同的决策选项中进行选择。
总之,这个算法在需要处理循环数据结构或需要按特定顺序访问元素的情况下非常有用,能够提高代码的简洁性和效率。
代码实例:
以下是一些使用数组旋转算法的C#代码示例,展示了不同场景下的应用。
1. 背包物品旋转示例
using System;
public class Inventory
{
public static void RotateItems(string[] items, int k)
{
Rotate2(items, k);
}
static void Rotate2<T>(T[] nums, int k)
{
int gcd(int a, int b)
{
return b > 0 ? gcd(b, a % b) : a;
}
int n = nums.Length;
k = k % n;
int count = gcd(k, n);
for (int i = 0; i < count; i++)
{
int cur = i;
T curValue = nums[i];
do
{
int nextIndex = (cur + k) % n;
T temp = nums[nextIndex];
nums[nextIndex] = curValue;
curValue = temp;
cur = nextIndex;
}
while (i != cur);
}
}
public static void Main()
{
string[] items = { "Sword", "Shield", "Potion", "Bow", "Arrow" };
int k = 2; // Rotate by 2 positions
Console.WriteLine("Before rotation: " + string.Join(", ", items));
RotateItems(items, k);
Console.WriteLine("After rotation: " + string.Join(", ", items));
}
}
2. 角色技能切换示例
using System;
public class Character
{
public static void RotateSkills(string[] skills, int k)
{
Rotate2(skills, k);
}
static void Rotate2<T>(T[] nums, int k)
{
int gcd(int a, int b)
{
return b > 0 ? gcd(b, a % b) : a;
}
int n = nums.Length;
k = k % n;
int count = gcd(k, n);
for (int i = 0; i < count; i++)
{
int cur = i;
T curValue = nums[i];
do
{
int nextIndex = (cur + k) % n;
T temp = nums[nextIndex];
nums[nextIndex] = curValue;
curValue = temp;
cur = nextIndex;
}
while (i != cur);
}
}
public static void Main()
{
string[] skills = { "Fireball", "Ice Blast", "Lightning", "Heal" };
int k = 1; // Rotate by 1 position
Console.WriteLine("Before rotation: " + string.Join(", ", skills));
RotateSkills(skills, k);
Console.WriteLine("After rotation: " + string.Join(", ", skills));
}
}
3. 转盘游戏示例
using System;
public class Roulette
{
public static void RotateOptions(string[] options, int k)
{
Rotate2(options, k);
}
static void Rotate2<T>(T[] nums, int k)
{
int gcd(int a, int b)
{
return b > 0 ? gcd(b, a % b) : a;
}
int n = nums.Length;
k = k % n;
int count = gcd(k, n);
for (int i = 0; i < count; i++)
{
int cur = i;
T curValue = nums[i];
do
{
int nextIndex = (cur + k) % n;
T temp = nums[nextIndex];
nums[nextIndex] = curValue;
curValue = temp;
cur = nextIndex;
}
while (i != cur);
}
}
public static void Main()
{
string[] options = { "Option 1", "Option 2", "Option 3", "Option 4", "Option 5" };
int k = 3; // Rotate by 3 positions
Console.WriteLine("Before rotation: " + string.Join(", ", options));
RotateOptions(options, k);
Console.WriteLine("After rotation: " + string.Join(", ", options));
}
}
说明
在以上示例中,Rotate2
方法负责处理数组的旋转。你可以根据需要修改旋转的步数 k
,并观察结果。每个示例都展示了如何在不同的上下文中使用数组旋转算法。运行这些代码将输出旋转前后的数组状态,帮助你理解旋转的效果。