二路归并问题
问题:将两个有序数组 a r r 1 arr1 arr1, a r r 2 arr2 arr2合并为一个新的有序数组 a r r 3 arr3 arr3。
分析:
- 根据选择排序思想,每次循环找到两个数组中的最小元素依次追加到新数组。
- 对于每次循环,需要找到两个数组中剩余元素的最小元素,即 m i n { m i n { a r r 1 } , m i n { a r r 2 } } min\{min\{arr1\},min\{arr2\}\} min{min{arr1},min{arr2}},由于 a r r 1 arr1 arr1和 a r r 2 arr2 arr2均为有序数组,故每次循环中的最小元素必为两个数组剩余元素最左侧元素中的较小者,即 m i n { a r r 1 [ l e f t ] , a r r 2 [ l e f t ] } min\{arr1[left],arr2[left]\} min{arr1[left],arr2[left]}.
- 当其中一个数组无剩余元素,则直接结束循环,由于另一个数组剩余元素依旧有序,直接依次追加到新数组后即可。
- 综上,设 a r r 1 arr1 arr1长度为 m m m, a r r 2 arr2 arr2长度为 n n n,若循环执行 x x x次时结束,可知 x = = m + y x==m+y x==m+y或 x = = n + y x==n+y x==n+y,这里的 y y y表示当循环遍历完成其中一个数组退出时,另一个数组所遍历到的次数,于是循环结束后还剩 n − y n-y n−y或 m − y m-y m−y个元素没有遍历到,前者表示循环是由于 a r r 1 arr1 arr1的遍历结束而终止的,故循环执行次数为 m + y m+y m+y,总计遍历次数为 m + y + n − y = = m + n m+y+n-y==m+n m+y+n−y==m+n;后者表示循环是由于 a r r 2 arr2 arr2的遍历结束而终止的,故循环执行次数为 n + y n+y n+y,总计遍历次数为 n + y + m − y = = n + m n+y+m-y==n+m n+y+m−y==n+m,两种情况遍历次数一致,故该算法时间复杂度为 O ( m + n ) O(m+n) O(m+n).
代码:
-
c++
void two_merge_sort(vector<int>& arr1, vector<int>& arr2, vector<int>& arr3) { int j = 0, k = 0; // j,k始终指向两个数组剩余元素最左侧 int size1 = arr1.size(), size2 = arr2.size(); while (j < size1 && k < size2) { if (arr1[j] < arr2[k]) { // 找到arr1和arr2中最小元素 arr3.push_back(arr1[j]); j++; } else { arr3.push_back(arr2[k]); k++; } } // 当循环执行arr2.size()次跳出,将arr1剩余元素追加到arr3后 for (; j < size1; j++) { arr3.push_back(arr1[j]); } // 当循环执行arr1.size()次跳出,将arr2剩余元素追加到arr3后 for (; k < size2; k++) { arr3.push_back(arr2[k]); } }
-
java
public class TwoMergeSort{ public void two_merge_sort(int[] arr1, int[] arr2, int[] arr3){ int i = 0, j = 0, k = 0; // j,k始终指向两个数组剩余元素最左侧 while (j < arr1.length && k < arr2.length){ if (arr1[j] < arr2[k]){ // 找到arr1和arr2中最小元素 arr3[i] = arr1[j]; j++; } else { arr3[i] = arr2[k]; k++; } i++; } // 当循环执行arr2.length次跳出,将arr1剩余元素追加到arr3后 for (; j < arr1.length; j++, i++) arr3[i] = arr1[j]; // 当循环执行arr1.length次跳出,将arr2剩余元素追加到arr3后 for (; k < arr2.length; k++, i++) arr3[i] = arr2[k]; } }
-
python
class TwoMergeSort: def two_merge_sort(self, arr1: list, arr2: list, arr3: list)->None: j = 0 # j,k始终指向两个数组剩余元素最左侧 k = 0 while j < len(arr1) and k < len(arr2): if arr1[j] < arr2[k]: # 找到arr1和arr2中最小元素 arr3.append(arr1[j]) j += 1 else : arr3.append(arr2[k]) k += 1 # 当循环执行arr2.length次跳出,将arr1剩余元素追加到arr3后 while j < len(arr1): arr3.append(arr1[j]) j += 1 # 当循环执行arr1.length次跳出,将arr2剩余元素追加到arr3后 while k < len(arr2): arr3.append(arr2[k]) k += 1
归并排序
问题:给定一个无序数组 a r r arr arr,排序后返回。
分析:
- 基于二路归并思想,将 a r r arr arr不断递归二分,然后借助二路归并算法进行排序。
- 设 a r r arr arr长度为 n n n,则二分后两边长度为 n / 2 n/2 n/2,由二路归并可知对两个长度为 n / 2 n/2 n/2的数组合并时间复杂度为 O ( n / 2 + n / 2 ) = O ( n ) O(n/2+n/2) = O(n) O(n/2+n/2)=O(n),同时合并需要执行次数即为递归拆分的次数,构造递归树或根据主定理法或类比二分查找可以知道,递归的时间复杂度为 O ( l o g n ) O(logn) O(logn),故该算法的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn).
代码:
-
c++
class MergeSort { private: vector<int> temp; // 用于排序中动态存放排序元素 void _merge_sort(vector<int>& arr, int left, int right) { // 内部递归实现 if (left + 1 == right) return; int mid = (left + right) / 2; _merge_sort(arr, left, mid); _merge_sort(arr, mid, right); int j = left, k = mid; while (j < mid && k < right) { if (arr[j] < arr[k]) { temp.push_back(arr[j]); j++; } else { temp.push_back(arr[k]); k++; } } for (; j < mid; j++) // 剩余元素追加 temp.push_back(arr[j]); for (; k < right; k++) // 剩余元素追加 temp.push_back(arr[k]); for (int m = left, n = 0; m < right; m++, n++) { // 替换原有元素为已排序元素 arr[m] = temp[n]; } temp.clear(); } public: void merge_sort(vector<int>& arr) { // 外部调用接口 temp.clear(); _merge_sort(arr, 0, arr.size()); } };
-
java
public class MergeSort{ private int[] temp = null; // 临时存放排序元素,避免重复开辟空间 private void _merge_sort(int[] arr, int left, int right) { // 内部递归实现 if (left + 1 == right) return; int mid = (left + right) / 2; this._merge_sort(arr, left, mid); this._merge_sort(arr, mid, right); int i = 0, j = left, k = mid; while (j < mid && k < right) { if (arr[j] < arr[k]) { temp[i] = arr[j]; j++; } else { temp[i] = arr[k]; k++; } i++; } for (; j < mid; j++, i++) // 追加剩余元素 temp[i] = arr[j]; for (; k < right; k++, i++) temp[i] = arr[k]; for (int m = 0, n = left; n < right; m++, n++) { // 替换已排序元素 arr[n] = temp[m]; } } public void merge_sort(int[] arr) { // 外部调用接口 this.temp = new int[arr.length]; this._merge_sort(arr, 0, arr.length); temp = null; } }
-
python
class MergeSort: def __init__(self): self.__temp = [] # 临时存放排序元素,避免重复开辟空间 def merge_sort(self, arr: list) -> None: # 外部调用接口 self.__merge_sort(arr, 0, len(arr)) self.__temp.clear() def __merge_sort(self, arr: list, left: int, right: int) -> None: if left + 1 == right: return None mid = (left + right) // 2 self.__merge_sort(arr, left, mid) self.__merge_sort(arr, mid, right) j = left k = mid while j < mid and k < right: if arr[j] < arr[k]: self.__temp.append(arr[j]) j += 1 else : self.__temp.append(arr[k]) k += 1 while j < mid: # 追加剩余元素 self.__temp.append(arr[j]) j += 1 while k < right: self.__temp.append(arr[k]) k += 1 arr[left:right] = self.__temp[:] # 替换已排序元素 self.__temp.clear()