【算法】轮转数组

68 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第27天,点击查看活动详情

题目

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

输入: nums = [1,2,3,4,5,6,7], k = 3 输出: [5,6,7,1,2,3,4]

解题思路

关键词:右移;k非负

关键信息:位移;数组;k可以大于数组长度

空间遍历解法

巧妙利用轮转逻辑,创建一个新数组保存轮转后的数组结果最后把新数组结果给到原数组中。

  1. 先获取到轮转数组前置位。
  2. 循环遍历原数组内容,起点以lag = length - k开始。
  3. 最后把新数组内容拷贝即可。
 public void rotate(int[] nums, int k) {
    int length = nums.lengthl
    k = k % length;
    int[] res = new int[length];
    int flag = length - k; // 标记位
    if(flag != length){
        for(int i= 0;i < length;i++){
        	res[i] = nums[flag];
        	flag = (flag + 1) % l;
        }
        for(int i= 0;i < length;i++){
        	nums[i] = res[i];
        }
    }
}

翻转三次解法

翻转解法就是对数组做三次旋转来实现轮转过程。首先从题目中发现其中过程是[1,2,3,4] [5,6,7] ->[5,6,7] [1,2,3,4]。

如果知道翻转数组会发现数组是 [1,2,3,4,5,6,7] -> [7,6,5][4,3,2,1] 。从其中会找到一个规则前面一段轮转数组段和翻转数组段是翻转关系,后面一段数组也是一样的,利用该规律就能实现算法解决。

  1. 创建一个翻转函数实现对数组做翻转操作。
  2. 首先翻转整个数组,首尾对调。
  3. 翻转数组前段数据,范围是[0 , k-1]
  4. 翻转数组后段数据,范围是[k ,length - 1]
	public int[] reverse(int[] nums,int l,int r){
        while(l < r){
            int temp = nums[l];
            nums[l] = nums[r];
            nums[r] = temp;
            l ++;
            r --;
        }
        return nums;
    }
    public void rotate(int[] nums, int k) {
        // 三次翻转
        int length = nums.length;
        if(length <= 1 ||  k > length) return; 
        nums = reverse(nums,0,length - 1);
        nums = reverse(nums,0,k - 1);
        nums = reverse(nums,k,length - 1);
    }

由于题意中的k值并非是在数组长度范围内,可能还超出数组长度。因此上述算法还存在漏洞不能通过测试。经过代码优化只需要对k值和length值做判断处理,然后将循环轮转步数忽略即可得到k在length范围内的位移。

 if( k > length ) k = k % length;
if(length <= 1 ||  k > length) return;

跳跃替换解法

该算法思路是不做k次位移而是将k次位移只执行一次结束。例如k = 2,第二个位置的值就要移动到第四个位置上,直接替换第四个位置的值;同理第四个值也是如此移动到第六个位置依次类推。

  1. count记录下交换次数(最多交换次数是数组长度
  2. 从第一个值开始做for循环操作
  3. 在每个值循环过程中交换位置是一个内循环(因为保证交换位置后被交换的每个值都有自己的位置
  4. 因此在内循环过程中当开始位置和结束位置是同一个位置是表示内循环结束了。
public void rotate(int[] nums, int k) {
        int len  = nums.length;
       k = k % len;
       int count = 0;         // 用来记录交换次数 
        for(int start = 0; count < len; start++) {
            int cur = start;      
            int pre = nums[cur];   
            do{
                int next = (cur + k) % len;
                int temp = nums[next];    
                nums[next] = pre;
                pre = temp;
                cur = next;
                count++;
            }while(start != cur);   // 是否回到了原地
        }  
    }

参考