我们已经讨论了归并排序。如何修改算法以便合并在 O(1) 额外空间中工作,并且算法仍然在 O(n Log n) 时间内工作。我们可以假设输入值只是整数。
例子:
Input : 5 4 3 2 1
Output : 1 2 3 4 5
Input : 999 612 589 856 56 945 243
Output : 56 243 589 612 856 945 999
对于整数类型,可以使用一些模数和除法的数学技巧就地进行归并排序。这意味着在一个索引处存储两个元素值,并且可以使用模数和除法来提取。
首先,我们必须找到一个大于数组所有元素的值。现在我们可以将原始值存储为模数,将第二个值存储为除法。假设我们想将arr[i]和arr[j]都存储在索引 i(在 arr[i] 中的意思)。首先,我们必须找到一个大于 arr[i] 和 arr[j] 的“maxval” 。现在我们可以存储为arr[i] = arr[i] + arr[j]*maxval 。现在arr[i]%maxval将给出arr[i]的原始值, arr[i]/maxval将给出 arr[j] 的值。所以下面是合并排序的实现。
方法:(欧氏除法)
dividend = divisor * quotient +remainder
ex: 5/3 = q:1, r:2 applying euclidean: 3*1+2 =>5 (dividend)
divisor = maxele (absolute max element in the array)+1 (so that we always get non zero remainder)
quotient = min(first, second)
remainder = original element
Note: (when getting current element, assume the current container already has encoded element hence using % divisor)
first = arr[i] % divisor
second = arr[j] % divisor
encoded element = remainder + quotient*divisor
合并中可能出现的问题:
1.如果当前数字是Integer.MAX,那么通常大于当前元素的新编码值将导致整数溢出和数据损坏(在Python中没有数字大小的限制,因此不会发生此问题)。
2. 不处理负数(即,当用另一个 -ve 数(选择最小的)编码一个 -ve 数(当前)时,不能保留符号,因为两个数都有 -ve 符号。也必须使用绝对值当计算股息 = 除数*商+余数(除数 = maxele,商 = 最小,余数 = 原始)并且必须恢复符号时,由于符号保留问题,它仍然可能不起作用。
3. 仅适用于无符号整数,例如通常为非负的索引。
4. AUX = O(n) 在最坏的情况下,假设在像Python这样没有字/整数大小限制的语言中,当输入数组元素几乎在 Integer.MAX 时,那么编码值可能需要 2x 位空间来表示新的数字,整个 2x 位空间可以变成 +1x 数组大小,这几乎就像创建一个 AUX 数组,但是以间接的方式。
5. mod 和 Division 操作是最昂贵的,因此降低了整体性能(在一定程度上)。
C++
// C++ program to sort an array using merge sort such
// that merge operation takes O(1) extra space.
#include
using namespace std;
void merge(int arr[], int beg, int mid, int end, int maxele)
{
int i = beg;
int j = mid + 1;
int k = beg;
while (i <= mid && j <= end) {
if (arr[i] % maxele <= arr[j] % maxele) {
arr[k] = arr[k] + (arr[i] % maxele) * maxele;
k++;
i++;
}
else {
arr[k] = arr[k] + (arr[j] % maxele) * maxele;
k++;
j++;
}
}
while (i <= mid) {
arr[k] = arr[k] + (arr[i] % maxele) * maxele;
k++;
i++;
}
while (j <= end) {
arr[k] = arr[k] + (arr[j] % maxele) * maxele;
k++;
j++;
}
// Obtaining actual values
for (int i = beg; i <= end; i++)
arr[i] = arr[i] / maxele;
}
// Recursive merge sort with extra parameter, naxele
void mergeSortRec(int arr[], int beg, int end, int maxele)
{
if (beg < end) {
int mid = (beg + end) / 2;
mergeSortRec(arr, beg, mid, maxele);
mergeSortRec(arr, mid + 1, end, maxele);
merge(arr, beg, mid, end, maxele);
}
}
// This functions finds max element and calls recursive
// merge sort.
void mergeSort(int arr[], int n)
{
int maxele = *max_element(arr, arr+n) + 1;
mergeSortRec(arr, 0, n-1, maxele);
}
int main()
{
int arr[] = { 999, 612, 589, 856, 56, 945, 243 };
int n = sizeof(arr) / sizeof(arr[0]);
mergeSort(arr, n);
cout << "Sorted array \n";
for (int i = 0; i < n; i++)
cout << arr[i] << " ";
return 0;
}
Java
// Java program to sort an array
// using merge sort such that
// merge operation takes O(1)
// extra space.
import java.util.Arrays;
class GFG
{
static void merge(int[] arr, int beg,
int mid, int end,
int maxele)
{
int i = beg;
int j = mid + 1;
int k = beg;
while (i <= mid && j <= end)
{
if (arr[i] % maxele <=
arr[j] % maxele)
{
arr[k] = arr[k] + (arr[i]
% maxele) * maxele;
k++;
i++;
}
else
{
arr[k] = arr[k] +
(arr[j] % maxele)
* maxele;
k++;
j++;
}
}
while (i <= mid)
{
arr[k] = arr[k] + (arr[i]
% maxele) * maxele;
k++;
i++;
}
while (j <= end)
{
arr[k] = arr[k] + (arr[j]
% maxele) * maxele;
k++;
j++;
}
// Obtaining actual values
for (i = beg; i <= end; i++)
{
arr[i] = arr[i] / maxele;
}
}
// Recursive merge sort
// with extra parameter, naxele
static void mergeSortRec(int[] arr, int beg,
int end, int maxele)
{
if (beg < end)
{
int mid = (beg + end) / 2;
mergeSortRec(arr, beg,
mid, maxele);
mergeSortRec(arr, mid + 1,
end, maxele);
merge(arr, beg, mid,
end, maxele);
}
}
// This functions finds
// max element and calls
// recursive merge sort.
static void mergeSort(int[] arr, int n)
{
int maxele = Arrays.stream(arr).max().getAsInt() + 1;
mergeSortRec(arr, 0, n - 1, maxele);
}
// Driver code
public static void main(String[] args)
{
int[] arr = {999, 612, 589,
856, 56, 945, 243};
int n = arr.length;
mergeSort(arr, n);
System.out.println("Sorted array ");
for (int i = 0; i < n; i++)
{
System.out.print(arr[i] + " ");
}
}
}
// This code is contributed by 29AjayKumar
Python3
# Python3 program to sort an array using
# merge sort such that merge operation
# takes O(1) extra space.
def merge(arr, beg, mid, end, maxele):
i = beg
j = mid + 1
k = beg
while (i <= mid and j <= end):
if (arr[i] % maxele <= arr[j] % maxele):
arr[k] = arr[k] + (arr[i] %
maxele) * maxele
k += 1
i += 1
else:
arr[k] = arr[k] + (arr[j] %
maxele) * maxele
k += 1
j += 1
while (i <= mid):
arr[k] = arr[k] + (arr[i] %
maxele) * maxele
k += 1
i += 1
while (j <= end):
arr[k] = arr[k] + (arr[j] %
maxele) * maxele
k += 1
j += 1
# Obtaining actual values
for i in range(beg, end + 1):
arr[i] = arr[i] // maxele
# Recursive merge sort with extra
# parameter, naxele
def mergeSortRec(arr, beg, end, maxele):
if (beg < end):
mid = (beg + end) // 2
mergeSortRec(arr, beg, mid, maxele)
mergeSortRec(arr, mid + 1, end, maxele)
merge(arr, beg, mid, end, maxele)
# This functions finds max element and
# calls recursive merge sort.
def mergeSort(arr, n):
maxele = max(arr) + 1
mergeSortRec(arr, 0, n - 1, maxele)
# Driver Code
if __name__ == '__main__':
arr = [ 999, 612, 589, 856, 56, 945, 243 ]
n = len(arr)
mergeSort(arr, n)
print("Sorted array")
for i in range(n):
print(arr[i], end = " ")
# This code is contributed by mohit kumar 29
C#
// C# program to sort an array
// using merge sort such that
// merge operation takes O(1)
// extra space.
using System;
using System.Linq;
class GFG
{
static void merge(int []arr, int beg,
int mid, int end,
int maxele)
{
int i = beg;
int j = mid + 1;
int k = beg;
while (i <= mid && j <= end)
{
if (arr[i] %
maxele <= arr[j] % maxele)
{
arr[k] = arr[k] + (arr[i] %
maxele) * maxele;
k++;
i++;
}
else
{
arr[k] = arr[k] +
(arr[j] % maxele) *
maxele;
k++;
j++;
}
}
while (i <= mid)
{
arr[k] = arr[k] + (arr[i] %
maxele) * maxele;
k++;
i++;
}
while (j <= end)
{
arr[k] = arr[k] + (arr[j] %
maxele) * maxele;
k++;
j++;
}
// Obtaining actual values
for ( i = beg; i <= end; i++)
arr[i] = arr[i] / maxele;
}
// Recursive merge sort
// with extra parameter, naxele
static void mergeSortRec(int []arr, int beg,
int end, int maxele)
{
if (beg < end)
{
int mid = (beg + end) / 2;
mergeSortRec(arr, beg,
mid, maxele);
mergeSortRec(arr, mid + 1,
end, maxele);
merge(arr, beg, mid,
end, maxele);
}
}
// This functions finds
// max element and calls
// recursive merge sort.
static void mergeSort(int []arr, int n)
{
int maxele = arr.Max() + 1;
mergeSortRec(arr, 0, n - 1, maxele);
}
//Driver code
public static void Main ()
{
int []arr = {999, 612, 589,
856, 56, 945, 243};
int n = arr.Length;
mergeSort(arr, n);
Console.WriteLine("Sorted array ");
for (int i = 0; i < n; i++)
Console.Write( arr[i] + " ");
}
}
// This code is contributed
// by inder_verma.
Javascript
Sorted array
56 243 589 612 856 945 999
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。