-
Notifications
You must be signed in to change notification settings - Fork 118
/
Copy pathRearrange Array Elements by Sign.cpp
152 lines (140 loc) · 7.26 KB
/
Rearrange Array Elements by Sign.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Runtime: 604 ms (Top 5.08%) | Memory: 138.6 MB (Top 22.35%)
class Solution {
// Uncomment/comment the below two lines for logs
// #define ENABLE_LOG(...) __VA_ARGS__
#define ENABLE_LOG(...)
public:
vector<int> rearrangeArray(vector<int>& nums) {
const int chunk_size = (int)(sqrt(nums.size())) / 2 * 2 + 2; // make it always an even number
const int original_n = nums.size();
constexpr int kPadPositive = 100006;
constexpr int kPadNegative = -100006;
// Pad the array to have size of a multiple of 4 * chunk_size
for (int i=0; i<nums.size() % (4 * chunk_size); ++i) {
nums.push_back(i % 2 == 0 ? kPadPositive : kPadNegative);
}
ENABLE_LOG(
cout << "chunk_size: " << chunk_size << endl;
cout << "padded array: ";
for (int v: nums) cout << v << " "; cout << endl;
)
// Denotion:
// the i-th positive number in original array: P_i.
// the i-th negative number in original array: N_i
// Step 1: Sort each chunk stably so that positive numbers appear before
// negative numbers
vector<int> chunk_buffer(chunk_size); // stores sorted result
for (int i=0; i < nums.size(); i += chunk_size) {
chunk_buffer.clear();
for (int j=i; j<i+chunk_size; ++j)
if (nums[j] > 0)
chunk_buffer.push_back(nums[j]);
for (int j=i; j<i+chunk_size; ++j)
if (nums[j] < 0)
chunk_buffer.push_back(nums[j]);
// Copy chunk_buffer back to nums[i:i+chunk_size]
copy_n(chunk_buffer.cbegin(), chunk_size, nums.begin() + i);
}
ENABLE_LOG(
cout << "chunk-sorted array: ";
for (int v: nums) cout << v << " "; cout << endl;
)
// Step 2: Merge every two chunks so that each chunk are either all positive numbers
// or all negative numbers
//
// This is based on the observation that:
// assuming chunk A having m positives followed by n negatives,
// chunk B having p positives followed by q negatives,
// a) if m+p >= chunk_size, we extract chunk_size positives into an all-positive chunk and put the remaining (m+p-chunk_size positives and n+q negatives) into the "buffer" chunk
// b) if n+q >= chunk_size, we extract chunk_size negatives into an all-negative chunk and make the remaining (m+p positives and n+q-chunk_size negatives) the "buffer" chunk
// Note that in either of the above two cases, the relative order for positive/negative numbers are unchanged
chunk_buffer = vector<int>{nums.begin(), nums.begin() + chunk_size};
for (int i = chunk_size; i<nums.size(); i+= chunk_size) {
const int m = find_if(chunk_buffer.cbegin(), chunk_buffer.cend(), [](int v) {
return v < 0;
}) - chunk_buffer.cbegin();
const int n = chunk_size - m;
const int p = find_if(nums.cbegin() + i, nums.cbegin() + i + chunk_size, [](int v) {
return v < 0;
}) - (nums.cbegin() + i);
const int q = chunk_size - p;
if (m + p >= chunk_size) {
// Copy positives to the previous chunk
copy_n(chunk_buffer.cbegin(), m, nums.begin() + i - chunk_size);
copy_n(nums.cbegin() + i, chunk_size - m, nums.begin() + i - chunk_size + m);
vector<int> new_buffer;
// the remaining positives (m+p-chunk_size) from this chunk
copy_n(nums.cbegin() + i + (chunk_size - m),
p - (chunk_size - m),
back_inserter(new_buffer));
// the remaining negatives in buffer
copy_n(chunk_buffer.cbegin() + m, n, back_inserter(new_buffer));
// the remaining negatives in this chunk
copy_n(nums.cbegin() + i + p, q, back_inserter(new_buffer));
chunk_buffer = move(new_buffer);
} else {
// Copy negatives to the previous chunk
copy_n(chunk_buffer.cbegin() + m, n, nums.begin() + i - chunk_size);
copy_n(nums.cbegin() + i + p, chunk_size - n, nums.begin() + i - chunk_size + n);
vector<int> new_buffer;
// the remaining positives in buffer
copy_n(chunk_buffer.cbegin(), m, back_inserter(new_buffer));
// the remaining positives in this chunk
copy_n(nums.cbegin() + i, p, back_inserter(new_buffer));
// the remaining negatives from this chunk
copy_n(nums.cbegin() + i + p + chunk_size - n, q - (chunk_size - n),
back_inserter(new_buffer));
chunk_buffer = move(new_buffer);
}
}
copy_n(chunk_buffer.cbegin(), chunk_size, nums.begin() + nums.size() - chunk_size);
ENABLE_LOG(
cout << "homonegeous array: ";
for (int v: nums) cout << v << " "; cout << endl;
)
// Step 3:
// After the above step, we will have sqrt(N) / 2 all-positive chunks and sqrt(N) / 2 all-negative chunks.
// Their initial chunk location is at (0, 1, 2, ..., sqrt(N))
// We want them to interleave each other, i.e., Positive Chunk1, Negative Chunk 1, Positive Chunk 2, Negative Chunk 2
// which could be achieved via cyclic permutation using an additional array tracking the target location of each chunk
// due to above padding, chunk_cnt is always a multiple of 4
const int chunk_cnt = nums.size() / chunk_size;
vector<int> target(chunk_cnt); // O(sqrt(N))
int positive_chunks = 0, negative_chunks = 0;
for (int i=0; i<chunk_cnt; ++i) {
if (nums[i * chunk_size] > 0)
target[i] = (positive_chunks++) * 2;
else
target[i] = (negative_chunks++) * 2 + 1;
}
for (int i=0; i<chunk_cnt; ++i) {
while (target[i] != i) {
swap_ranges(nums.begin() + i * chunk_size,
nums.begin() + i * chunk_size + chunk_size,
nums.begin() + target[i] * chunk_size);
swap(target[target[i]], target[i]);
}
}
ENABLE_LOG(
cout << "sorted array: ";
for (int v: nums) cout << v << " "; cout << endl;
)
// Step 4:
// Now we get Positive Chunk1, Negative Chunk 1, Positive Chunk 2, Negative Chunk 2, ...
// For each pair of adjacent (positive, negative) chunks, we can reorder the elements inside
// to make positive and negative numbers interleave each other
vector<int> two_chunk_elements_interleaved; // O(2 * chunk_size) = O(sqrt(N))
for (int i=0; i<nums.size(); i += 2 * chunk_size) {
two_chunk_elements_interleaved.clear();
for (int j=0; j<chunk_size; ++j)
{
two_chunk_elements_interleaved.push_back(nums[i + j]);
two_chunk_elements_interleaved.push_back(nums[i + chunk_size + j]);
}
copy_n(two_chunk_elements_interleaved.cbegin(), 2 * chunk_size, nums.begin() + i);
}
// Remove paddings
nums.resize(original_n);
return nums;
}
};