[算法学习]不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
2012-07-03 11:45
501 查看
参考/article/1361438.html
题目如下:
一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序。
比如: input: 1,7,-5,9,-12,15 ,ans: -5,-12,1,7,9,15 。且要求时间复杂度O(N),空间O(1) 。
目前还未看到满足条件的算法。我只是简单实现july博客里面的各种思想。
复杂度o(N2)
首先,定义这样一个过程为“翻转”:(a1,a2,...,am,b1,b2,...,bn) --> (b1,b2,...,bn,a1,a2,...,am)。其次,对于待处理的未排序整数数组,从头到尾进行扫描,寻找(正正...正负...负负)串;每找到这样一个串,则计数器加1;若计数为奇数,则对当前串做一个“翻转”;反复扫描,直到再也找不到(正正...正负...负负)串。
举例如下
Input : 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8
Step1.1: [1, -1], 2, -2, [3, -3], 4, -4, [5, -5], 6, -6, [7, -7], 8, -8
Step1.2: [-1, 1], 2, -2, [-3, 3], 4, -4, [-5, 5], 6, -6, [-7, 7], 8, -8
Step2.1: -1, [1, 2, -2, -3], 3, 4, -4, -5, [5, 6, -6, -7], 7, 8, -8
Step2.2: -1, [-2, -3, 1, 2], 3, 4, -4, -5, [-6, -7, 5, 6], 7, 8, -8
Step3.1: -1, -2, -3, [1, 2, 3, 4, -4, -5, -6, -7], 5, 6, 7, 8, -8
Step3.2: -1, -2, -3, [-4, -5, -6, -7, 1, 2, 3, 4], 5, 6, 7, 8, -8
Step4.1: -1, -2, -3, -4, -5, -6, -7, [1, 2, 3, 4, 5, 6, 7, 8, -8]
Step4.2: -1, -2, -3, -4, -5, -6, -7, [-8, 1, 2, 3, 4, 5, 6, 7, 8]
Output: -1, -2, -3, -4, -5, -6, -7, -8, 1, 2, 3, 4, 5, 6, 7, 8
证明如下
先计算“翻转”的时间复杂度。
将(a1,a2,...,am,b1,b2,...,bn) 翻转为 (b1,b2,...,bn,a1,a2,...,am),只要三步:
(a1,a2,...,am,b1,b2,...,bn) -->
(bn,...,b2,b1,am,...,a2,a1) -->
(b1,b2,...,bn,am,...,a2,a1) -->
(b1,b2,...,bn,a1,a2,...,am)
总的来说,时间复杂度为O(2m+2n)
对于长度为N的数串来说,任选其中若干个互不重叠的子串进行翻转,总的时间复杂度不会超过O(N)。
接下来的问题,是“扫描”进行了多少次。
因为每次扫描只是翻转不相邻的(正正...正负...负负)串,因此所有被翻转的串,从正负数分界处被分为两部分,分别归入前后两个相邻的(正正...正负...负负)串。因此,每趟扫描能消灭一半的(正正...正负...负负)串。最终,总共需要扫描的次数最多为logN。
综上,时间复杂度为O(N*logN)。
贴一个自己写的代码
题目如下:
一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序。
比如: input: 1,7,-5,9,-12,15 ,ans: -5,-12,1,7,9,15 。且要求时间复杂度O(N),空间O(1) 。
目前还未看到满足条件的算法。我只是简单实现july博客里面的各种思想。
1.循环扫描法
思想: 从左到右扫描数组,如果扫描到的数为正数,则后面的数依次前移,该数放在最后面。如果是负数,则跳过,处理下一个数。这里需要注意的是,要考虑是否所有的数已经处理完毕。复杂度o(N2)
#include<iostream> using namespace std; void special_sort(int *arr,int n); int main() { int n; cin>>n; int *a=new int ; for(int i=0;i<n;i++) cin>>a[i]; special_sort(a,n); cout<<"排序后的数组为:"<<endl; for(int i=0;i<n;i++) cout<<a[i]<<" "; cout<<endl; delete[] a; return 0; } /* *功能:复杂度o(n2) */ void special_sort(int *arr,int n) { int tmp=0,num=0;//num用来标记当前处理到第几个数 for(int i=0;i<n&&num<n;++i,++num) { if(arr[i]<0) continue; else { tmp=arr[i]; for(int j=i;j<n-1;j++) arr[j]=arr[j+1]; arr[n-1]=tmp; i--;//arr[i]为正数的时候,后面的数会前移,这是arr[i]变为arr[i+1],为了不漏掉数,所以i不能变。--一次和++一次保证i不变 //但是这有可能会导致死循环。所以用num来标记已处理数的个数。 } } }
2.翻转法
http://qing.weibo.com/1570303725/5d98eeed33000hcb.html首先,定义这样一个过程为“翻转”:(a1,a2,...,am,b1,b2,...,bn) --> (b1,b2,...,bn,a1,a2,...,am)。其次,对于待处理的未排序整数数组,从头到尾进行扫描,寻找(正正...正负...负负)串;每找到这样一个串,则计数器加1;若计数为奇数,则对当前串做一个“翻转”;反复扫描,直到再也找不到(正正...正负...负负)串。
举例如下
Input : 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8
Step1.1: [1, -1], 2, -2, [3, -3], 4, -4, [5, -5], 6, -6, [7, -7], 8, -8
Step1.2: [-1, 1], 2, -2, [-3, 3], 4, -4, [-5, 5], 6, -6, [-7, 7], 8, -8
Step2.1: -1, [1, 2, -2, -3], 3, 4, -4, -5, [5, 6, -6, -7], 7, 8, -8
Step2.2: -1, [-2, -3, 1, 2], 3, 4, -4, -5, [-6, -7, 5, 6], 7, 8, -8
Step3.1: -1, -2, -3, [1, 2, 3, 4, -4, -5, -6, -7], 5, 6, 7, 8, -8
Step3.2: -1, -2, -3, [-4, -5, -6, -7, 1, 2, 3, 4], 5, 6, 7, 8, -8
Step4.1: -1, -2, -3, -4, -5, -6, -7, [1, 2, 3, 4, 5, 6, 7, 8, -8]
Step4.2: -1, -2, -3, -4, -5, -6, -7, [-8, 1, 2, 3, 4, 5, 6, 7, 8]
Output: -1, -2, -3, -4, -5, -6, -7, -8, 1, 2, 3, 4, 5, 6, 7, 8
证明如下
先计算“翻转”的时间复杂度。
将(a1,a2,...,am,b1,b2,...,bn) 翻转为 (b1,b2,...,bn,a1,a2,...,am),只要三步:
(a1,a2,...,am,b1,b2,...,bn) -->
(bn,...,b2,b1,am,...,a2,a1) -->
(b1,b2,...,bn,am,...,a2,a1) -->
(b1,b2,...,bn,a1,a2,...,am)
总的来说,时间复杂度为O(2m+2n)
对于长度为N的数串来说,任选其中若干个互不重叠的子串进行翻转,总的时间复杂度不会超过O(N)。
接下来的问题,是“扫描”进行了多少次。
因为每次扫描只是翻转不相邻的(正正...正负...负负)串,因此所有被翻转的串,从正负数分界处被分为两部分,分别归入前后两个相邻的(正正...正负...负负)串。因此,每趟扫描能消灭一半的(正正...正负...负负)串。最终,总共需要扫描的次数最多为logN。
综上,时间复杂度为O(N*logN)。
贴一个自己写的代码
/* *功能:翻转实现负数在前,正数在后,并不改变相对顺序 */ void special_sort2(int *arr,int n) { bool isFind=true; int start=-1,end=-1,turned=-1,counts=0; int status=0;//0表示期待正数的到来,1表示期待负数的带来 while(isFind) { isFind=false; start=-1; end=-1; turned=-1; counts=0; status=0; for(int i=0;i<n;i++) { if(0==status) { if(arr[i]>0) { if(start==-1)//第一次出现正数 { start=i;//设置起点 } }else { if(start!=-1) { turned=i; status=1; if(i==n-1)// { end=i; ++counts; if(counts%2!=0) { reverse(arr,start,turned,end); } isFind=true; status=0; start=-1; } } } }else { if(arr[i]>0)//出现正数 { end=i-1; status=0; ++counts; if(counts%2!=0) { reverse(arr,start,turned,end); } isFind=true; start=i; }else { if(i==n-1)//最后一个元素为负,前面出现过正数. { end=i; status=0; ++counts; if(counts%2!=0) { reverse(arr,start,turned,end); } isFind=true; } } } } } } void reverse(int *arr,int start,int turned,int end) { if(turned==-1)//无翻转点.则以中心为准,翻转一次 { int i=start,j=end,tmp; while(i<j) { tmp=arr[i]; arr[i]=arr[j]; arr[j]=tmp; ++i; --j; } }else { reverse(arr,start,-1,turned-1); reverse(arr,turned,-1,end); reverse(arr,start,-1,end); } }
相关文章推荐
- [算法学习]不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 算法,不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 第二十七章:不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)
- 不改变正负数之间相对顺序重新排列数组(时间O(N),空间O(1))
- 不改变正负数之间相对顺序重新排列数组(时间O(N),空间O(1))(桶排序)
- 不改变正负数之间相对顺序重新排列数组
- 第二十七章:不改变正负数之间相对顺序重新排列数组
- 一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序,比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时
- 一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序,比如: input: 1,7,-5,9,-12,15 ans: -5,-12,1,7,9,15 要求时
- 一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的正负数之间相对顺序。 要求时
- 不改变正负数之间相对顺序重新排列数组
- 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)
- 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)
- 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)
- 一个未排序整数数组,有正负数,重新排列使负数排在正数前面,并且要求不改变原来的相对顺序.
- 不改变正负数相对顺序重新排列数组
- 不改变正负数相对顺序重新排列数组
- 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)
- 程序员编程艺术第二十七章:不改变正负数相对顺序重新排列数组(无解?)