数据结构与算法之排序(C/C++代码)

排序概述

1.有序性

有序通常分为非递增和非递减,这是比较专业的术语。递增一般是指严格的递增,即后一个元素必须比前一个元素大,不允许相等。那么非递增呢?非递增是指后一个元素必须比前一个元素小,允许相等。其实,非递增就是允许元素相等的递减,同理,非递减就是允许元素相等的递增。

排序是按照关键字进行排列的,一个记录(元素)如果包含多个关键字,就需要指明按照哪个关键字排序。如图9-1所示,排序时需要指明按学号排序,还是按成绩排序。
在这里插入图片描述

2.稳定性

当排序的关键字值相等时,如按成绩排序,如图9-1所示,王斌和李冰的成绩都是72,排序前王斌在李冰的前面,排序后仍然保持王斌在李冰的前面,那么该排序方法是稳定的;如果排序后,王斌在李冰的后面了,那么该排序方法是不稳定的。排序的稳定性是指当关键字相等时,排序前后的位置变化。

3.内部排序和外部排序

内部排序是数据记录在内存中进行排序。外部排序是因排序的数据很大,内存一次不能容纳全部的排序记录,在排序过程中需要访问外存。

4.内部排序算法的分类

内部排序算法根据主要操作又分为插入排序、交换排序、选择排序、归并排序、分配排序五大类
在这里插入图片描述

插入排序

插入排序的思想是每次将一个待排序的记录,按其关键字大小插入已经排好序的数据序列中,保持数据序列仍然有序。将待排序记录插入有序序列的过程中,需要查找插入位置。根据查找方法不同,分为直接插入排序、希尔排序。

直接插入排序

直接插入排序是最简单的排序方法,每次将一个待排序的记录,插入已经排好序的数据序列中,得到一个新的长度增1的有序表
在这里插入图片描述
将待插入元素一个个插入初始已有序部分中的过程,而插入位置的选择遵循了使插入后仍保存有序的原则,具体做法一般是从后往前枚举已有序部分来确定插入位置。

算法步骤

1)设待排序的记录存储在数组r[1…n]中,可以把第一个记录r[1]看作一个有序序列。
2)依次将r[i](i=2, …, n)插入已经排好序的序列r[1…i-1]中,并保持有序性。

算法图解

例如,利用直接插入排序算法对序列{12, 2, 16, 30, 28, 10, 16*, 20, 6, 18}进行非递减排序。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

少废话上代码

#include<stdio.h>

void StraightInsertSort(int a[], int n)
{
    
    
	int i, j;
	for(i = 2; i <= n; i++)
	{
    
    
		if(a[i] < a[i - 1])
		{
    
    
			/*
			a[0]作哨兵 
			*/
			a[0] = a[i];
			a[i] = a[i - 1];
			for(j = i - 2; a[j] > a[0]; j--)
			{
    
    
				a[j + 1] = a[j];
			}
			a[j + 1] = a[0];
		}
	}
}

int main()
{
    
    
	int array[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 52, 33};
	StraightInsertSort(array, 8);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", array[i]);
	}
	printf("\n");
	return 0;
} 

在这里插入图片描述

算法复杂度分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

折半插入排序

void BInsertSort(int a[], int n)
{
    
    
	int i;
	int low, high, mid;
	for(i = 2; i <= n; i++)
	{
    
    
		a[0] = a[i];
		low = 1;
		high = i - 1;
		int j;
		while(low <= high)
		{
    
    
			mid = (low + high) / 2;
			if(a[0] < a[mid])
			{
    
    
				high = mid - 1;
			}
			else
			{
    
    
				low = mid + 1;
			}
		}
			for(j = i - 1; j >= high + 1; j--)
			{
    
    
				a[j + 1] = a[j];
			}
			a[high + 1] = a[0];
	}
} 

完整版调试代码

#include<stdio.h>

void StraightInsertSort(int a[], int n)
{
    
    
	int i, j;
	for(i = 2; i <= n; i++)
	{
    
    
		if(a[i] < a[i - 1])
		{
    
    
			/*
			a[0]作哨兵 
			*/
			a[0] = a[i];
			a[i] = a[i - 1];
			for(j = i - 2; a[j] > a[0]; j--)
			{
    
    
				a[j + 1] = a[j];
			}
			a[j + 1] = a[0];
		}
	}
}

void BInsertSort(int a[], int n)
{
    
    
	int i;
	int low, high, mid;
	for(i = 2; i <= n; i++)
	{
    
    
		a[0] = a[i];
		low = 1;
		high = i - 1;
		int j;
		while(low <= high)
		{
    
    
			mid = (low + high) / 2;
			if(a[0] < a[mid])
			{
    
    
				high = mid - 1;
			}
			else
			{
    
    
				low = mid + 1;
			}
		}
			for(j = i - 1; j >= high + 1; j--)
			{
    
    
				a[j + 1] = a[j];
			}
			a[high + 1] = a[0];
	}
} 

int main()
{
    
    
	int array[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 52, 33};
	StraightInsertSort(array, 8);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", array[i]);
	}
	printf("\n");
	int b[9] = {
    
    0, 9, 6, 3, 24, 52, 33, 36, 7};
	BInsertSort(b, 8);
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", b[i]);
	}
	printf("\n");
	return 0;
} 

在这里插入图片描述
时间复杂度:O(n2)
空间复杂度:O(1)

减少了比较次数,但没有减少移动次数,移动次数与直接插入排序相同
平均性能优于直接插入排序

希尔排序

在直接插入排序中,如果待排序序列的记录个数比较少,而且基本有序,则排序的效率较高。1959年,Donald Shell从“减少记录个数”和“基本有序”两个方面对直接插入排序进行了改进,提出了希尔排序算法。

希尔排序又称“缩小增量排序”,将待排序记录按下标的一定增量分组(减少记录个数),对每组记录使用直接插入排序算法排序(达到基本有序);随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个序列基本有序,再对全部记录进行一次直接插入排序。

算法步骤

在这里插入图片描述

算法图解

例如,利用希尔排序算法对序列{12, 2, 16, 30, 28, 10, 16*, 6, 20, 18}进行非递减排序。
1)初始状态,假设增量序列为{5, 3, 1}。
2)第一趟排序取增量d1=5,所有间隔为5的记录分在一组,分组后如图9-23所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

少废话上代码

#include<stdio.h>

void ShellInsert(int a[], int n, int dk)
{
    
    
	int i, j;
	for(i = dk + 1; i <= n; i++)
	{
    
    
		if(a[i] < a[i - dk])
		{
    
    
			a[0] = a[i];
			for(j = i - dk; j > 0 && a[j] > a[0]; j -= dk)
			{
    
    
				a[j + dk] = a[j];
			}
			a[j + dk] = a[0];
		}	
	}
}

void ShellSort(int a[], int n, int dt[], int t)
{
    
    
	int k; 
	for(k = 0; k < t; k++)
	{
    
    
		ShellInsert(a, n, dt[k]);		
	}	
} 

int main()
{
    
    
	int dt[3] = {
    
    5, 3, 1};
	int array[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 52, 33};
	ShellSort(array, 8, dt, 3);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", array[i]);
	}
	printf("\n");
	return 0;
}

算法复杂度分析

(1)时间复杂度

希尔排序的时间复杂度和增量序列有关,不同的增量序列其时间复杂度不同。遗憾的是,到目前为止还没有人证明哪一种是最好的增量序列。大量的实验结果表明,当n在某个特定范围内,希尔排序的时间复杂度约为O(n1.3),希尔排序时间复杂度的下界是O(nlogn),最坏情况下的时间复杂度为O(n2)。希尔排序没有快速排序算法快,但是比O(n2)复杂度的算法快得多。

(2)空间复杂度

希尔排序在分组进行直接插入排序时使用了一个辅助空间r[0],空间复杂度为O(1)。

(3)稳定性

直接插入排序算法本身是稳定的,但是希尔排序在不同的分组中进行直接插入排序,相同的元素可能在各自的分组中移动,因此两个相等的记录在排序前后的位置顺序有可能会改变。例如,上例中排序前16在16之前,排序后16在16之后,因此希尔排序是不稳定的排序方法。

交换排序

交换的意思是根据两个关键字值的比较结果,不满足次序要求时交换位置。冒泡排序和快速排序是典型的交换排序算法,其中快速排序是目前最快的排序算法。

冒泡排序

冒泡排序是一种最简单的交换排序算法,通过两两比较关键字,如果逆序就交换,使关键字大的记录像泡泡一样冒出来放在尾部。重复执行若干次冒泡排序,最终得到有序序列。

算法图解

在这里插入图片描述
经过第一趟排序后,最大的记录已经冒泡到最后一个位置,第二趟排序不需要再参加。
在这里插入图片描述
在这里插入图片描述

少废话上代码

#include<stdio.h>

void swap(int *a, int *b)
{
    
    
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void BubbleSort(int a[], int n)
{
    
    
	int i, j;
	i = n - 1;
	int flag = 1;
	while(i > 0 && flag)
	{
    
    
		flag = 0;
		for(j = 0; j < i; j++)
		{
    
    
			if(a[j] > a[j + 1])
			{
    
    
				swap(&a[j], &a[j + 1]);
				flag = 1;
			}
		}
		i--;
	}
}

int main()
{
    
    
	int array[8] = {
    
    24, 6, 3, 9, 8, 1, 0, 33};
	BubbleSort(array, 8);
	int i;
	for(i = 0; i < 8; i++)
	{
    
    
		printf("%5d", array[i]);
	}
	printf("\n");
	return 0;
} 

在这里插入图片描述
在最好情况下,冒泡排序时间复杂度为O(n)
在最坏情况下,冒泡排序的时间复杂度为O(n2)
在平均情况下,冒泡排序的时间复杂度也为O(n2)

冒泡排序使用了一些辅助空间,即i、j、temp、flag,空间复杂度为O(1)。

冒泡排序是稳定的排序方法。

快速排序

快速排序(Quicksort)是比较快速的排序方法。快速排序由C. A. R. Hoare在1962年提出。它的基本思想是通过一组排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此使所有数据变成有序序列。

算法思想

快速排序算法是基于分治策略的,其算法思想如下

  • 1)分解:先从数列中取出一个元素作为基准元素。以基准元素为标准,将序列分解为两个子序列,使小于或等于基准元素的子序列在左侧,使大于基准元素的子序列在右侧。
  • 2)治理:对两个子序列进行快速排序。
  • 3)合并:将排好序的两个子序列合并在一起,得到原问题的解。

少废话上代码

#include<stdio.h>

int Partition(int a[], int left, int right)
{
    
    
	int tmp = a[left];
	while(left < right)
	{
    
    
		while(left < right && a[right] >= tmp)
		{
    
    
			right--;
		}
		a[left] = a[right];
		while(left < right && a[left] < tmp)
		{
    
    
			left++;
		}
		a[right] = a[left];
	}
	a[left] = tmp;
	return left;
} 

void QuickSort(int a[], int left, int right)
{
    
    
	int pos;
	if(left < right)
	{
    
    
		pos = Partition(a, left, right);
		QuickSort(a, left, pos - 1);
		QuickSort(a, pos + 1, right);
	}
}


int main()
{
    
    
	int a[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 7, 33};
	QuickSort(a, 1, 8);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", a[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述
快速排序算法平均情况下,时间复杂度为O(nlogn)。

空间复杂度:程序中变量占用了一些辅助空间,这些辅助空间都是常数阶的,递归调用所使用的栈空间是O(logn),空间复杂度为O(logn)。

算法改进(减少交换的次数)

在这里插入图片描述
在这里插入图片描述

#include<stdio.h>

int Partition(int a[], int left, int right)
{
    
    
	int tmp = a[left];
	while(left < right)
	{
    
    
		while(left < right && a[right] >= tmp)
		{
    
    
			right--;
		}
		a[left] = a[right];
		while(left < right && a[left] < tmp)
		{
    
    
			left++;
		}
		a[right] = a[left];
	}
	a[left] = tmp;
	return left;
} 

void swap(int *a, int *b)
{
    
    
	int tmp;
	tmp = *a;
	*a = *b;
	*b = tmp;
}

int Partition2(int a[], int left, int right)
{
    
    
	int u = left;
	int tmp = a[left];
	while(left < right)
	{
    
    
		while(left < right && a[right] >= tmp)
		{
    
    
			right--;
		}
		left += 1;
		while(left < right && a[left] < tmp)
		{
    
    
			left++;
		}
		if(left < right)
		{
    
    
			swap(&a[left++], &a[right--]);
		}
	}
	if(a[left] > tmp)
	{
    
    
		swap(&a[left - 1], &a[u]);
		return left - 1;
	}
	swap(&a[left], &a[u]);
	return left;
}



void QuickSort(int a[], int left, int right)
{
    
    
	int pos;
	if(left < right)
	{
    
    
		pos = Partition2(a, left, right);
		QuickSort(a, left, pos - 1);
		QuickSort(a, pos + 1, right);
	}
}


int main()
{
    
    
	int a[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 7, 33};
	int b[4] = {
    
    0, 49,50,49}; 
	QuickSort(a, 1, 8);
	QuickSort(b, 1, 3);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", a[i]);
	}
	printf("\n");
	
	for(i = 1; i < 4; i++)
	{
    
    
		printf("%5d", b[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

选择排序

选择排序包括简单选择排序和堆排序

简单选择排序

简单选择排序又称为直接选择排序,是一种最简单的选择排序算法,每次从待排序序列中选择一个最小的放在最前面。

算法步骤

在这里插入图片描述

算法图解

例如,利用简单选择排序算法对序列{12, 2, 16, 30, 28, 20, 16*, 6, 10, 18}进行非递减排序。
在这里插入图片描述
在这里插入图片描述

少废话上代码

#include<stdio.h>

void SimpleSelectSort(int a[], int n)
{
    
    
	int i, j, k, tmp;
	for(i = 0; i < n - 1; i++)
	{
    
    
		k = i;
		for(j = i + 1; j < n; j++)
		{
    
    
			if(a[j] < a[k])
			{
    
    
				k = j;
			}
		} 
		if(k != i)
		{
    
    
			tmp = a[i];
			a[i] = a[k];
			a[k] = tmp;
		}
	}
} 

int main()
{
    
    
	int a[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 7, 33};
	SimpleSelectSort(a, 9);
	
	int i;
	for(i = 0; i < 9; i++)
	{
    
    
		printf("%5d", a[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

算法复杂度分析

简单选择排序的时间复杂度为O(n2)。

简单选择排序在交换时使用了一个辅助空间temp,空间复杂度也为O(1)。

稳定性从上面实例中也看出,16和16*排序前后的位置是相反的,因此简单选择排序是不稳定的排序方法。

堆排序

堆排序是一种树形选择排序算法。简单选择排序算法每次选择一个关键字最小的记录需要O(n)的时间,而堆排序选择一个关键字最大的记录只需要O(logn)的时间。

堆可以看作一棵完全二叉树的顺序存储结构。在这棵完全二叉树中,如果每一个节点的值都大于等于左右孩子的值,称为最大堆(大顶堆)。如果每一个节点的值都小于等于左右孩子的值,称为最小堆(小顶堆)。

例如,一个数据元素序列如图9-57所示,其对应的完全二叉树如图9-58所示,该完全二叉树满足最大堆的定义。
在这里插入图片描述
在这里插入图片描述

堆的调整算法步骤

在这里插入图片描述

算法图解

在这里插入图片描述
“下沉”操作:堆顶与左右孩子比较,如果比孩子大,则已调整为堆;如果比孩子小,则与较大的孩子交换;交换到新的位置后,继续向下比较,从根节点一直比较到叶子。

堆的建立

在这里插入图片描述
在这里插入图片描述
2)从最后一个分支节点n/2=5开始调整堆,28比其孩子18大,不需要交换。

3)下标为4的节点调整堆,30比其两个孩子6、10都大,不需要交换。

4)下标为3的节点调整堆,2比其大孩子20小,与较大孩子交换,如图9-62所示。
在这里插入图片描述
在这里插入图片描述
16交换到新位置后继续比较,16比其两个孩子6、10都大,不需要交换,比较到叶子停止。

6)序号为1的节点调整堆,12比其大孩子30小,与较大孩子交换,如图9-64所示。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
思考:构建初始堆为什么要从最后一个分支节点开始到1号节点逆序调整堆?

因为调整堆的前提是除了堆顶之外,其他节点都满足最大堆的定义,只需要堆顶“下沉”操作即可。叶子节点没有孩子,可以认为已满足最大堆的定义,从最后一个分支节点开始调整堆,调整后该节点以下的分支已经满足最大堆的定义,其双亲节点调整时,其左右子树均已满足最大堆的定义。例如在图9-63中,2号节点调整堆时,其左右子树均已调整为堆,只需要堆顶下沉即可。

堆排序

构建初始堆之后,开始进行堆排序。因为最大堆的堆顶是最大的记录,可以将堆顶交换到最后一个元素的位置,然后堆顶执行下沉操作,调整r[1…n-1]为堆即可。重复此过程,直到剩余一个节点,得到有序序列。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

少废话上代码

#include<stdio.h>

#define maxn 100
int heap[maxn];
int n = 10;

void downAdjust(int low, int high)
{
    
    
	/*
	其中low为欲调整结点的数组下标
	high一般为堆的最后一个元素的数组下标 
	*/
	int i = low, j = i * 2;
	while(j <= high)
	{
    
    
		/*
		如果右孩子存在,且右孩子的值大于左孩子 
		*/ 
		if(j + 1 <= high && heap[j + 1] > heap[j])
		{
    
    
			j = j + 1;
		}
		if(heap[j] > heap[i])
		{
    
    
			swap(&heap[j], &heap[i]);
			i = j;
			j = i * 2;	
		} 
		else
		{
    
    
			break;
		}
	}
}

void upAdjust(int low, int high)
{
    
    
	/*
	low一般设置为1,high表示欲调整结点的数组下标 
	i为欲调整结点,j为其父节点
	*/ 
	int i = high, j = i / 2;
	while(j >= low)
	{
    
    
		if(heap[j] < heap[i])
		{
    
    
			swap(&heap[j], &heap[i]);
			i = j;
			j = i / 2;
		}
		else
		{
    
    
			break;
		}
	}
}

void createHeap()
{
    
    
	int i;
	for(i = n / 2; i >= 1; i--)
	{
    
    
		downAdjust(i, n);
	}
}

void heapSort()
{
    
    
	createHeap();
	int i;
	for(i = n; i > 1; i--)
	{
    
    
		swap(&heap[i], &heap[1]);
		downAdjust(1, i - 1);
	}
}


int swap(int *a, int *b)
{
    
    
	int tmp = *a;
	*a = *b;
	*b = tmp; 
}

void deleteTop()
{
    
    
	/*
	删除堆顶元素
	*/ 
	heap[1] = heap[n--];
	downAdjust(1, n);	
} 


void insert(int x)
{
    
    
	heap[++n] = x;
	upAdjust(1, n);
}


int main()
{
    
    
	int a[11] = {
    
    0, 85, 55, 82, 57, 68, 92, 99, 98, 66, 56};
	int i;
	for(i = 0; i < 11; i++)
	{
    
    
		heap[i] = a[i];
	}
	heapSort();
	for(i = 1; i < 11; i++)
	{
    
    
		printf("%5d", heap[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

算法复杂度分析

(1)时间复杂度

堆排序的运行时间主要耗费在构建初始堆和反复调整堆上。构建初始堆需要从最后一个分支节点(n/2)到第一个节点进行下沉操作,下沉操作最多达到树的深度logn,因此构建初始堆的时间复杂度上界是O(nlogn)。实际上这是一个比较大的上界,大多数分支节点的下沉操作少于logn,构建n个记录的堆,只需要少于2n次的比较和少于n次的交换,构建初始堆的时间复杂度是线性阶O(n)。堆排序的过程中,每一趟排序需要从堆顶下沉到叶子,下沉操作为树的深度logn,一共n-1趟排序,总的时间复杂度为O(nlogn)。

(2)空间复杂度

交换记录时需要一个辅助空间,使用的辅助空间为常数,空间复杂度为O(1)。

(3)稳定性

堆排序时多次交换关键字,可能会发生相等关键字排序前后位置不一致的情况,因此堆排序是不稳定的排序方法。

归并排序

归并排序就是采用分治的策略,将一个大的问题分成若干个小问题,先解决小问题,再通过小问题解决大问题。可以把待排序序列分解成两个规模大致相等的子序列。如果不易解决,再将得到的子序列继续分解,直到子序列中包含的元素个数为1。因为单个元素的序列本身是有序的,此时便可以进行合并,从而得到一个完整的有序序列。

算法步骤

在这里插入图片描述

算法图解

给定一个序列(42, 15, 20, 6, 8, 38, 50, 12),进行归并排序,如图9-77所示。
在这里插入图片描述
从图9-77可以看出,首先将待排序元素分成大小大致相同的两个子序列,接着再把子序列分成大小大致相同的两个子序列,如此下去,直到分解成一个元素停止,这时含有一个元素的子序列都是有序的。然后执行合并操作,将两个有序的子序列合并为一个有序序列,如此下去,直到所有的元素都合并为一个有序序列。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

少废话上代码(C++版)

#include<bits/stdc++.h>
using namespace std;

void Merge(int a[], int low, int mid, int high)
{
    
    
	int *b = new int[high - low + 1];
	int i = low, j = mid + 1, k = 0;
	while(i <= mid && j <= high)
	{
    
    
		if(a[i] <= a[j])
		{
    
    
			b[k++] = a[i++];
		}
		else
		{
    
    
			b[k++] = a[j++];
		}
	}
	while(i <= mid)
	{
    
    
		b[k++] = a[i++];
	}
	while(j <= high)
	{
    
    
		b[k++] = a[j++];
	}
	
	for(i = low, k = 0; i <= high; i++)
	{
    
    
		a[i] = b[k++];
	}
	delete []b;
}

void MergeSort(int a[], int low, int high)
{
    
    
	if(low < high)
	{
    
    
		int mid = (low + high) / 2;
		MergeSort(a, low, mid);
		MergeSort(a, mid + 1, high);
		Merge(a, low, mid, high);
	}
}

int main()
{
    
    
	int array[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 52, 33};
	MergeSort(array, 1, 8);
	for(int i = 1; i < 9; i++)
	{
    
    
		cout << array[i] << "   ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

少废话上代码(C代码)

#include<stdio.h>
#include<stdlib.h> 


void Merge(int a[], int low, int mid, int high)
{
    
    
	int *b = (int *)malloc((high - low + 1) * sizeof(int));
	int i = low, j = mid + 1, k = 0;
	while(i <= mid && j <= high)
	{
    
    
		if(a[i] <= a[j])
		{
    
    
			b[k++] = a[i++];
		}
		else
		{
    
    
			b[k++] = a[j++];
		}
	}
	while(i <= mid)
	{
    
    
		b[k++] = a[i++];
	}
	while(j <= high)
	{
    
    
		b[k++] = a[j++];
	}
	
	for(i = low, k = 0; i <= high; i++)
	{
    
    
		a[i] = b[k++];
	}
	free(b); 
}

void MergeSort(int a[], int low, int high)
{
    
    
	if(low < high)
	{
    
    
		int mid = (low + high) / 2;
		MergeSort(a, low, mid);
		MergeSort(a, mid + 1, high);
		Merge(a, low, mid, high);
	}
}

int main()
{
    
    
	int array[9] = {
    
    0, 24, 6, 3, 9, 8, 1, 52, 33};
	MergeSort(array, 1, 8);
	int i;
	for(i = 1; i < 9; i++)
	{
    
    
		printf("%5d", array[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

算法复杂度分析

在这里插入图片描述
合并排序算法的时间复杂度为O(nlogn)

(2)空间复杂度
程序中变量占用了一些辅助空间,这些辅助空间都是常数阶的,每调用一次Merge(),会分配一个适当大小的缓冲区,且在退出时释放。最多分配大小为n,所以空间复杂度为O(n)。递归调用所使用的栈空间是O(logn)

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

猜你喜欢

转载自blog.csdn.net/qq_44631615/article/details/120940232
今日推荐