Java教程

旋转数组的解法——牛客算法入门题

本文主要是介绍旋转数组的解法——牛客算法入门题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

题目来源:旋转数组_牛客题霸_牛客网 (nowcoder.com)

题目描述

一个数组A中存有N(N&gt0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M>=0)个位置,即将A中的数据由(A0 A1 ……AN-1 )变换为(AN-M …… AN-1 A0 A1 ……AN-M-1 )(最后M个数循环移至最前面的M个位置)。如果需要考虑程序移动数据的次数尽量少,要如何设计移动的方法?

示例1

输入:
6,2,[1,2,3,4,5,6]
返回值:
[5,6,1,2,3,4]

备注:

(1<=N<=100,M>=0)




解法1:
对数组中元素进行遍历,遍历过程中将每个元素直接移动至应在的位置。(自己动手画一画就知道了)
class Solution {
public:
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型vector 给定数组
     * @return int整型vector
     */
    vector<int> solve(int n, int m, vector<int>& a)
    {
        // write code here
        if(m%n == 0)
            return a;           //若所要移动的位置是元素总数的整数倍,则相当于没移动。
        else
            m %= n;            //否则将右移距离除以元素总数取余。此举是为了减少计算移动后下标的计算量。
        int temp1,temp2;         //临时存储中间变量
        int new_pos,idx = 0,i =0,cnt =0;//new_pos为当前元素移动后的下标,cnt指目前已移动了多少个元素,idx为移动的轮数,同时也代表每轮初始移动元素的下标,i指代当前元素移动前的下标。
            
        for(idx;idx<=m;idx++)
        {
            
            if(cnt >= n)         //所有元素均移动完毕
                break;
            temp1=a[idx];        //暂存当前元素
            i = idx;           //初始化当前元素下标
            while((i+m)%n!= idx)    //只要当前元素移动后的下标不是最初的下标便继续循环。亦即移动这一轮的最后一个元素之前结束循环。
            {
                new_pos = (i+m)%n;  //新的下标=旧下标+移动位数 与元素总数n取余。
                temp2 = a[new_pos];  //暂存当前新下标中的元素
                a[new_pos] = temp1;  //移动到新下标中
                i = new_pos;      //下一个处理的是之前存放在新下标中的老元素
                temp1 = temp2;     //temp1为即将进行移动的元素值
                cnt++;          //已经完成了一次移动
            }
            a[idx] = temp1;       //移动这一轮最后一个元素到新下标。由于每轮最后一次移动的元素的新下标为这一轮移动的第一个元素的老下标,形成了一个闭环,所以不再继续。
            cnt++;            //完成最后一次移动
            }
        return a;            //循环结束即代表已完成了所有的移动任务。返回。
    }
};

同种思想的另一种写法: 

 

class Solution {
public:
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型vector 给定数组
     * @return int整型vector
     */
    vector<int> solve(int n, int m, vector<int>& a) {
        // write code here
        int start = 0;
        int pos = start;
        int count = 0;
        int tmp1 = a[start];
        int tmp2 = tmp1;
        m = m % n;
        while(true)
        {
            tmp1 = a[(pos + m) % n];
            a[(pos + m) % n] = tmp2;
            tmp2= tmp1;
            pos = (pos + m) % n;
            count ++;
            if(count == n)
                break;
            if(pos == start)
            {
                start = start + 1;
                pos = start;
                tmp1 = a[start];
                tmp2 = a[start];
            }
        }
        return a;
    }
};

 

 

 

 

 解法2:

  这个解法应该是这道题名字的由来,这也是网上最普遍的解法,即三次旋转解决问题。

 

  该方法基于如下的事实:当我们将数组的元素向右移动 k 次后,尾部 k mod n 个元素会移动至数组头部,其余元素向后移动 k mod n个位置。

 

  该方法为数组的翻转:我们可以先将所有元素翻转,这样尾部的 k mod n个元素就被移至数组头部,然后我们再翻转 [0, k mod n-1]区间的元素和 [k mod n, n-1]区间的元素即能得到最后的答案。

  这里自己写了个reverse函数用于旋转数组。

class Solution {
public:
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型vector 给定数组
     * @return int整型vector
     */
    void reverse(vector<int>& a,int start,int end){
        while(start<end){
            swap(a[start],a[end]);
            start++;
            end--;
 
        }
    }
    vector<int> solve(int n, int m, vector<int>& a) {
        m=m%n;
        reverse(a,0,n-1);
        reverse(a,0,m-1);
        reverse(a,m,n-1);
 
        return a;
    }
 
};

 

  

  这里充分运用了vector数组对象的方法begin()和end()以及STL中algorithm库的算法reverse,简化了步骤。

class Solution {
  public:
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型vector 给定数组
     * @return int整型vector
     */
    vector<int> solve(int n, int m, vector<int> &a) {
        // write code here
 
        if (!n)
            return a;
        if (!m)
            return a;
        m %= n;
 
        reverse(a.begin(), a.end());
        reverse(a.begin(), a.begin() + m);
        reverse(a.begin() + m , a.end());
        return a;
    }
};

 

解法3:

此法抓住了题目的漏洞,既然题目不允许声明另外的数组,那我直接在你给的vector数组后面追加一模一样的元素。实质是使用辅助数组来直接移动。

该法同样活用了vector类的成员函数push_back(int),pop_back(),分别代表在数组尾部追加与删除元素。

class Solution {
public:
    /**
     * 旋转数组
     * @param n int整型 数组长度
     * @param m int整型 右移距离
     * @param a int整型vector 给定数组
     * @return int整型vector
     */
    vector<int> solve(int n, int m, vector<int>& a) {
        // write code here
        int M = m%n;
        for(int i = 0; i < n; i++)
        {
            a.push_back(a[i]);
        }
        for(int i = 0; i < n; i++)
        {
            a[i] = a[i+n-M];
        }
        for(int i = 0;i<n;i++)
        {
            a.pop_back();
        }
        return a;
        //return
    }
};

 

 

 

参考来源:旋转数组__牛客网 (nowcoder.com)

这篇关于旋转数组的解法——牛客算法入门题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!