始终相信能听明白和能说明白是两种不同的境界,如果对于一件问题,你说你明白了,但是你却不能说明白,那在我眼里就会有这样的一个事实,那就是:你并没有真正地明白。当然,也存在这种情况,那就是说是说明白了,但是未必对,这正是我担心的情况,所以还请阅读过我所有文章的朋友们指出我文章中的错误。谢谢你们……
1.问题定义
设计一个算法,把一个含有n个元素的数组A循环右移k位,要求时间复杂度是O(n)的。举个例子说明一下算法要达到的效果:数组中包含的元素为123456,现在要循环右移2位,则变成了561234 。
2.解决方案
2.1 朴素的思路
往往是这样,朴素的思路虽不能满足题目的要求,但是确可能是优化的依据。对于这个问题朴素的解法是,每次完成一次右移,并且完成一次右移的时间复杂度是O(n),这样的操作要进行k次,所以时间复杂度是O(n*k)。但是现在想一想,既然已经知道要右移的位数k,那么要是还一步一步的移是不是显得有点笨了呢。为什么不一步到位呢?
2.2 空间换时间
对于算法上的优化,以空间换时间是一种比较常规的办法了。可以借助一个辅助数组T,先把数组A的第n-k+1到n位数组中的元素存储到T中,然后再把数组A中的第1到n-k位数组元素存储到辅助数组T中,然后将数组T中的元素复制回数组A,这样就完成了数组的循环右移,时间复杂度为O(n),但是呢,却增加了O(n)的空间复杂度。如果要求最多只能允许使用两个辅助空间呢?这可就要想一想能不能进一步优化了。
2.3 利用“翻转”完成优化
考虑一下数组A中元素123456循环右移2位到底是怎么个情况!!!可不可以这样实现呢?将数组A分成两个部分:A[0~n-k-1] 和 A[n-k~n-1] ,将这两个部分分别翻转,然后放在一起在翻转(逆序)。具体是这样的:
(1)翻转1234:123456 ---> 432156
(2)翻转56: 432156 ---> 432165
(3)翻转432165:432165 ---> 561234
看看吧,确实完成了翻转的操作。现在来看一下代码(灰常简单):
1 //逆序 2 void Reverse(int A[],int b,int e) 3 { 4 for(;b < e;b++,e--) 5 { 6 int temp = A[b]; 7 A[b] = A[e]; 8 A[e] = temp; 9 } 10 } 11 //循环右移 12 void RightShift(int A[],int,int n,int k) 13 { 14 Reverse(A,0,n-k-1); 15 Reverse(A,n-k,n-1); 16 Reverse(A,0,n-1); 17 }
现在可以分析一下时空复杂度了,时间复杂度显然是O(n),主要是完成翻转(逆序)操作,并且只用了一个辅助空间。显然这是一个不错算法哦。。。
注:
细心的读者可能就发现,这里的右移并没有说小于n呀,那么一个k>n那就会出现问题,经过简单的分析,我们发现如果A[]={1,2,3,4,5,6},数组长度是6,那么循环右移7位和循环右移1位是一样的,所以只需要把函数RightShift()中的k取值为k=k%n。如此一来问题就解决了。
学习中的一点总结,欢迎拍砖哦^^
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库