mergesort算法
A. 归并排序时间复杂度是什么
归并排序(MERGE-SORT)时间复杂度是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
归并算法采用分治法,将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
其实现方式:先把待排序区间以中点二分;接着把左边子区间排序;再把右边子区间排序;最后把左区间和右区间用一次归李厅并操作合并成有序的区间。
合并排序又称为归并排序算法,是比较排序中时间复杂度最低巧磨的算法(已经理论证明)。也孝扰斗是充分利用了分治思想,分而治之,将复杂重复的工作不断进行分解至最小单元,而后逐层向上汇总,就像复杂的行政结构。
B. 归并排序是如何进行的
.example-btn{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.example-btn:hover{color:#fff;background-color:#47a447;border-color:#398439}.example-btn:active{background-image:none}div.example{width:98%;color:#000;background-color:#f6f4f0;background-color:#d0e69c;background-color:#dcecb5;background-color:#e5eecc;margin:0 0 5px 0;padding:5px;border:1px solid #d4d4d4;background-image:-webkit-linear-gradient(#fff,#e5eecc 100px);background-image:linear-gradient(#fff,#e5eecc 100px)}div.example_code{line-height:1.4em;width:98%;background-color:#fff;padding:5px;border:1px solid #d4d4d4;font-size:110%;font-family:Menlo,Monaco,Consolas,"Andale Mono","lucida console","Courier New",monospace;word-break:break-all;word-wrap:break-word}div.example_result{background-color:#fff;padding:4px;border:1px solid #d4d4d4;width:98%}div.code{width:98%;border:1px solid #d4d4d4;background-color:#f6f4f0;color:#444;padding:5px;margin:0}div.code div{font-size:110%}div.code div,div.code p,div.example_code p{font-family:"courier new"}pre{margin:15px auto;font:12px/20px Menlo,Monaco,Consolas,"Andale Mono","lucida console","Courier New",monospace;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;border:1px solid #ddd;border-left-width:4px;padding:10px 15px}排序算法是《唯禅数据结构与算法》中最基本的算法之一。液山渣排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。以下是归并排序算法:
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
作为一种典型的分而治之思想的算法应用,闹悄归并排序的实现由两种方法:
自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);自下而上的迭代;在《数据结构与算法 JavaScript 描述》中,作者给出了自下而上的迭代方法。但是对于递归法,作者却认为:
However, it is not possible to do so in JavaScript, as the recursion goes too deep for the language to handle.
然而,在 JavaScript 中这种方式不太可行,因为这个算法的递归深度对它来讲太深了。
说实话,我不太理解这句话。意思是 JavaScript 编译器内存太小,递归太深容易造成内存溢出吗?还望有大神能够指教。
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn) 的时间复杂度。代价是需要额外的内存空间。
2. 算法步骤申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
设定两个指针,最初位置分别为两个已经排序序列的起始位置;
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
重复步骤 3 直到某一指针达到序列尾;
将另一序列剩下的所有元素直接复制到合并序列尾。
3. 动图演示
代码实现JavaScript实例 function mergeSort(arr) { // 采用自上而下的递归方法var len = arr.length;if(len < 2) { return arr;}var middle = Math.floor(len / 2), left = arr.slice(0, middle), right = arr.slice(middle);return merge(mergeSort(left), mergeSort(right));}function merge(left, right){var result = [];while (left.length && right.length) { if (left[0] <= right[0]) {result.push(left.shift()); } else {result.push(right.shift()); }}while (left.length) result.push(left.shift());while (right.length) result.push(right.shift());return result;}Python实例 def mergeSort(arr):import mathif(len(arr)<2): return arrmiddle = math.floor(len(arr)/2)left, right = arr[0:middle], arr[middle:]return merge(mergeSort(left), mergeSort(right))def merge(left,right):result = []while left and right: if left[0] <= right[0]:result.append(left.pop(0)) else:result.append(right.pop(0));while left: result.append(left.pop(0))while right: result.append(right.pop(0));return resultGo 实例 func mergeSort(arr []int) []int { length := len(arr) if length < 2 {return arr } middle := length / 2 left := arr[0:middle] right := arr[middle:] return merge(mergeSort(left), mergeSort(right))}func merge(left []int, right []int) []int { var result []int for len(left) != 0 && len(right) != 0 {if left[0] <= right[0] {result = append(result, left[0])left = left[1:]} else {result = append(result, right[0])right = right[1:]} } for len(left) != 0 {result = append(result, left[0])left = left[1:] } for len(right) != 0 {result = append(result, right[0])right = right[1:] } return result}Java实例 public class MergeSort implements IArraySort {@Overridepublic int[] sort(int[] sourceArray) throws Exception { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.Of(sourceArray, sourceArray.length); if (arr.length < 2) {return arr; } int middle = (int) Math.floor(arr.length / 2); int[] left = Arrays.OfRange(arr, 0, middle); int[] right = Arrays.OfRange(arr, middle, arr.length); return merge(sort(left), sort(right));}protected int[] merge(int[] left, int[] right) { int[] result = new int[left.length + right.length]; int i = 0; while (left.length > 0 && right.length > 0) {if (left[0] <= right[0]) {result[i++] = left[0];left = Arrays.OfRange(left, 1, left.length);} else {result[i++] = right[0];right = Arrays.OfRange(right, 1, right.length);} } while (left.length > 0) {result[i++] = left[0];left = Arrays.OfRange(left, 1, left.length); } while (right.length > 0) {result[i++] = right[0];right = Arrays.OfRange(right, 1, right.length); } return result;}}PHP实例 function mergeSort($arr){$len = count($arr);if ($len < 2) { return $arr;}$middle = floor($len / 2);$left = array_slice($arr, 0, $middle);$right = array_slice($arr, $middle);return merge(mergeSort($left), mergeSort($right));}function merge($left, $right){$result = [];while (count($left) > 0 && count($right) > 0) { if ($left[0] <= $right[0]) {$result[] = array_shift($left); } else {$result[] = array_shift($right); }}while (count($left)) $result[] = array_shift($left);while (count($right)) $result[] = array_shift($right);return $result;}C实例 int min(int x, int y) {return x < y ? x : y;}void merge_sort(int arr[], int len) {int *a = arr;int *b = (int *) malloc(len * sizeof(int));int seg, start;for (seg = 1; seg < len; seg += seg) { for (start = 0; start < len; start += seg * 2) {int low = start, mid = min(start + seg, len), high = min(start + seg * 2, len);int k = low;int start1 = low, end1 = mid;int start2 = mid, end2 = high;while (start1 < end1 && start2 < end2)b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];while (start1 < end1)b[k++] = a[start1++];while (start2 < end2)b[k++] = a[start2++]; } int *temp = a; a = b; b = temp;}if (a != arr) { int i; for (i = 0; i < len; i++)b[i] = a[i]; b = a;}free(b);}递归版:
实例 void merge_sort_recursive(int arr[], int reg[], int start, int end) {if (start >= end) return;int len = end - start, mid = (len >> 1) + start;int start1 = start, end1 = mid;int start2 = mid + 1, end2 = end;merge_sort_recursive(arr, reg, start1, end1);merge_sort_recursive(arr, reg, start2, end2);int k = start;while (start1 <= end1 && start2 <= end2) reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];while (start1 <= end1) reg[k++] = arr[start1++];while (start2 <= end2) reg[k++] = arr[start2++];for (k = start; k <= end; k++) arr[k] = reg[k];}void merge_sort(int arr[], const int len) {int reg[len];merge_sort_recursive(arr, reg, 0, len - 1);}C++迭代版:
实例 template<typename T> // 整_或浮__皆可使用,若要使用物件(class)_必__定"小于"(<)的_算子功能void merge_sort(T arr[], int len) {T *a = arr;T *b = new T[len];for (int seg = 1; seg < len; seg += seg) { for (int start = 0; start < len; start += seg + seg) {int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);int k = low;int start1 = low, end1 = mid;int start2 = mid, end2 = high;while (start1 < end1 && start2 < end2)b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];while (start1 < end1)b[k++] = a[start1++];while (start2 < end2)b[k++] = a[start2++]; } T *temp = a; a = b; b = temp;}if (a != arr) { for (int i = 0; i < len; i++)b[i] = a[i]; b = a;}delete[] b;}递归版:
实例 void Merge(vector<int> &Array, int front, int mid, int end) {// preconditions:// Array[front...mid] is sorted// Array[mid+1 ... end] is sorted// Copy Array[front ... mid] to LeftSubArray// Copy Array[mid+1 ... end] to RightSubArrayvector<int> LeftSubArray(Array.begin() + front, Array.begin() + mid + 1);vector<int> RightSubArray(Array.begin() + mid + 1, Array.begin() + end + 1);int idxLeft = 0, idxRight = 0;LeftSubArray.insert(LeftSubArray.end(), numeric_limits<int>::max());RightSubArray.insert(RightSubArray.end(),
C. 归并排序
归并排序 (Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为 。1945 年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
这里面提到了两个概念,分别是 分治(法) 和 递归 ,它们是什么呢?
分治法(Divide and Conquer)是基于多路分支递归求和的一种很重要的算法范式。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多凯宏的相同或相似的子问题,再把子问题分成更小的子问题,直到最后子问题可以简单的直接求解,原问题的解就是子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法中的快速排序和归并排序,傅立叶变换中的快速傅立叶变换…
分治模式在每层递归时都有三个步骤:
递归(英语:Recursion),又译为递回, 在数学和计算机科学中,递归指由一种(或多种)简单的基本情况定义的一类对象或方法,并规定其他所有情况都能被还原为其基本情况,如函数的定义中使用函数自身的方法。递归一词还较常用于描述以自相似方法重复事物的过程。 例如,当两面镜子相互之间睁丛近似平行时,镜中嵌套的图像是以无限递归的形式出现的。 也可以理解为自我复制的过程。
归并排序算法完全遵循分治模式,直观上,其操作步骤如下:
当待排序的序列长度为 1 时,递归“开始回升”,在这种情况下无须作任何工作,因为长度为 1 的每个序列都已排好序。
MERGE 的详细工作过程如下:
我们必须证明第 12~17 行 for 循环的第一次迭代之前该循环不变式成立,且在该循环的每次迭代时保持该不变式,当循环终止时,该不变式须提供一种有用的性质来证明算法的正确性。
前面我们分析了分治算法的过程,我们可以把 MERGE 作为归并排序算法中的一个子程序来用。
上面已经对分治法做悉孙樱了正确性证明,归并排序的正确性不言而喻。
分治算法运行时间的递归式来自基本模式的三个步骤,即分解、解决和合并。假设 T(n) 是规模为 n 的一个问题的运行时间。若问题规模足够小,如对某个常量 c,n≤c,则直接求解需要常量时间,可以将其写成 O(1)。假设把原问题分解成 a 个子问题,每个子问题的规模是原问题的 1/b。为了求解一个规模为 n/b 的子问题,需要 T(n/b) 的时间,所以需要 aT(n/b) 的时间来求解 a 个子问题。如果分解问题成子问题需要时间 D(n),合并子问题的解成原问题的解需要时间 C(n),那么得到递归式:
现在我们来讨论归并排序。假定问题规模是 2 的幂(不是 2 的幂时也能正确地工作),归并排序一个元素的时间是常量,当有 n>1 个元素时,分解运行的时间如下:
为了分析归并排序,我们可以将 D(n) 与 C(n) 相加,即把一个 函数与另一个 函数相加,得到的和是一个 n 的线性函数,即 。把它与来自“解决”步骤的项 2T(n/2) 相加,将给出归并排序的最坏情况的运行时间
将递归式重写,得到
其中,常量 c 代表求解规模为 1 的问题所需要的时间以及在分解步骤与合并步骤处理每个数组元素所需要的时间。(相同的常量一般不可能刚好即代表求解规模为 1 的问题的时间又代表分解步骤与合并步骤处理每个数组元素的时间。通过假设 c 为这两个时间的较大者并认为我们的递归式将给出运行时间的一个上界,或者通过假设 c 为这两个时间的较小者并认为我们的递归式将给出运行时间的下界,我们可以暂时回避这个问题。两个界的阶都是 ,合在一起将给出运行时间为 )。
求解递归式的过程如下图所示:
可以看出,树根 cn 通过递归分解,直到规模降为 1 后,每个子问题只要代价 c。分解步骤一共经历了 次,即树高为 层,每层的代价为 cn,因此总代价为 。
上面我们已经知道了,总代价为 ,忽略低阶项和常量 c,归并排序的时间复杂度为 O(nlogn)。
归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间,但是这个申请额外的内存空间,会在合并完成之后释放,因此,在任意时刻,只会有一个临时的内存空间在使用,临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)。
Javascript 递归版
Go
迭代版
递归版
D. 什么叫归并算法
合并排序(MERGE SORT)是又一类不同的排序方法,合并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。它的基本思想就是假设数组A有N个元素,那么可以看成数组A是又N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个 N/2 个长度为2或1的有序子序列,再两两合并,如此重复,值得得到一个长度为N的有序数据序列为止,这种排序方法称为2—路合并排序。
例如数组A有7个数据,分别是: 49 38 65 97 76 13 27,那么采用归并排序算法的操作过程如图7所示:
初始值 [49] [38] [65] [97] [76] [13] [27]
看成由长度为1的7个子序列组成
第一次合并之后 [38 49] [65 97] [13 76] [27]
看成由长度为1或2的4个子序列组成
第二次合并之后 [38 49 65 97] [13 27 76]
看成由长度为4或3的2个子序列组成
第三次合并之后 [13 27 38 49 65 76 97]
合并算法的核心操作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列。合并算法也可以采用递归算法来实现,形式上较为简单,但实用性很差。合并算法的合并次数是一个非常重要的量,根据计算当数组中有3到4个元素时,合并次数是2次,当有5到8个元素时,合并次数是3次,当有9到16个元素时,合并次数是4次,按照这一规律,当有N个子序列时可以推断出合并的次数是X(2 >=N,符合此条件的最小那个X)。
其时间复杂度为:O(nlogn).所需辅助存储空间为:O(n)
归并算法如下:
long merge(long *A,long p,long q,long r)
{
long n1,n2,i,j,k;
long *L,*R;
n1=q-p+1;
n2=r-q;
L=(long *)malloc((n1+2)*sizeof(long));
R=(long *)malloc((n2+2)*sizeof(long));
for(i=1;i<=n1;i++)
L=A[p+i-1];
for(j=1;j<=n2;j++)
R[j]=A[q+j];
L[n1+1]=R[n2+1]=RAND_MAX;
i=j=1;
for(k=p;k<=r;k++)
{
if(L<=R[j])
{
A[k]=L;
i++;
}
else
{
A[k]=R[j];
j++;
}
}
free(L);
free(R);
return 0;
}
long mergesort(long *A,long p,long r)
{
long q;
if(p<r)
{
q=(p+r)/2;
mergesort(A,p,q);
mergesort(A,q+1,r);
merge(A,p,q,r);
}
return 0;
}
E. 中位数算法
楼上才是白痴,自己什么也不懂不要说的别人也是什么也不懂。
就是因为有了你们这种人,世界多花了巨额的代价来多做不必要的工作。
很明显楼主不是你这样的。
实数的排序算法复杂度是o(nlogn),这个中位数可以做到o(n)
下面我来说明这个算法的过程。
算法是基于归并排序(merge-sort)的更改。
把中位数更改为等价的叙述。无序的n个数中的第int(n/2)大的元素。(k=int(n/2))
1.随机化数据,这样可以保证因为输出时候的对称性(可能的顺序输入)而造成的算法退化。
for
(int
i=number.count;i>=0;i--)
swap(number[i],number[random(0,i-1)]);//swap,交换,random,0,k闭区间的随机数。
2.归并排序主过程。
mergesort(a,b,k)//寻找number数组中从下标a到下标b的元素中的第k大的元素。
{
t=number[a];
把这a,b中的元素从排,使a~p-1的元素比t小,p+1~b的元素比t大。number[p]=t;//o(n),这步你构造吧。不是很困难,伪代码不写太多。
//此时比t小元素有p-1-a+1=p-a个,
//分情况,如果k=p-a+1,返回t
//如果k>p-a+1,返回mergesort(p+1,b,k-(p-a+1))
//如果k
评论
0
0
0
加载更多
F. Python实现的几个常用排序算法实例
#encoding=utf-8
importrandom
fromimport
defdirectInsertSort(seq):
"""直接插入排序"""
size=len(seq)
foriinrange(1,size):
tmp,j=seq[i],i
whilej>0andtmp<seq[j-1]:
seq[j],j=seq[j-1],j-1
seq[j]=tmp
returnseq
defdirectSelectSort(seq):
"""直接选择排序"""
size=len(seq)
foriinrange(0,size-1):
k=i;j=i+1
whilej<size:
ifseq[j]<seq[k]:
k=j
j+=1
seq[i],seq[k]=seq[k],seq[i]
returnseq
defbubbleSort(seq):
"""冒泡排序"""
size=len(seq)
foriinrange(1,size):
forjinrange(0,size-i):
ifseq[j+1]<seq[j]:
seq[j+1],seq[j]=seq[j],seq[j+1]
returnseq
def_divide(seq,low,high):
"""快速排序划分函数"""
tmp=seq[low]
whilelow!=high:
whilelow<highandseq[high]>=tmp:high-=1
iflow<high:
seq[low]=seq[high]
low+=1
whilelow<highandseq[low]<=tmp:low+=1
iflow<high:
seq[high]=seq[low]
high-=1
seq[low]=tmp
returnlow
def_quickSort(seq,low,high):
"""快速排序辅助函数"""
iflow>=high:return
mid=_divide(seq,low,high)
_quickSort(seq,low,mid-1)
_quickSort(seq,mid+1,high)
defquickSort(seq):
"""快速排序包裹函数"""
size=len(seq)
_quickSort(seq,0,size-1)
returnseq
defmerge(seq,left,mid,right):
tmp=[]
i,j=left,mid
whilei<midandj<=right:
ifseq[i]<seq[j]:
tmp.append(seq[i])
i+=1
else:
tmp.append(seq[j])
j+=1
ifi<mid:tmp.extend(seq[i:])
ifj<=right:tmp.extend(seq[j:])
seq[left:right+1]=tmp[0:right-left+1]
def_mergeSort(seq,left,right):
ifleft==right:
return
else:
mid=(left+right)/2
_mergeSort(seq,left,mid)
_mergeSort(seq,mid+1,right)
merge(seq,left,mid+1,right)
#二路并归排序
defmergeSort(seq):
size=len(seq)
_mergeSort(seq,0,size-1)
returnseq
if__name__=='__main__':
s=[random.randint(0,100)foriinrange(0,20)]
prints
print" "
printdirectSelectSort((s))
printdirectInsertSort((s))
printbubbleSort((s))
printquickSort((s))
printmergeSort((s))
G. 中位数算法
此题的中位数算法应该是将数据从小到大重新排列。n
是样本数
这里样本数为30.是偶数
则样本中位数m=1/2【(x(15)+x(15+1)】
这里就是m=1/2【x(30/2)+x(30/2+1)】
其中x后面小括号的是下标。我从这上面表示不出来。于是结果就是m=1/2(18+18)=18
明白了么?
中位数的计算主要是排序,然后根据样本数量是奇数还是偶数,确定公式代入公式找到中位数就可以了
H. 归并排序中算法MergeSort()是怎么回事
-MergeSort(R,low,mid);和MergeSort(R,mid+1,high);是对R(low...mid)和R(mid+1...high)进行排序吗??
不是排序,而是分裂。分别对左右两边进行折半分裂,直到只剩下一个元素。归并排序是Divide and Conquer算法,分裂+合并。先递归调用MergeSort分裂直至只剩一个元素,然后调Merge辅助函数合并两个有序数组(只有一个元素的数组也是有序数组)。
-我怎么认为只是把例如R(N)={0,1,3,2},最终分解为0,1,3,2呢??
的确先是分成单个元素的,然后{0}与{1}两个数组通过Merge合并成为{0, 1};{3}与{2}两个数组合并成{2, 3}。然后退到递归上一层,调Merge合并{0, 1}和{2, 3]两个有序数组为{0, 1, 2, 3}。
归并排序的思想就是每次合并两个有序数组。
再举个例子:{5, 3, 6, 1}。
MergeSort分裂为{5, 3}和{6, 1}
MergeSort分裂{5, 3}为{5}, {3};分裂{6, 1}为{6}, {1}
现在递归已经到头,因为继续分的话low>high
所以运行Merge,合并{5}, {3}为{3, 5};合并{6}, {1}为{1, 6}
退到上一层递归,合并{3, 5}和{1, 6}为{1, 3, 5, 6}。
其实真正的排序步骤是在Merge里:怎样合并两个有序数组。
I. 大数据中的归并排序(Merge Sort)
最近在研究大数据的分治思想,很多场景计算得出的中间结果都是“内部有序,外部无序”,这时候就要用到“归衫袭并排序”算法将多个这样的结果集合归并为一个有序的集合。于是就复习了一下“归并排序”。
在大数据场景中或亩兄,数据量远大于计算机的内存,磁盘和网络的IO又是无法解决的瓶颈,就需要用到分治思想。
比如有1000G的中国2020年某电商平台用户消费总金额数据。数据格式是用户名:消费总金额。所有数据都是无序的。现在有1台磁盘5T/内存8G(程序可用内存约为5G多)的计算机。如何将数据按消费总金额从大到小排序输出?
a. 从有序队列中取出第一个元素。
b. 将此元素数据写入输出的文件中,并从此元素所属的数组中的下一个元素放入有序耐耐队列。
c. 重复步骤a,b直到某个数组元素耗尽,从这个数组所属的5G文件中,继续加载25M数据到内存中。
d. 重复步骤a,b,c直到所有5G文件的数据全部加载到内存中,并写入到输出文件。