leetcode面试经典150题——06轮转数组

题目: 轮转数组

描述
给定一个整数数组 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]
leetcode链接

一开始就想到了将整体移动k次分为k轮,一轮移动一个位置,最后一个多出的元素存到第一个元素中

void rotate(vector<int>& nums, int k) {
	while(k--){
		int tag = nums[nums.size()-1];
		for(int i = nums.size()-1;i>=1;i--){
			nums[i] = nums[i-1];
		}
		nums[0] = tag;
	}
}

但是该方法的时间复杂度为o(n²),提交不通过,那么我们如何避免此种重复的复制,只需要一次就能够把元素复制到最终的位置呢,这里就引出了一种时间复杂度为o(n)的方法。

方法一:额外数组
我们对下标为i的元素移动k个位置,那么它最后的位置应该为k+i,但是k+i可能超过了数组的界限,因此我们将(k+i)%n,其中n为数组的长度,那么就得到了元素的最终位置,我再申请一个同样长度为n的数组,将元素存储到它移动后的位置上,最后将此数组复制到原数组中。此方法的缺点是空间复杂度为o(n).

void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> newArr(n);//新数组
        for (int i = 0; i < n; ++i) {
            newArr[(i + k) % n] = nums[i];//存储到新数组中正确的位置
        }
        nums.assign(newArr.begin(), newArr.end());
    }

关于vector的assign函数,assign()的主要功能是给vector重新赋值,更具体的,它会先把vector中的内容删除,然后根据用户提供的参数进行填充,assign一共有三种参数形式:
1:void assign (InputIterator first, InputIterator last);
将vector的内容替换为[first,last)地址存储的内容,复制的区间为左闭右开
2:void assign (size_type n, const value_type& val);
将vector中的内容替换为n个值为val的元素
3:void assign (initializer_list<value_type> il);
此形式将vector的内容替换为初始化列表il中的元素。

方法二:轮转数组
我们移动k次,一共有k%n个元素被移出数组外,那么我们假如翻转整个数组一次,末尾的k%n个元素就到了数组的头部,此时的整体的位置是正确的,但是顺序是逆序的,所以我们将0到k-1的元素再翻转一次,k到n-1的元素再翻转一次,就能够得到顺序正确的数组。次方法的时间复杂度为o(n),空间复杂度为o(1)

void reverse(vector<int>& nums, int start, int end) {//双指针实现翻转操作
        while (start < end) {//两个指针相遇的时候退出循环
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }

void rotate(vector<int>& nums, int k) {
     k %= nums.size();
     reverse(nums, 0, nums.size() - 1);//第一次翻转整个数组
     reverse(nums, 0, k - 1);//第二次翻转0到k-1
     reverse(nums, k, nums.size() - 1);//第三次翻转k到n-1
}

方法三:环状替换
我们方法一的唯一缺点就是需要额外的额数组,假如我们不存到额外的数组中,直接覆盖掉最终位置的元素,此时就会造成元素的丢失,那么我们应该用一个temp变量来存储交换后的元素,我们假设从第一个元素nums[0]开始,最开始temp存储的为nums[0],然后将(0+k)%n处位置的元素与temp交换,然后再交换temp和(0+k+k)%n处位置的元素,然后以此类推,最终我们一定会回到最初的元素,证明如下:
假设从位置为i的元素开始,经过n次替换后,到达的位置应该为(i+nk)%n = i,也就是元素i所在的位置,所以我们最多需要n次来达到最初的位置,此种情况也是遍历的数组n个元素,但是我们从一个元素出发开始替换,不一定会遍历到所有元素,此时我们应该从第二个元素开始继续替换,那我们什么时候停止替换呢,答案很明显就是我们替换了n个元素的时候,也就是把每个元素都放在了其最终的位置上的时候,我们停止替换,那么我们需要一个count来记录我们替换的元素的个数。
该方法的时间复杂度为o(n)
空间复杂度为o(1)

void rotate(vector<int> &nums,int k){
	int temp;//temp来保存替换后的元素
	int count = 0;
	for(int i=0;i<nums.size();i++){
		temp = nums[i];//从位置i开始替换
		int j = i;
		do{
			j = (j+k)%nums.size();
			int tag = temp;
			temp = nums[j];//交换temp和nums[j]
			nums[j] = tag;
			count++;
		}while(j!=i);//回到最开始的位置循环结束
		if(count == nums.size()){//当每个元素都移动后,退出循环
			break;
		}
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值