[Sorting] Merge sorting (detailed recursive + non-recursive illustration)

introduction

In this article, we will continue to introduce a sorting algorithm: merge sort.
Merge sort uses the idea of ​​merging, which is to merge two ordered sequences into one ordered sequence. In the previous merging of two ordered linked lists, this idea was used: poke me to see the merge to realize the merger of two ordered linked lists (method 2)

merge sort

train of thought

Merge sort requires a space of the same size as the array for temporary storage of the merged data;

When sorting, first divide the entire array into two equal parts, and then divide it into four parts...and so on, until it can no longer be divided equally (only one element is left in the interval); merge upwards, that is,
merge the two intervals into the corresponding ones in the temporary space position;
then copy the sorted data in the corresponding interval in the temporary array back to the original array;
merge upwards until the sorting is complete.
(It should be noted that the bisection here is not carried out at the same time. In the recursion, it is carried out line by line. After the left side recurses to the head, it returns to the upper level, and continues to recurse to the right side of the second-to-last level, and the right side returns to the end. On the upper level, execute the merge of the bottom left and right intervals. After the merge, return to the penultimate third layer, and then recurse to the right of the layer...)

insert image description here

recursive implementation

Different from the recursive mode of quick sort, quick sort is to recurse downward after completing the operation on the largest interval, and operate on the left and right intervals until the sorting ends; while sorting is to recurse to the smallest interval first, and then merge upwards. Therefore, when implementing quick sorting, the operation of each layer is first, and the function recursion is last; while the quick sort is recursive first, and the operation of each layer is last;

The function has 4 parameters: the original array, the left and right subscripts of the interval, and the temporary space.
The initial parameter passed to the function is the entire array, recursively downward, when begin>=end, return ends the recursion;
create midi, record the middle position of the interval, and then recurse the left and right intervals respectively;

Then realize the operation on the interval:
first use begin1, end1, begin2, end2 to record the beginning and end subscripts of the left and right intervals of the interval to be merged, and use k to record the corresponding starting position of the interval to be merged in the temporary space temp; then the left and right
intervals Merge to the corresponding position of temp;
finally copy the data in the corresponding space of temp back:

insert image description here

For the process of merging, the data in the two intervals are compared in turn, and the smaller elements are tail-inserted into the temporary space:

insert image description here

void _MergeSort(int* a, int begin, int end, int* temp)
{
    
    
	if (begin >= end)
	{
    
    
		return;
	}
	int midi = (begin + end) / 2;
	_MergeSort(a, begin, midi, temp);
	_MergeSort(a, midi+1, end, temp);
	
	int begin1 = begin, end1 = midi;
	int begin2 = midi+1, end2 = end;
	int k = begin1;
	while (begin1 <= end1 && begin2 <= end2)
	{
    
    
		if (a[begin1] < a[begin2])
		{
    
    
			temp[k++] = a[begin1++];
		}
		else
		{
    
    
			temp[k++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
    
    
		temp[k++] = a[begin1++];      
	}
	while (begin2 <= end2)
	{
    
    
		temp[k++] = a[begin2++];
	}

	memcpy(a + begin, temp + begin, sizeof(int)*(end - begin + 1));
}

void MergeSort(int* a, int n) //递归实现
{
    
    
	int left = 0;
	int right = n - 1;
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
    
    
		perror("malloc");
		return;
	}
	_MergeSort(a, left, right, temp);
	free(temp);
}

sort non-recursive

train of thought

When sorting is implemented non-recursively, unlike quick sorting, there is no need to record the intervals of the previous layer. After merging a certain interval, it is enough to return to the original array. So we only need to group the array and merge the values ​​of each two groups. The number of elements in each group starts from 1. After each loop, the number of elements in each group is multiplied by 2 until the loop ends when it is greater than the length of the array;

In each layer of loops, it is necessary to merge all the two intervals in this layer into a group, and transfer these data back to the original array:

(picture)

accomplish

When implementing non-recursion, first dynamically open up a temporary space temp;
then, create a gap as the number of elements in each interval that needs to be merged in each layer, and initialize it to 1;

The outer while loop controls the value of the gap. When the number of elements in the array is greater than or equal to the number of elements, the loop ends; the
inner for loop controls the groups to be merged in each layer (two cells whose number of elements are gaps are merged as a group):
The start position begin1 of the left interval is i, the end position end1 is begin1+gap-1; the start position begin2 of the right interval is end1+1, and the end position end2 is begin2+gap-1; k is the corresponding starting position in temp The subscript of the starting position;
then it needs to be judged whether this group of intervals is out of bounds. Since the for loop only controls i<n, that is, begin1<n, end1, begin2, and end2 may all cross the boundary;
when end1 or begin2 crosses the boundary, it means that this group (left and right) intervals has one less interval, so there is no need to merge. When end2 crosses the boundary, it means that a set of left and right intervals exists and needs to be merged. Just change end2 to the subscript of the last element of the array;

Then merge the left and right intervals to the corresponding positions in temp;
after each group (left and right) intervals are merged, copy the array back to the original array:
[It should be noted that because the previous end2 may be out of bounds, it cannot be directly Copy 2*gap data back, but it should be end2-i+1 data]

insert image description here

void MergeSortNonR2(int* a, int n) //非递归实现(分别转移)
{
    
    
	int gap = 1;
	int* temp = (int*)malloc(sizeof(int) * n);
	if (temp == NULL)
	{
    
    
		perror("malloc");
		return;
	}

	while (gap < n)
	{
    
    
		for (int i = 0; i < n; i += (gap * 2))//每层
		{
    
    
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = end1 + 1, end2 = begin2 + gap - 1;
			int k = begin1;

			//判断是否越界(如果越界,直接break跳过归并)
			if (end1 >= n || begin2 >= n)
			{
    
    
				break;
			}
			else if (end2 >= n)
			{
    
    
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
    
    
				if (a[begin1] < a[begin2])
				{
    
    
					temp[k++] = a[begin1++];
				}
				else
				{
    
    
					temp[k++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
    
    
				temp[k++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
    
    
				temp[k++] = a[begin2++];
			}

			memcpy(a + i, temp + i, (end2 - i + 1) * sizeof(int));//每次归并都转移
		}

		gap *= 2;//走向下一层
	}
}

Summarize

At this point, the content about merge sorting has been introduced, and the explanation of the sorting algorithm has also come to an end.
If you think that I have not introduced a certain part clearly or there is a problem with a certain part, you are welcome to raise it in the comment area.

If this article is helpful to you, I hope it will be connected with one click

Hope to make progress together with you

Guess you like

Origin blog.csdn.net/weixin_73450183/article/details/130319055