归并排序算法实现 (迭代和递归)

合集下载

必备算法:递归!无论你是前端开发,还是后端开发,都需要掌握它!

必备算法:递归!无论你是前端开发,还是后端开发,都需要掌握它!

必备算法:递归!⽆论你是前端开发,还是后端开发,都需要掌握它!递归是⼀种⾮常重要的算法思想,⽆论你是前端开发,还是后端开发,都需要掌握它。

在⽇常⼯作中,统计⽂件夹⼤⼩,解析xml⽂件等等,都需要⽤到递归算法。

它太基础太重要了,这也是为什么⾯试的时候,⾯试官经常让我们⼿写递归算法。

本⽂呢,将跟⼤家⼀起深⼊挖掘⼀下递归算法~什么是递归?递归,在计算机科学中是指⼀种通过重复将问题分解为同类的⼦问题⽽解决问题的⽅法。

简单来说,递归表现为函数调⽤函数本⾝。

在知乎看到⼀个⽐喻递归的例⼦,个⼈觉得⾮常形象,⼤家看⼀下:❝递归最恰当的⽐喻,就是查词典。

我们使⽤的词典,本⾝就是递归,为了解释⼀个词,需要使⽤更多的词。

当你查⼀个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第⼆个词,可惜,第⼆个词⾥仍然有不懂的词,于是查第三个词,这样查下去,直到有⼀个词的解释是你完全能看懂的,那么递归⾛到了尽头,然后你开始后退,逐个明⽩之前查过的每⼀个词,最终,你明⽩了最开始那个词的意思。

❞来试试⽔,看⼀个递归的代码例⼦吧,如下:递归的特点实际上,递归有两个显著的特征,终⽌条件和⾃⾝调⽤:✿⾃⾝调⽤:原问题可以分解为⼦问题,⼦问题和原问题的求解⽅法是⼀致的,即都是调⽤⾃⾝的同⼀个函数。

✿终⽌条件:递归必须有⼀个终⽌的条件,即不能⽆限循环地调⽤本⾝。

结合以上demo代码例⼦,看下递归的特点:递归与栈的关系其实,递归的过程,可以理解为出⼊栈的过程的,这个⽐喻呢,只是为了⽅便读者朋友更好理解递归哈。

以上代码例⼦计算sum(n=3)的出⼊栈图如下:为了更容易理解⼀些,我们来看⼀下函数sum(n=5)的递归执⾏过程,如下:✿计算sum(5)时,先sum(5)⼊栈,然后原问题sum(5)拆分为⼦问题sum(4),再⼊栈,直到终⽌条件sum(n=1)=1,就开始出栈。

✿ sum(1)出栈后,sum(2)开始出栈,接着sum(3)。

✿最后呢,sum(1)就是后进先出,sum(5)是先进后出,因此递归过程可以理解为栈出⼊过程啦~递归的经典应⽤场景哪些问题我们可以考虑使⽤递归来解决呢?即递归的应⽤场景⼀般有哪些呢?✿阶乘问题✿⼆叉树深度✿汉诺塔问题✿斐波那契数列✿快速排序、归并排序(分治算法体现递归)✿遍历⽂件,解析xml⽂件递归解题思路解决递归问题⼀般就三步曲,分别是:✿第⼀步,定义函数功能✿第⼆步,寻找递归终⽌条件✿第⼆步,递推函数的等价关系式这个递归解题三板斧理解起来有点抽象,我们拿阶乘递归例⼦来喵喵吧~1、定义函数功能定义函数功能,就是说,你这个函数是⼲嘛的,做什么事情,换句话说,你要知道递归原问题是什么呀?⽐如你需要解决阶乘问题,定义的函数功能就是n的阶乘,如下:2、寻找递归终⽌条件递归的⼀个典型特征就是必须有⼀个终⽌的条件,即不能⽆限循环地调⽤本⾝。

设计两个有序单链表的合并排序算法

设计两个有序单链表的合并排序算法

设计两个有序单链表的合并排序算法有序单链表的合并排序,是一种高效的排序算法,可以在较短的时间内对大量数据进行排序。

这种排序算法的核心在于将两个有序的单链表合并成一个有序的单链表,然后再对整个链表进行排序。

合并排序算法的基本原理是分治法。

将需要排序的数组不断地分解成两个子数组,直到每个子数组只包含一个元素为止。

然后再将这些子数组两两合并,直到整个数组被合并成一个有序的数组为止。

这里介绍两个有序单链表的合并排序算法,它们分别是迭代算法和递归算法。

1. 迭代算法迭代算法是一种通用的算法,它的思路是利用循环结构来重复执行一段相同或相似的代码,从而解决一类问题。

对于有序单链表的合并排序,迭代算法的基本思路是将两个有序单链表的元素依次比较,然后将较小的元素加入到新的链表中,直到两个链表中的元素全部被加入到新链表中为止。

以下是迭代算法的具体实现过程:```// 合并两个有序单链表Node* mergeList(Node* head1, Node* head2) { // 新建一个头结点Node* dummy = new Node(-1);// 定义两个指针,分别指向两个链表的头结点 Node* p = head1;Node* q = head2;// 定义一个指针,指向新链表的最后一个节点 Node* curr = dummy;// 循环比较两个链表中的元素while (p != nullptr && q != nullptr) {if (p->val <= q->val) {curr->next = p;p = p->next;} else {curr->next = q;q = q->next;}curr = curr->next;}// 将剩余的元素加入到新链表中curr->next = p != nullptr ? p : q;// 返回新链表的头结点return dummy->next;}// 归并排序Node* mergeSort(Node* head) {if (head == nullptr || head->next == nullptr) {return head;}// 定义两个指针,一个快指针每次走两步,一个慢指针每次走一步 Node* slow = head;Node* fast = head->next;while (fast != nullptr && fast->next != nullptr) {slow = slow->next;fast = fast->next->next;}// 将链表分成两部分Node* head1 = head;Node* head2 = slow->next;slow->next = nullptr;// 分别对两部分链表进行归并排序head1 = mergeSort(head1);head2 = mergeSort(head2);// 合并两个有序单链表return mergeList(head1, head2);}```2. 递归算法递归算法的思想是将一个大问题分解成若干个小问题,然后逐个解决这些小问题,最终得到大问题的解决方案。

c语言分治法实现合并排序算法

c语言分治法实现合并排序算法

c语言分治法实现合并排序算法在计算机科学中,分治算法是一种将问题划分为较小子问题,然后将结果合并以解决原始问题的算法。

其中,合并排序算法就是一种常见的分治算法。

C语言可以使用分治法实现合并排序算法。

该算法的基本思想是将原始数组递归地分成两半,直到每个部分只有一个元素,然后将这些部分合并起来,直到形成一个完整的已排序的数组。

具体实现过程如下:1.首先,定义一个函数merge,该函数将两个已排序的数组合并成一个已排序的数组。

2.然后,定义一个函数merge_sort,该函数使用递归的方式将原始数组分成两个部分,并对每个部分调用merge_sort函数以进行排序。

3.最后,将已排序的两个数组合并到一起,使用merge函数。

以下是C语言代码:void merge(int arr[], int left[], int left_count, int right[], int right_count) {int i = 0, j = 0, k = 0;while (i < left_count && j < right_count) {if (left[i] < right[j]) {arr[k++] = left[i++];} else {arr[k++] = right[j++];}}while (i < left_count) {arr[k++] = left[i++];}while (j < right_count) {arr[k++] = right[j++];}}void merge_sort(int arr[], int size) { if (size < 2) {return;}int mid = size / 2;int left[mid];int right[size - mid];for (int i = 0; i < mid; i++) {left[i] = arr[i];}for (int i = mid; i < size; i++) {right[i - mid] = arr[i];}merge_sort(left, mid);merge_sort(right, size - mid);merge(arr, left, mid, right, size - mid);}int main() {int arr[] = {3, 8, 1, 6, 9, 4, 5, 7, 2};int size = sizeof(arr) / sizeof(arr[0]);merge_sort(arr, size);for (int i = 0; i < size; i++) {printf('%d ', arr[i]);}return 0;}以上代码可以将数组{3, 8, 1, 6, 9, 4, 5, 7, 2}排序成{1, 2, 3, 4, 5, 6, 7, 8, 9}。

python 归并排序详解

python 归并排序详解

python 归并排序详解摘要:一、归并排序的基本概念二、归并排序的算法实现1.递归实现2.非递归实现三、归并排序的优化1.优化空间复杂度2.优化时间复杂度四、归并排序的应用与实战五、总结与拓展正文:一、归并排序的基本概念归并排序(Merge Sort)是一种分治思想的排序算法。

它将待排序的序列分成两部分,分别对这两部分进行递归排序,然后将排序好的两部分合并成一个有序序列。

这个过程一直重复,直到整个序列被排序。

归并排序的时间复杂度为O(nlogn),空间复杂度为O(n)。

二、归并排序的算法实现1.递归实现归并排序的递归实现如下:```pythondef merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = arr[:mid]right_half = arr[mid:]left_half = merge_sort(left_half)right_half = merge_sort(right_half)return merge(left_half, right_half) def merge(left, right):result = []i = j = 0while i < len(left) and j < len(right): if left[i] < right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1result += left[i:]result += right[j:]return resultarr = [38, 27, 43, 3, 9, 82, 10]print(merge_sort(arr))```2.非递归实现归并排序的非递归实现如下:```pythondef merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2left_half = arr[:mid]right_half = arr[mid:]left_half = merge_sort(left_half)right_half = merge_sort(right_half)return merge(left_half, right_half) def merge(left, right):result = []i = j = 0while i < len(left) and j < len(right): if left[i] < right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1result += left[i:]result += right[j:]return resultarr = [38, 27, 43, 3, 9, 82, 10]print(merge_sort(arr))```三、归并排序的优化1.优化空间复杂度归并排序的空间复杂度为O(n),可以通过合并过程中的数组切片实现空间优化,将空间复杂度降低到O(logn)。

多路归并排序算法的过程

多路归并排序算法的过程

多路归并排序算法的过程
多路归并排序算法的过程可以分为以下步骤:
1.分割:将待排序的数据集分割成若干个较小的子集,每个子集都可以用递归的方式进行多路归并排序,直到子集中只有一个或零个元素为止。

2.合并:将已经排好序的子集按照升序或降序的方式合并成一个更大的有序集合,直到所有子集都合并完毕为止。

这个过程大致为:首先将k个归并段中的首元素关键字依次存入b[0]--b[k-1]的叶子结点空间里,然后调用CreateLoserTree创建败者树,创建完毕之后最小的关键字
下标(即所在归并段的序号)便被存入ls[0]中。

然后不断循环:把ls[0]所存最小关键字来自于哪个归并段的序号得
到为q,将该归并段的首元素输出到有序归并段里,然后把
下一个元素关键字放入上一个元素本来所在的叶子结点
b[q]中,调用Adjust顺着b[q]这个叶子结点往上调整败者
树直到新的最小的关键字被选出来,其下标同样存在ls[0]中。

循环这个操作过程直至所有元素被写到有序归并段里。

JS的十大经典算法

JS的十大经典算法

JS的⼗⼤经典算法冒泡排序(Bubble Sort)冒泡排序须知:作为最简单的排序算法之⼀,冒泡排序给我的感觉就像Abandon在单词书⾥出现的感觉⼀样,每次都在第⼀页第⼀位,所以最熟悉。

冒泡排序还有⼀种优化算法,就是⽴⼀个flag,当在⼀趟序列遍历中元素没有发⽣交换,则证明该序列已经有序。

但这种改进对于提升性能来说并没有什么太⼤作⽤。

什么时候最快(Best Cases):当输⼊的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何⽤啊。

)什么时候最慢(Worst Cases):当输⼊的数据是反序时(写⼀个for循环反序输出数据不就⾏了,⼲嘛要⽤你冒泡排序呢,我是闲的吗。

)冒泡排序JavaScript代码实现:1function bubbleSort(arr) {2var len = arr.length;3for (var i = 0; i < len; i++) {4for (var j = 0; j < len - 1 - i; j++) {5if (arr[j] > arr[j+1]) { //相邻元素两两对⽐6var temp = arr[j+1]; //元素交换7 arr[j+1] = arr[j];8 arr[j] = temp;9 }10 }11 }12return arr;13 }选择排序(Selection Sort)选择排序须知:在时间复杂度上表现最稳定的排序算法之⼀,因为⽆论什么数据进去都是O(n²)的时间复杂度。

所以⽤到它的时候,数据规模越⼩越好。

唯⼀的好处可能就是不占⽤额外的内存空间了吧。

选择排序JavaScript代码实现:1function selectionSort(arr) {2var len = arr.length;3var minIndex, temp;4for (var i = 0; i < len - 1; i++) {5 minIndex = i;6for (var j = i + 1; j < len; j++) {7if (arr[j] < arr[minIndex]) { //寻找最⼩的数8 minIndex = j; //将最⼩数的索引保存9 }10 }11 temp = arr[i];12 arr[i] = arr[minIndex];13 arr[minIndex] = temp;14 }15return arr;16 }插⼊排序(Insertion Sort)插⼊排序须知:插⼊排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的⼈都应该能够秒懂。

算法—4.归并排序(自顶向下)

算法—4.归并排序(自顶向下)

算法—4.归并排序(⾃顶向下)1.基本思想将两个有序的数组归并成⼀个更⼤的有序数组,很快⼈们就根据这个操作发明了⼀种简单的递归排序算法:归并排序。

要将⼀个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。

你将会看到,归并排序最吸引⼈的性质是它能够保证将任意长度为N的数组排序所需时间和NlogN成正⽐;它的主要缺点则是它所需的额外空间和N成正⽐。

简单的归并排序如下图所⽰:原地归并的抽象⽅法:实现归并的⼀种直截了当的办法是将两个不同的有序数组归并到第三个数组中,实现的⽅法很简单,创建⼀个适当⼤⼩的数组然后将两个输⼊数组中的元素⼀个个从⼩到⼤放⼊这个数组中。

public void merge(Comparable[] a, int lo, int mid, int hi){int i = lo, j = mid+1;//将a[lo..hi]复制到aux[lo..hi]for (int k = lo; k <= hi; k++) {aux[k] = a[k];}//归并回到a[lo..hi]for (int k = lo; k <= hi; k++) {if(i > mid){a[k] = aux[j++];}else if(j > hi){a[k] = aux[i++];}else if(less(aux[j], aux[i])){a[k] = aux[j++];}else{a[k] = aux[i++];}}}以上⽅法会将⼦数组a[lo..mid]和a[mid+1..hi]归并成⼀个有序的数组并将结果存放在a[lo..hi]中。

在归并时(第⼆个for循环)进⾏了4个条件判断:左半边⽤尽(取右半边的元素)、右半边⽤尽(取左半边的元素)、右半边的当前元素⼩于左半边的当前元素(取右半边的元素)以及右半边的当前元素⼤于等于左半边的当前元素(取左半边的元素)。

2.具体算法/*** ⾃顶向下的归并排序* @author huazhou**/public class Merge extends Model{private Comparable[] aux; //归并所需的辅助数组public void sort(Comparable[] a){System.out.println("Merge");aux = new Comparable[a.length]; //⼀次性分配空间sort(a, 0, a.length - 1);}//将数组a[lo..hi]排序private void sort(Comparable[] a, int lo, int hi){if(hi <= lo){return;}int mid = lo + (hi - lo)/2;sort(a, lo, mid); //将左半边排序sort(a, mid+1, hi); //将右半边排序merge(a, lo, mid, hi); //归并结果}} 此算法基于原地归并的抽象实现了另⼀种递归归并,这也是应⽤⾼效算法设计中分治思想的最典型的⼀个例⼦。

二分归并排序的时间复杂度以及递推式

二分归并排序的时间复杂度以及递推式

一、简介二分归并排序是一种常见的排序算法,它通过将问题分解为子问题,并将子问题的解合并来解决原始问题。

该算法的时间复杂度非常重要,因为它直接影响算法的效率和性能。

在本文中,我们将深入探讨二分归并排序的时间复杂度,并通过递推式来进一步分析算法的性能。

二、二分归并排序的时间复杂度1. 分析在二分归并排序中,时间复杂度可以通过以下三个步骤来分析:- 分解:将原始数组分解为较小的子数组。

- 解决:通过递归调用来对子数组进行排序。

- 合并:将排好序的子数组合并为一个整体有序的数组。

2. 时间复杂度在最坏情况下,二分归并排序的时间复杂度为O(nlogn)。

这是因为在每一层递归中,都需要将数组分解为两个规模近似相等的子数组,并且在每一层递归的最后都需要将这两个子数组合并起来。

可以通过递推式来进一步证明算法的时间复杂度。

3. 递推式分析我们可以通过递推式来分析二分归并排序的时间复杂度。

假设对规模为n的数组进行排序所需的时间为T(n),则可以得到以下递推式:T(n) = 2T(n/2) +其中,T(n/2)表示对规模为n/2的子数组进行排序所需的时间表示将两个子数组合并所需的时间。

根据递推式的定义,我们可以得到二分归并排序的时间复杂度为O(nlogn)。

三、结论与个人观点通过以上分析,我们可以得出二分归并排序的时间复杂度为O(nlogn)。

这意味着该算法在最坏情况下也能保持较好的性能,适用于大规模数据的排序。

我个人认为,二分归并排序作为一种经典的排序算法,其时间复杂度的分析对于理解算法的工作原理和性能至关重要。

通过深入研究递推式,可以更加直观地理解算法的性能表现,为进一步优化算法提供了重要的参考依据。

四、总结在本文中,我们探讨了二分归并排序的时间复杂度,通过分析和递推式的方式深入理解了该算法的性能表现。

通过对时间复杂度的分析,我们对算法的性能有了更深入的认识,并且能够更好地理解算法在实际应用中的表现。

相信通过本文的阅读,读者能够对二分归并排序有更全面、深刻和灵活的理解。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

归并排序算法实现(迭代和递归)\递归实现归并排序的原理如下:
递归分割:
递归到达底部后排序返回:
最终实现排序:
#include <stdio.h>
void merge(int *array, int low, int center, int high) {
if(low >= high) return;
int m = center - low + 1;
int n = high - center;
int L[m], R[n];
for(int i=0; i<m; ++i) L[i] = array[low+i];
for(int i=0; i<n; ++i) R[i] = array[low+i+m];
int i,j,k;
for(i=0,j=0,k=low; i<m && j<n;++k) {
if(L[i] > R[j])
array[k] = R[j++];
else
array[k] = L[i++];
}
while(i<m) array[k++] = L[i++];
while(j<n) array[k++] = R[j++];
}
void m_sort(int *array, int low, int high)
{
int center;
if(low < high) {
center = (low + high)/2;
m_sort(array, low, center);
m_sort(array, center+1, high);
merge(array, low, center, high);
}
}
int main()
{
int array[] = {23, 2, 45, 78, 1, 99, 3};
m_sort(array, 0, 6);
for(int i=0; i<=6; ++i) {
printf("%d\n", array[i]);
}
return 0;
}
时间复杂度:最坏和平均时间复杂度均为O(nlogn)
空间复杂度:上面的实现有些问题,如果真如上面实现,在函数merge中都要使用辅助函数L[m]和R[n],那么归并了logn层,每层消耗空间O(n),则实际消耗O(nlogn),再加上栈空间的消耗O(longn),总的消耗为O(nlogn+logn).该算法不符合空间复杂度为O(n)的要求,所以需要修改,实现如下:
#include <stdio.h>
#include <string>
using std::string;
void merge(int *array, int *extra, int low, int center, int high) {
memcpy(extra+low, array+low, (high-low+1)*sizeof(int)); int i,j,k;
for(i=low,j=(center+1),k=low; i<=center && j<=high;++k) { if(extra[i] > extra[j])
array[k] = extra[j++];
else
array[k] = extra[i++];
}
while(i<=center) array[k++] = extra[i++];
while(j<=high) array[k++] = extra[j++];
}
void m_sort(int *array, int *extra, int low, int high)
{
int center;
if(low < high) {
center = (low + high)/2;
m_sort(array, extra, low, center);
m_sort(array, extra, center+1, high);
merge(array, extra, low, center, high);
}
}
int main()
{
int array[7] = {23, 2, 45, 78, 1, 99, 3};
int extra[7];
m_sort(array, extra, 0, 6);
for(int i=0; i<=6; ++i) {
printf("%d\n", array[i]);
}
return 0;
}
空间复杂度:使用了辅助数组extra,消耗辅助空间O(n),加上栈空间的消耗O(logn),总的空间复杂度为
O(n+logn),如果n无限大,则空间复杂度可以简化为O(n)。

归并排序的迭代实现的原理如下:
实现程序如下:
#include <stdio.h>
#include <string>
using std::string;
void merge(int *sort, int *list, int low, int center, int high)
{
int i,j,k;
for(i=low,j=(center+1),k=low; i<=center && j<=high;++k) {
if(list[i] > list[j])
sort[k] = list[j++];
else
sort[k] = list[i++];
}
while(i<=center) sort[k++] = list[i++];
while(j<=high) sort[k++] = list[j++];
}
void merge_pass(int *a, int *b, int length, int n) {
int i;
for(i=0; i<=n-2*length; i+=2*length) {
int center = (i + i + 2*length - 1)/2;
merge(a, b, i, center, i+2*length-1);
}
if((i+length)<n) {
int center = (i + i + 2*length - 1)/2;
merge(a, b, i, center, n-1);
} else {
while(i<n) {
a[i] = b[i];
++i;
}
}
}
void m_sort(int *array, int n)
{
int extra[n];
int length = 1;
while(length < n) {
merge_pass(extra, array, length, n);
length *= 2;
merge_pass(array, extra, length, n);
length *= 2;
}
}
int main()
{
int data[] = {34, 23, 12, 55, 66, 4, 2, 99, 1, 45, 77, 88, 99, 5};
m_sort(data, 14);
for(int i=0; i<14; ++i) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}
时间复杂度为:O(nlogn)
空间复杂度为:这里的空间复杂度仅为所需的辅助空间,不需栈空间,为O(n)。

相关文档
最新文档