合并排序技术使用“分而治之”策略。在这种技术中,将要排序的数据集被划分为较小的单元以对其进行排序。
例如,如果要使用mergesort对数组进行排序,则该数组将围绕其中间元素分成两个子数组。将这两个子数组进一步划分为较小的单元,直到每个单元只有1个元素。
划分完成后,该技术通过比较每个元素并在合并时对它们进行排序来合并这些单个单元。这样,当整个数组合并回去时,我们得到了一个排序数组。
在本教程中,我们将全面讨论该排序技术的所有细节,包括其算法和伪代码以及该技术在Java中的实现。
Java中的MergeSort算法
以下是该技术的算法。
#1)声明一个长度为N的数组myArray
#2)检查N = 1,myArray是否已排序
#3)如果N大于1,
- 设置左= 0,右= N-1
- 计算中间=(左+右)/ 2
- 调用子例程merge_sort(myArray,left,middle)=>这样对数组的前半部分进行排序
- 调用子例程merge_sort(myArray,middle + 1,right)=>这将对数组的后半部分进行排序
- 调用子例程合并(myArray,左,中,右)以合并在上述步骤中排序的数组。
#4)退出
从算法步骤中可以看出,该数组在中间分为两个。然后我们递归地对数组的左半部分和右半部分进行排序。一旦我们分别对两个半部分进行排序,它们就会合并在一起以获得一个排序后的数组。
合并排序伪代码
让我们看一下Mergesort技术的伪代码。正如已经讨论过的,因为这是一种“分而治之”的技术,我们将介绍用于划分数据集然后合并排序的数据集的例程。
procedure mergesort( var intarray as array )
if
( n ==
1
)
return
intarray
var lArray as array = intarray[
0
] ... intarray [n/
2]
var rArray as array = intarray [n/
2
+
1] ... intarray [n]
lArray = mergesort(lArray )
rArray = mergesort(rArray )
return
merge(lArray, rArray )
end procedure
procedure merge( var l_array as array, var r_array as array )
var result as array
while
(l_array and r_array have elements )
if
(l_array [
0
] > r_array [
0] )
add r_array [
0] to the end of result
remove r_array [
0] from r_array
else
add l_array [
0] to the end of result
remove l_array [
0] from l_array
end if
end while
while (l_array has elements )
add l_array [ 0 ] to the end of result
remove l_array [ 0 ] from l_array
end while
while (r_array has elements )
add r_array [ 0 ] to the end of result
remove r_array [ 0 ] from r_array
end while
return result
end procedure
|
在上面的伪代码中,我们有两个例程,即Mergesort和merge。例程Mergesort将输入数组拆分为易于排序的单个数组。然后,它调用合并例程。
合并例程合并各个子数组,并返回结果排序后的数组。看到用于Merge排序的算法和伪代码之后,现在让我们使用一个示例来说明此技术。
MergeSort图
考虑使用此技术将要排序的以下数组。
现在根据Merge排序算法,我们将该数组的中间元素拆分为两个子数组。然后,我们将继续将子数组拆分为较小的数组,直到在每个数组中获得单个元素为止。
一旦每个子数组中只有一个元素,我们将合并这些元素。合并时,我们比较元素并确保它们在合并数组中的顺序正确。因此,我们一直在努力以获取已排序的合并数组。
该过程如下所示:
如上图所示,我们看到该数组被重复分割,然后合并以获得排序后的数组。考虑到这一概念,让我们继续使用Java编程语言实现Mergesort。
合并Java中的排序实现
我们可以使用两种方法在Java中实现该技术。
迭代合并排序
这是一种自下而上的方法。每个元素的子数组都经过排序和合并以形成两个元素的数组。然后将这些数组合并以形成四元素数组,依此类推。这样,通过向上构建已排序的数组。
下面的Java示例显示了迭代的Merge Sort技术。
import java.util.Arrays;
class Main
{
// merge arrays : intArray[start...mid] and intArray[mid+1...end]
public static void merge( int [] intArray, int [] temp, int start, int mid, int end)
{
int k = start, i = start, j = mid + 1 ;
// traverse through elements of left and right arrays
while (i <= mid && j <= end) {
if (intArray[i] < intArray[j]) {
temp[k++] = intArray[i++];
} else {
temp[k++] = intArray[j++];
}
}
// Copy remaining elements
while (i <= mid) {
temp[k++] = intArray[i++];
}
// copy temp array back to the original array to reflect sorted order
for (i = start; i <= end; i++) {
intArray[i] = temp[i];
}
}
// sorting intArray[low...high] using iterative approach
public static void mergeSort( int [] intArray)
{
int low = 0 ;
int high = intArray.length - 1 ;
// sort array intArray[] using temporary array temp
int [] temp = Arrays.copyOf(intArray, intArray.length);
// divide the array into blocks of size m
// m = [1, 2, 4, 8, 16...]
for ( int m = 1 ; m <= high - low; m = 2 *m)
{
for ( int i = low; i < high; i += 2 *m)
{
int start = i;
int mid = i + m - 1 ;
int end = Integer.min(i + 2 * m - 1 , high);
//call merge routine to merge the arrays
merge(intArray, temp, start, mid, end);
}
}
}
public static void main(String[] args)
{
//define array to be sorted
int [] intArray = { 10 , 23 ,- 11 , 54 , 2 , 9 ,- 10 , 45 };
//print the original array
System.out.println( "Original Array : " + Arrays.toString(intArray));
//call mergeSort routine
mergeSort(intArray);
//print the sorted array
System.out.println( "Sorted Array : " + Arrays.toString(intArray));
}
}
|
输出:
原始数组:[10、23,-11、54、2、9,-10、45]
排序数组:[-11,-10、2、9、10、23、45、54]
递归合并排序
这是一种自上而下的方法。在这种方法中,将要排序的数组分解为较小的数组,直到每个数组仅包含一个元素。然后分类变得容易实现。
以下Java代码实现了Merge排序技术的递归方法。
import java.util.Arrays;
public class Main {
public static void merge_Sort( int [] numArray) {
//return if array is empty
if (numArray == null ) {
return ;
}
if (numArray.length > 1 ) {
int mid = numArray.length / 2 ; //find mid of the array
// left half of the array
int [] left = new int [mid];
for ( int i = 0 ; i < mid; i++) {
left[i] = numArray[i];
}
// right half of the array
int [] right = new int [numArray.length - mid];
for ( int i = mid; i < numArray.length; i++) {
right[i - mid] = numArray[i];
}
merge_Sort(left); //call merge_Sort routine for left half of the array
merge_Sort(right); // call merge_Sort routine for right half of the array
int i = 0 ;
int j = 0 ;
int k = 0 ;
// now merge two arrays
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
numArray[k] = left[i];
i++;
}
else {
numArray[k] = right[j];
j++;
}
k++;
}
// remaining elements
while (i < left.length) {
numArray[k] = left[i];
i++;
k++;
}
while (j < right.length) {
numArray[k] = right[j];
j++;
k++;
}
}
}
public static void main(String[] args) {
int numArray[] = { 10 , 23 , - 11 , 54 , 2 , 9 , - 10 , 45 };
int i= 0 ;
//print original array
System.out.println( "Original Array:" + Arrays.toString(numArray));
//call merge_Sort routine to sort arrays recursively
merge_Sort(numArray);
//print the sorted array
System.out.println( "Sorted array:" + Arrays.toString(numArray));
}
}
|
输出:
原始数组:[10、23,-11、54、2、9,-10、45]
排序数组:[-11,-10、2、9、10、23、45、54]
在下一节中,让我们从数组切换,并使用该技术对链接列表和数组列表数据结构进行排序。
使用Java合并对链接列表进行排序
Mergesort技术是对链表进行排序的首选方法。由于涉及到的链表主要是顺序访问,因此其他排序技术的效果不佳。
下面的程序使用此技术对链接列表进行排序。
import java.util.*;
// A singly linked list node
class Node
{
int data;
Node next;
Node( int data, Node next) {
this .data = data;
this .next = next;
}
};
class Main {
//two sorted linked list are merged together to form one sorted linked list
public static Node Sorted_MergeSort(Node node1, Node node2) {
//return other list if one is null
if (node1 == null )
return node2;
else if (node2 == null )
return node1;
Node result;
// Pick either node1 or node2, and recur
if (node1.data <= node2.data) {
result = node1;
result.next = Sorted_MergeSort(node1.next, node2);
}
else {
result = node2;
result.next = Sorted_MergeSort(node1, node2.next);
}
return result;
}
//splits the given linked list into two halves
public static Node[] FrontBackSplit(Node source) {
// empty list
if (source == null || source.next == null ) {
return new Node[]{ source, null } ;
}
Node slow_ptr = source;
Node fast_ptr = source.next;
// Advance 'fast' two nodes, and advance 'slow' one node
while (fast_ptr != null ) {
fast_ptr = fast_ptr.next;
if (fast_ptr != null ) {
slow_ptr = slow_ptr.next;
fast_ptr = fast_ptr.next;
}
}
// split the list at slow_ptr just before mid
Node[] l_list = new Node[]{ source, slow_ptr.next };
slow_ptr.next = null ;
return l_list;
}
// use Merge sort technique to sort the linked list
public static Node Merge_Sort(Node head) {
// list is empty or has single node
if (head == null || head.next == null ) {
return head;
}
// Split head into 'left' and 'right' sublists
Node[] l_list = FrontBackSplit(head);
Node left = l_list[ 0 ];
Node right = l_list[ 1 ];
// Recursively sort the sublists
left = Merge_Sort(left);
right = Merge_Sort(right);
// merge the sorted sublists
return Sorted_MergeSort(left, right);
}
// function to print nodes of given linked list
public static void printNode(Node head) {
Node node_ptr = head;
while (node_ptr != null ) {
System.out.print(node_ptr.data + " -> " );
node_ptr = node_ptr.next;
}
System.out.println( "null" );
}
public static void main(String[] args) {
// input linked list
int [] l_list = { 4 , 1 , 6 , 2 , 7 , 3 , 8 };
Node head = null ;
for ( int key: l_list) {
head = new Node(key, head);
}
//print the original list
System.out.println( "Original Linked List: " );
printNode(head);
// sort the list
head = Merge_Sort(head);
// print the sorted list
System.out.println( "\nSorted Linked List:" );
printNode(head);
}
}
|
输出:
原始链接列表:
8-> 3-> 7-> 2-> 6-> 1-> 4-> null已
排序链接列表:
1-> 2-> 3-> 4-> 6-> 7-> 8- > null
在Java中使用合并对ArrayList进行排序
像数组和链接列表一样,我们也可以使用此技术对ArrayList进行排序。我们将使用类似的例程以递归方式划分ArrayList,然后合并子列表。
以下Java代码实现了ArrayList的Merge排序技术。
import java.util.ArrayList;
class Main {
//splits arrayList into sub lists.
public static void merge_Sort(ArrayList<Integer> numList){
int mid;
ArrayList<Integer> left = new ArrayList<>();
ArrayList<Integer> right = new ArrayList<>();
if (numList.size() > 1 ) {
mid = numList.size() / 2 ;
// left sublist
for ( int i = 0 ; i < mid; i++)
left.add(numList.get(i));
//right sublist
for ( int j = mid; j < numList.size(); j++)
right.add(numList.get(j));
//recursively call merge_Sort for left and right sublists
merge_Sort(left);
merge_Sort(right);
//now merge both arrays
merge(numList, left, right);
}
}
private static void merge(ArrayList<Integer> numList, ArrayList<Integer> left, ArrayList<Integer> right){
//temporary arraylist to build the merged list
ArrayList<Integer> temp = new ArrayList<>();
//initial indices for lists
int numbersIndex = 0 ;
int leftIndex = 0 ;
int rightIndex = 0 ;
//traverse left and righ lists for merging
while (leftIndex < left.size() && rightIndex < right.size()) {
if (left.get(leftIndex) < right.get(rightIndex) ) {
numList.set(numbersIndex, left.get(leftIndex));
leftIndex++;
} else {
numList.set(numbersIndex, right.get(rightIndex));
rightIndex++;
}
numbersIndex++;
}
//copy remaining elements from both lists, if any.
int tempIndex = 0 ;
if (leftIndex >= left.size()) {
temp = right;
tempIndex = rightIndex;
}
else {
temp = left;
tempIndex = leftIndex;
}
for ( int i = tempIndex; i < temp.size(); i++) {
numList.set(numbersIndex, temp.get(i));
numbersIndex++;
}
}
public static void main(String[] args) {
//declare an ArrayList
ArrayList<Integer> numList = new ArrayList<>();
int temp;
//populate the ArrayList with random numbers
for ( int i = 1 ; i <= 9 ; i++)
numList.add( ( int )(Math.random() * 50 + 1 ) );
//print original ArrayList of random numbers
System.out.println( "Original ArrayList:" );
for ( int val: numList)
System.out.print(val + " " );
//call merge_Sort routine
merge_Sort(numList);
//print the sorted ArrayList
System.out.println( "\nSorted ArrayList:" );
for ( int ele: numList)
System.out.print(ele + " " );
System.out.println();
}
}
|
输出:
原始ArrayList:
17 40 36 7 6 23 35 2 38
排序后的ArrayList:
2 6 7 17 23 35 36 38 40
经常问的问题
Q#1)可以在没有递归的情况下完成合并排序吗?
答:可以。我们可以执行称为“迭代合并排序”的非递归合并排序。这是一种自下而上的方法,首先将具有单个元素的子数组合并为两个元素的子数组。
然后将这些2个元素的子数组合并为4个元素的子数组,依此类推,使用迭代构造。这个过程一直持续到我们得到一个排序数组为止。
问题#2 )可以在适当的位置进行合并排序吗?
答:合并排序通常不就位。但是我们可以通过使用一些聪明的实现来使其就位。例如,通过在一个位置存储两个元素值。以后可以使用模数和除法将其提取。
Q#3 )什么是三向合并排序?
答:上面我们看到的技术是2向合并排序,其中将数组拆分为两个部分。然后,我们对数组进行排序和合并。
在三向合并排序中,我们没有将数组拆分为两部分,而是将其拆分为三部分,然后进行排序并最终合并。
问题4 ) Mergesort的时间复杂度是多少?
答:在所有情况下,合并排序的总时间复杂度为O(nlogn)。
问#5)在哪里使用“合并”排序?
答:它主要用于以O(nlogn)时间对链表进行排序。它也用于分布式情况下,其中新数据在排序之前或之后进入系统。这也在各种数据库方案中使用。
结论
合并排序是一种稳定的排序,它是通过以下步骤执行的:首先将数据集重复拆分为子集,然后对这些子集进行排序和合并,以形成排序后的数据集。拆分数据集,直到每个数据集都是无关紧要且易于排序的为止。
我们已经看到了排序技术的递归和迭代方法。我们还讨论了使用Mergesort对链接列表和ArrayList数据结构进行排序。