📜  就地合并排序

📅  最后修改于: 2021-05-05 00:22:10             🧑  作者: Mango

实施合并排序,即标准实施,将排序算法保持在原位。
就地意味着它不会像标准情况下那样为合并操作占用额外的内存。

例子:

方法1:

  • 维护两个指针,这些指针指向必须合并的段的起点。
  • 比较指针所在的元素。
  • 如果element1 那么element1在正确的位置,只需增加指针1即可
  • 否则,将element1和element2之间的所有元素(包括element1但不包括element2)右移1,然后将element2放在element1的先前位置(即,向右移位之前) 。将所有指针加1

下面是上述方法的实现:

C++
// C++ program in-place Merge Sort
#include 
using namespace std;
 
// Merges two subarrays of arr[].
// First subarray is arr[l..m]
// Second subarray is arr[m+1..r]
// Inplace Implementation
void merge(int arr[], int start, int mid, int end)
{
    int start2 = mid + 1;
 
    // If the direct merge is already sorted
    if (arr[mid] <= arr[start2]) {
        return;
    }
 
    // Two pointers to maintain start
    // of both arrays to merge
    while (start <= mid && start2 <= end) {
 
        // If element 1 is in right place
        if (arr[start] <= arr[start2]) {
            start++;
        }
        else {
            int value = arr[start2];
            int index = start2;
 
            // Shift all the elements between element 1
            // element 2, right by 1.
            while (index != start) {
                arr[index] = arr[index - 1];
                index--;
            }
            arr[start] = value;
 
            // Update all the pointers
            start++;
            mid++;
            start2++;
        }
    }
}
 
/* l is for left index and r is right index of the
   sub-array of arr to be sorted */
void mergeSort(int arr[], int l, int r)
{
    if (l < r) {
 
        // Same as (l + r) / 2, but avoids overflow
        // for large l and r
        int m = l + (r - l) / 2;
 
        // Sort first and second halves
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
 
        merge(arr, l, m, r);
    }
}
 
/* UTILITY FUNCTIONS */
/* Function to print an array */
void printArray(int A[], int size)
{
    int i;
    for (i = 0; i < size; i++)
        printf("%d ", A[i]);
    printf("\n");
}
 
/* Driver program to test above functions */
int main()
{
    int arr[] = { 12, 11, 13, 5, 6, 7 };
    int arr_size = sizeof(arr) / sizeof(arr[0]);
 
    mergeSort(arr, 0, arr_size - 1);
 
    printArray(arr, arr_size);
    return 0;
}


Java
// Java program in-place Merge Sort
 
public class GFG {
 
    // Merges two subarrays of arr[].
    // First subarray is arr[l..m]
    // Second subarray is arr[m+1..r]
    // Inplace Implementation
    static void merge(int arr[], int start, int mid,
                      int end)
    {
        int start2 = mid + 1;
 
        // If the direct merge is already sorted
        if (arr[mid] <= arr[start2]) {
            return;
        }
 
        // Two pointers to maintain start
        // of both arrays to merge
        while (start <= mid && start2 <= end) {
 
            // If element 1 is in right place
            if (arr[start] <= arr[start2]) {
                start++;
            }
            else {
                int value = arr[start2];
                int index = start2;
 
                // Shift all the elements between element 1
                // element 2, right by 1.
                while (index != start) {
                    arr[index] = arr[index - 1];
                    index--;
                }
                arr[start] = value;
 
                // Update all the pointers
                start++;
                mid++;
                start2++;
            }
        }
    }
 
    /* l is for left index and r is right index of the
       sub-array of arr to be sorted */
    static void mergeSort(int arr[], int l, int r)
    {
        if (l < r) {
 
            // Same as (l + r) / 2, but avoids overflow
            // for large l and r
            int m = l + (r - l) / 2;
 
            // Sort first and second halves
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
 
            merge(arr, l, m, r);
        }
    }
 
    /* UTILITY FUNCTIONS */
    /* Function to print an array */
    static void printArray(int A[], int size)
    {
        int i;
        for (i = 0; i < size; i++)
            System.out.print(A[i] + " ");
        System.out.println();
    }
 
    /* Driver program to test above functions */
    public static void main(String[] args)
    {
        int arr[] = { 12, 11, 13, 5, 6, 7 };
        int arr_size = arr.length;
 
        mergeSort(arr, 0, arr_size - 1);
        printArray(arr, arr_size);
    }
    // This code is contributed by ANKITRAI1
}


Python3
# Python program in-place Merge Sort
 
# Merges two subarrays of arr.
# First subarray is arr[l..m]
# Second subarray is arr[m+1..r]
# Inplace Implementation
 
 
def merge(arr, start, mid, end):
    start2 = mid + 1
 
    # If the direct merge is already sorted
    if (arr[mid] <= arr[start2]):
        return
 
    # Two pointers to maintain start
    # of both arrays to merge
    while (start <= mid and start2 <= end):
 
        # If element 1 is in right place
        if (arr[start] <= arr[start2]):
            start += 1
        else:
            value = arr[start2]
            index = start2
 
            # Shift all the elements between element 1
            # element 2, right by 1.
            while (index != start):
                arr[index] = arr[index - 1]
                index -= 1
 
            arr[start] = value
 
            # Update all the pointers
            start += 1
            mid += 1
            start2 += 1
 
 
'''
* l is for left index and r is right index of
the sub-array of arr to be sorted
'''
 
 
def mergeSort(arr, l, r):
    if (l < r):
 
        # Same as (l + r) / 2, but avoids overflow
        # for large l and r
        m = l + (r - l) // 2
 
        # Sort first and second halves
        mergeSort(arr, l, m)
        mergeSort(arr, m + 1, r)
 
        merge(arr, l, m, r)
 
 
''' UTILITY FUNCTIONS '''
''' Function to pran array '''
 
 
def printArray(A, size):
 
    for i in range(size):
        print(A[i], end=" ")
    print()
 
 
''' Driver program to test above functions '''
if __name__ == '__main__':
    arr = [12, 11, 13, 5, 6, 7]
    arr_size = len(arr)
 
    mergeSort(arr, 0, arr_size - 1)
    printArray(arr, arr_size)
 
# This code is contributed by 29AjayKumar


C#
// C# program in-place Merge Sort
// sum.
using System;
 
class GFG {
 
    // Merges two subarrays of arr[].
    // First subarray is arr[l..m]
    // Second subarray is arr[m+1..r]
    // Inplace Implementation
    static void merge(int[] arr, int start, int mid,
                      int end)
    {
        int start2 = mid + 1;
 
        // If the direct merge is already sorted
        if (arr[mid] <= arr[start2]) {
            return;
        }
 
        // Two pointers to maintain start
        // of both arrays to merge
        while (start <= mid && start2 <= end) {
 
            // If element 1 is in right place
            if (arr[start] <= arr[start2]) {
                start++;
            }
            else {
                int value = arr[start2];
                int index = start2;
 
                // Shift all the elements between element 1
                // element 2, right by 1.
                while (index != start) {
                    arr[index] = arr[index - 1];
                    index--;
                }
                arr[start] = value;
 
                // Update all the pointers
                start++;
                mid++;
                start2++;
            }
        }
    }
 
    /* l is for left index and r is right index of the
    sub-array of arr to be sorted */
    static void mergeSort(int[] arr, int l, int r)
    {
        if (l < r) {
 
            // Same as (l + r) / 2, but avoids overflow
            // for large l and r
            int m = l + (r - l) / 2;
 
            // Sort first and second halves
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
 
            merge(arr, l, m, r);
        }
    }
 
    /* UTILITY FUNCTIONS */
    /* Function to print an array */
    static void printArray(int[] A, int size)
    {
        int i;
        for (i = 0; i < size; i++)
            Console.Write(A[i] + " ");
        Console.WriteLine();
    }
 
    /* Driver code */
    public static void Main(String[] args)
    {
        int[] arr = { 12, 11, 13, 5, 6, 7 };
        int arr_size = arr.Length;
 
        mergeSort(arr, 0, arr_size - 1);
        printArray(arr, arr_size);
    }
}
 
// This code is contributed by Princi Singh


Java
// Java program for the above approach
import java.io.*;
import java.util.*;
 
class InPlaceMerge {
 
    // Calculating next gap
    private static int nextGap(int gap)
    {
        if (gap <= 1)
            return 0;
        return (int)Math.ceil(gap / 2.0);
    }
 
    // Function for swapping
    private static void swap(int[] nums, int i, int j)
    {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
 
    // Merging the subarrays using shell sorting
    // Time Complexity: O(nlog n)
    // Space Complexity: O(1)
    private static void inPlaceMerge(int[] nums, int start,
                                     int end)
    {
        int gap = end - start + 1;
        for (gap = nextGap(gap); gap > 0;
             gap = nextGap(gap)) {
            for (int i = start; i + gap <= end; i++) {
                int j = i + gap;
                if (nums[i] > nums[j])
                    swap(nums, i, j);
            }
        }
    }
 
    // merge sort makes log n recursive calls
    // and each time calls merge()
    // which takes nlog n steps
    // Time Complexity: O(n*log n + 2((n/2)*log(n/2)) +
    // 4((n/4)*log(n/4)) +.....+ 1)
    // Time Complexity: O(logn*(n*log n))
    // i.e. O(n*(logn)^2)
    // Space Complexity: O(1)
    private static void mergeSort(int[] nums, int s, int e)
    {
        if (s == e)
            return;
 
        // Calculating mid to slice the
        // array in two halves
        int mid = (s + e) / 2;
 
        // Recursive calls to sort left
        // and right subarrays
        mergeSort(nums, s, mid);
        mergeSort(nums, mid + 1, e);
        inPlaceMerge(nums, s, e);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        int[] nums = new int[] { 12, 11, 13, 5, 6, 7 };
        mergeSort(nums, 0, nums.length - 1);
        System.out.println(Arrays.toString(nums));
    }
}


输出
5 6 7 11 12 13 

注:上述方法的时间复杂度为O(N 2),因为合并是O(n 2)。标准合并排序的时间复杂度较小,为O(n Log n)。

方法二:

这个想法:我们开始比较彼此相距遥远而不是相邻的元素。基本上,我们使用外壳排序将两个排序后的数组合并在一起,并增加O(1)个空间。

mergeSort():

  • 计算中二将数组分为两半(左子数组和右子数组)
  • 在左子数组和右子数组上递归调用合并排序以对其进行排序
  • 调用合并函数合并左子数组和右子数组

合并():

  • 对于每次通过,我们都计算间隙并比较间隙右侧的元素。
  • 以n / 2的上限值开始间隔,其中n是左右子数组的组合长度。
  • 每次通过,间隙减小到间隙/ 2的上限。
  • 拿一个指针我传递数组。
  • 如果第(i + gap)个元素小于(或大于按降序排序时的第i个元素),请交换第i个和第(i + gap)个元素。
  • 当(i + gap)达到n时停止。

下面是上述方法的实现:

Java

// Java program for the above approach
import java.io.*;
import java.util.*;
 
class InPlaceMerge {
 
    // Calculating next gap
    private static int nextGap(int gap)
    {
        if (gap <= 1)
            return 0;
        return (int)Math.ceil(gap / 2.0);
    }
 
    // Function for swapping
    private static void swap(int[] nums, int i, int j)
    {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
 
    // Merging the subarrays using shell sorting
    // Time Complexity: O(nlog n)
    // Space Complexity: O(1)
    private static void inPlaceMerge(int[] nums, int start,
                                     int end)
    {
        int gap = end - start + 1;
        for (gap = nextGap(gap); gap > 0;
             gap = nextGap(gap)) {
            for (int i = start; i + gap <= end; i++) {
                int j = i + gap;
                if (nums[i] > nums[j])
                    swap(nums, i, j);
            }
        }
    }
 
    // merge sort makes log n recursive calls
    // and each time calls merge()
    // which takes nlog n steps
    // Time Complexity: O(n*log n + 2((n/2)*log(n/2)) +
    // 4((n/4)*log(n/4)) +.....+ 1)
    // Time Complexity: O(logn*(n*log n))
    // i.e. O(n*(logn)^2)
    // Space Complexity: O(1)
    private static void mergeSort(int[] nums, int s, int e)
    {
        if (s == e)
            return;
 
        // Calculating mid to slice the
        // array in two halves
        int mid = (s + e) / 2;
 
        // Recursive calls to sort left
        // and right subarrays
        mergeSort(nums, s, mid);
        mergeSort(nums, mid + 1, e);
        inPlaceMerge(nums, s, e);
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        int[] nums = new int[] { 12, 11, 13, 5, 6, 7 };
        mergeSort(nums, 0, nums.length - 1);
        System.out.println(Arrays.toString(nums));
    }
}
输出
[5, 6, 7, 11, 12, 13]

时间复杂度: O(log n * nlog n)

注意: mergeSort方法进行log n次递归调用,每次调用merge都需要nlog n时间来合并2个排序的子数组