[Merge sort] Recursive and non-recursive implementation principles of merge sort (C language implementation)


foreword

Merge sort (MERGE-SORT) is an effective sorting algorithm based on the merge operation, which is a very typical application of divide and conquer (Divide and Conquer). Combine the ordered subsequences to obtain a completely ordered sequence; that is, first make each subsequence in order, and then make the subsequence segments in order. Merging two sorted lists into one sorted list is called a two-way merge. Merge sort core steps:

insert image description here

1. Recursive method

insert image description here
insert image description here

void _MergeSort(int* a, int begin, int end, int* tmp) {
    
    
	if (begin >= end) {
    
    
		//递归结束条件
		return;
	}
	int mid = (begin + end) / 2;
	//先找到中间的位置
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);
	//利用分治的思想进行分解,直到每一组只有一个元素的时候
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	//规定要排序两组的开始与结束
	//我们合并的主要思想就是在两组有序数组里
	//依次选出最小的放入临时数组tmp里,
	//然后当两组元素放入完毕后,tmp里面就是之前两组数据的有序合并
	int i = begin;
	//i来记录这次合并的最开始的元素
	while (begin1 <= end1 && begin2 <= end2) {
    
    
		if (a[begin1] < a[begin2]) {
    
    
			tmp[i++] = a[begin1++];
		}
		else {
    
    
			tmp[i++] = a[begin2++];
		}
	}
	//循环比较结束可能第一组的元素都比第二组大
	//这样当第二组全放入tmp里面以后,还需要放第一组的元素
	//第一组元素有剩余:
	while (begin1 <= end1) {
    
    
		tmp[i++] = a[begin1++];
	}
	//第二组元素有剩余:
	while (begin2 <= end2) {
    
    
		tmp[i++] = a[begin2++];
	}
	//将此次排好序的数组tmp拷贝到原数组里面
	//从begin位置开始
	memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));

}

void MergeSort(int* a, int n) {
    
    //主要部分
	int* tmp = (int*)malloc(sizeof(int) * n);
	//建立临时的数组
	if (tmp == NULL) {
    
    
		perror("malloc");
		return;
	}
	//begin为0,end为n-1,这里指的是下标
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

2. Non-recursive method

The main idea of ​​non-recursive sorting is actually decomposing and then merging, but here we don’t need to decompose, and set the number of initial data in each group to 1, that is, gap=1, and then gradually accumulate, two data with a gap of 1 Merge into an ordered array with a gap of 2, and then compare and merge the ordered arrays with a gap=2 into an array with a gap=4, and so on until gap=total number of arrays to be sorted
insert image description here
insert image description here

1. Copy the entire array at once (not recommended)

insert image description here

void MergeSortNonR1(int* a, int n) {
    
    
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
    
    
		perror("malloc");
		return;
	}
	//申请一个临时存放的数组
	int gap = 1;
	//每组数据从1开始进行有序合并
	while (gap < n) {
    
    
		for (int i = 0; i < n; i += 2 * gap) {
    
    //i+=2*gap
			//每次跳过两个gap长度的下标,新的begin1从上一个end2的后面一个位置开始
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;

			int j = i;
			//修改路线
			//因为我们可能要归并的数据元素个数为奇数个这个时候可能会出现有一组
			//相对另一组多或者少一些元素
			//这个时候我们就要修改边界位置
			if (end1 >= n) {
    
    //end1越界了
				end1 = n - 1;
				//end1指向最后一个下标
				end2 = n-1;
				begin2 = n ;
				//begin2与end2就不该存在的,所以让begin2》end2
				//这样就不会进入到下面的循环比较过程
			}
			else if (begin2 >= n) {
    
    
				//end1没越界,begin2越界了
				begin2 = n ;
				end2 = n - 1;
				//begin2与end2就不该存在的,所以让begin2》end2
				//这样就不会进入到下面的循环比较过程
			}
			else if (end2 >= n) {
    
    
				//begin2没越界,end2越界了
				end2 = n - 1;
				//end2指向最后一个下标位置
			}
			while (begin1 <= end1 && begin2 <= end2) {
    
    
				//两个有序数组合并过程
				if (a[begin1] < a[begin2]) {
    
    
					tmp[j++] = a[begin1++];
				}
				else {
    
    
					tmp[j++] = a[begin2++];
				}
			
			}
			//begin1剩余。继续合并
			while (begin1 <= end1) {
    
    
				tmp[j++] = a[begin1++];
			}
			//begin2剩余继续合并
			while (begin2 <= end2) {
    
    
				tmp[j++] = a[begin2++];
			}
		}
		//当gap=m,(m<n)完成以后
		//一把拷贝整个数组到原数组里面
		memcpy(a, tmp, sizeof(int) * n);
		//然后增加每一组的个数,继续进行合并
		gap *= 2;
	}
}

2. Distribution copy array (recommended)

insert image description here

The code is as follows (example):

void MergeSortNonR2(int* a, int n) {
    
    
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL) {
    
    
		perror("malloc");
		return;
	}//申请一个临时存放的数组
	int gap = 1;
	//每组数据从1开始进行有序合并
	while (gap < n) {
    
    
		for (int i = 0; i < n; i += 2 * gap) {
    
    //i+=2*gap
			//每次跳过两个gap长度的下标,新的begin1从上一个end2的后面一个位置开始
			int begin1 = i;
			int end1 = i + gap - 1;
			int begin2 = i + gap;
			int end2 = i + 2 * gap - 1;
			//修改路线
			//因为我们可能要归并的数据元素个数为奇数个这个时候可能会出现有一组
			//相对另一组多或者少一些元素
			//这个时候我们就要修改边界位置

			if (end1 >= n || begin2 >= n) {
    
    
				break;
				//因为为部分拷贝,我两组直接排序完了就拷贝一次
				//所以当出现这种情况的时候直接break
				//如果你直接从tmp里面拷贝,tmp对应位置存的随机值
				//就会覆盖a的值
			}
			if (end2 >= n) {
    
    
				end2 = n - 1;
				//直接改边界
			}
			int j = i;
			while (begin1 <= end1 && begin2 <= end2) {
    
    
				if (a[begin1] < a[begin2]) {
    
    
					tmp[j++] = a[begin1++];
				}
				else {
    
    
					tmp[j++] = a[begin2++];
				}
			}
			//begin1剩余。继续合并
			while (begin1 <= end1) {
    
    
				tmp[j++] = a[begin1++];
			}
			//begin2剩余继续合并
			while (begin2 <= end2) {
    
    
				tmp[j++] = a[begin2++];
			}
			//部分拷贝
			//从begin1位置开始拷贝,拷贝end2与begin1之间排完序的数据
			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
		}
		gap *= 2;
	}
	free(tmp);
}

Summarize

Summary of the characteristics of merge sort:

  1. The disadvantage of merging is that it requires O(N) space complexity, and the thinking of merging and sorting is more to solve the problem of external sorting in the disk.
  2. Time complexity: O(N*logN)
  3. Space complexity: O(N)
  4. Stability: Stable

Guess you like

Origin blog.csdn.net/m0_74774759/article/details/131010148