不改变正负数相对顺序,重排数组,使负数在正数之前
2016-03-16 15:24
567 查看
最近看到了来自 v_JULY_v的博客一个关于这个问题的解决(原文链接 :nhttp://blog.csdn.net/v_JULY_v/article/details/7329314),其中第一句话引起了我的兴趣,原话(一直未曾看到令人满意的答案,为何呢?),我不信邪,非要想一想,于是有了下面的解法,不过我最后还是信邪了。确实我想不出时间复杂度O(N),空间复杂度O(1)的方法,如果是链表的话,倒是可以做到。不过倒是有时间复杂度O(N),
空间复杂度O(m)(m为负数个数)的方法。还有O(n+m^2)(不知道这个时间复杂度算的对不对。数学不好 ==!),空间复杂度O(1)的方法。。m可以控制在n/2以内。当m大于n/2的时候,可以调整方法,反过来对正数进行操作。。
我的两种解法是要预知负数个数的情况下,所以需要先遍历一遍数组。其实我们知道负数的个数的情况下,是不是可以确定好了负数在数组前面的多少个位置,正数在数组的后多少个位置。然后我们再顺着数组遍历一遍,遇到负数,则放到数组前面的负数的区间,正数则放到正数应该在的区间内。遍历玩数组。就排好顺序了。如图:
但是还有一个问题要解决。。我们遇到一个负数的时候把他放在负数的地盘里,但是原来负数的地盘里可能有正数。这个正数怎么办呢?
所以我想到两个解决办法。
先看第二个方法的流程:
第一个方法:分配一个与负数个数相同大小的数组,用来存放这个负数区间里面的正数。
代码如下:
另外一种方式是,始终保持负数区间内的负数都是按顺序排好的。所以。这里需要浪费一点时间复杂度,如果能不要移动数组元素的话。那就好了。可惜我想不到了。。
代码如下
如果你发现有什么不对。
请斧正,感激不尽。
谢谢!
空间复杂度O(m)(m为负数个数)的方法。还有O(n+m^2)(不知道这个时间复杂度算的对不对。数学不好 ==!),空间复杂度O(1)的方法。。m可以控制在n/2以内。当m大于n/2的时候,可以调整方法,反过来对正数进行操作。。
我的两种解法是要预知负数个数的情况下,所以需要先遍历一遍数组。其实我们知道负数的个数的情况下,是不是可以确定好了负数在数组前面的多少个位置,正数在数组的后多少个位置。然后我们再顺着数组遍历一遍,遇到负数,则放到数组前面的负数的区间,正数则放到正数应该在的区间内。遍历玩数组。就排好顺序了。如图:
但是还有一个问题要解决。。我们遇到一个负数的时候把他放在负数的地盘里,但是原来负数的地盘里可能有正数。这个正数怎么办呢?
所以我想到两个解决办法。
先看第二个方法的流程:
第一个方法:分配一个与负数个数相同大小的数组,用来存放这个负数区间里面的正数。
代码如下:
inline void swap(int& a, int& b) { if (a != b) { a = a^b; b = a^b; a = a^b; } } //其实两个算法都可以调整。如果是负数超过一半。那就应该是先排正数,这样可以保证辅助空间小于n/2 //第二个算法的实际执行时间降低,也就是有递归的那个地方,因为那个地方跟负数的个数有关,如果负数多,则倒过来 //从后面往前面排,移动正数, int ReSort(int a[], int n) { assert(a); int nNeg = 0; //记录负数的总个数 for (int i = 0; n > 0 && i < n; i++) { if (a[i] < 0) { nNeg++; //统计负数的个数 } } if (n == nNeg || nNeg == 0)//全部是负数,或者没有负数。就返回 { return nNeg; } int* pa = new int[nNeg] {0};// (int*)malloc(nNeg * sizeof(int)); if (pa == NULL) { return 0; } int nFind = 0; //目前为止找到的负数的个数 int nStart = 0; //每个子区间的开始位置 int nPos = 0; int nTrip = 0; memcpy(pa, a, nNeg* sizeof(int)); for (int i = 0; i < n ; i++) { if ((i - nStart) == nNeg) { nNeg -= nTrip; nStart = i; nPos = 0; nTrip = 0; } if (a[i] < 0) { a[nFind++] = a[i]; a[i] = pa[i - nStart]; nTrip++; } else { if (nPos == 0 && nTrip == 0 && nNeg != 0) { swap(pa[nPos++], a[i]); } else { pa[nPos++] = a[i]; a[i] = pa[i - nStart]; } } } return nFind; //返回一共调整了多少个负数 }
另外一种方式是,始终保持负数区间内的负数都是按顺序排好的。所以。这里需要浪费一点时间复杂度,如果能不要移动数组元素的话。那就好了。可惜我想不到了。。
代码如下
int ReSort_1(int a[], int n) { assert(a); int nNeg = 0; //记录负数的总个数 for (int i = 0; n > 0 && i < n; i++) { if (a[i] < 0) { nNeg++; //统计负数的个数 } } if (n == nNeg || nNeg == 0)//全部是负数,或者没有负数。就返回 { return nNeg; } int nFind = 0; //目前为止找到的负数的个数 int nStart = 0; //每个子区间的开始位置 int nTrip = 0; //一个区间找出的负数个数 int nPos = 0; for (int i = 0; i < n ; i++) { if (i - nStart == nNeg) { nStart = i; nNeg -= nTrip; nFind += nTrip; nTrip = 0; } nPos = i - nStart; swap(a[i], a[nFind + nPos]); if (a[nFind + nPos] < 0) { //ReSort_1(a + nFind + nTrip, nPos - nTrip + 1); //相当于下面的循环。因为只有一个负数。。移动到他到合适位置即可 for (int j = nPos; j > nTrip; j--) { swap(a[nFind + j], a[nFind + j - 1]); } nTrip++; } } return nFind; //返回一共调整了多少个负数 }然后就是调用的部分代码:
void Print(int a[], int n) { assert(a); for (int i = 0; i < n; i++) { printf("%4d ", a[i]); } } int main() { int a[] = { -1,-7,-59,-12,15, 33 ,8,9,-999 }; #define len (sizeof(a)/sizeof(int)) int b[len] ; memcpy(b, a, sizeof(a)); printf("排序前: "); Print(a, len); printf("\n"); ReSort(a, len); ReSort_1(b, len); printf("\n第一种排序后: "); Print(a,len); printf("\n第二种排序后: "); Print(b, len); return _getch(); }
如果你发现有什么不对。
请斧正,感激不尽。
谢谢!
相关文章推荐
- JavaScript编程风格--基本的格式化
- bootstrap-导航总结
- windows2003网络负载平衡设置
- ORACLE 存儲過程發郵件
- ListView 中关于checkBox的复用出错问题,个人总结的
- 《JAVA与模式》之装饰模式
- Shiro源码分析
- Java中如何从一个字符串中删除指定字符
- ImageView各种scaleType的显示效果
- Linux内核分析——第四周学习笔记
- MongoDB使用小结:一些常用操作分享
- 男人的三大,女人的三大,人生的“三大”
- a标签的onclick和href同时存在的写法
- UIWebview笔记
- (转)运算优先级、结合性、求值顺序、副作用和顺序点
- android中跨进程debug
- Java面试知识点二
- 【Android】三种工厂模式简析
- FX Composer VS RenderMonkey
- 解决JavaMail 发送邮件javax.mail.MessagingException: 501 Syntax: HELO hostname问题