算法,不改变正负数之间相对顺序重新排列数组.时间O(N),空间O(1)

大概思路:
1,遍历数组,找到最小值,最大值,以及负数元素个数,然后设 s = 最小值绝对值,m = 最大值 + 1,k = 负数元素个数 - 1。

2,遍历数组,改变每个元素,设该元素为a, 改变后为 c = a+s + (m+s)*n, 其中,n为该元素在按照题目要求排序后的位置,其实不难得到,遍历过程中分别用两个变量保存当前正负元素的排序号,如果当前元素是负数,则第一个变量+1然后付值给n,如果是正数,则第二个变量+1然后付值给n,注意,第一个变量从0开始计数,第二个从k开始计数。

3,再次遍历数组,设每个元素为c,则c对(m+s)取余数后得到b,由于b= a+s => a = b-s,然后 (c -b)/(m+s) 得到n,也就是该元素的排序号,用此排序号进行占位排序,一个替一个一直到最大号。


简单的说如果把数组先变成正数,
A ={-1,8,-2,-3,2,7} , 最大元素 8 设m=8+1=9, -3最小设s=3,都加3=> {2,11,1,0,5,10}=B。

然后是用最大元素11加上1=12,即m+s=9+3=12 作为参数用于改变数组B每个元素,C={2+12*1, 11+12*4(也就是初始数组元素8到最后应该排在第4位), 1+12*2, 0+12*3, 5*12*5, 10*12*6}
初始数组A最大元素8=>m=8+1=9,也就是说m+s=9+3=12大于任何一个数组B中的元素,那么通过构造C中元素c=a+s+(m+s)*n我们不但能够存储原有A中元素a,还可以存储排序后的排号n.
原理等同于那个把整数变成浮点数的方法,但是我这里通过计算还原原来的元素以及排序号,c%(m+s)得到a+s,a可以还原,(c-a-s)/(m+s)得到排序号。

代码如下:

#include <limits>
#include <iostream>


using namespace std;

int main(int argc, char **argv)
{
	const size_t size = 6;
	int A[size] = {-3,1,8,-2,1,2};
	
	//first step, find min, max 
	
	int min = numeric_limits<int>::max();
	int max = numeric_limits<int>::min();
	//and number of elements which is less than 0
	//later used as unsigned elements new position
	size_t unsignedCnt = 0; 
	for(size_t i = 0; i < size; ++i)
	{
		if(A[i] < min)
			min = A[i];
		if(A[i] > max)
			max = A[i];
		if(A[i] < 0)
			++unsignedCnt;
	}
	++max;
	if(min < 0)
		min = -min;
	
	//signed elements first position
	size_t signedCnt = 0;	
	
	//new position index
	size_t cnt = 0;
	for(size_t i = 0; i < size; ++i)
	{
		//update element to make them greater than 0
		//and include a final position in 
		
		if(A[i] < 0)		
			cnt = ++signedCnt;			
		else		
			cnt = ++unsignedCnt;
		
		
		//update element
		//cnt starts from 1
		A[i] = A[i] + min + (min + max) * cnt;
		}
	
	
	//in-place key index sorting
	for(size_t i = 0; i < size; ++i)
	{
		//if the element already put in right position continue
		if(A[i] <= max) continue;
		else
		{
			int temp = 0;			
			int prev = A[i];
			size_t id = 0;			
						
			do{				
				temp = prev % (max + min);
				id = (prev - temp)/(min + max) - 1;
				prev = A[id];				
				A[id] = temp - min;								
			}while(id != i);			
			
		}
			
	}

	for(size_t i = 0; i < size; ++i)
	{
		cout << A[i]<<endl;
	}		
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值