《数据结构与算法》 第八章 排序

一、基本概念和方法概述

1.1 排序概念

  排序(Sorting):按关键字的非递减或非递增顺序对一组记录重新进行排序的操作
  排序的稳定性:当排序记录中的关键字都不相同时,则任何一个记录的无序序列经排序后得到的结果唯一
排序算法:
  ①内部排序:排序期间元素全部存放在内存中
  ②外部排序:排序期间元素无法全部同时存放在内存中,在排序过程中不断在内外存之间移动的排序

1.2 排序方法

  插入类:将无序子序列中的一个或几个记录 “插入” 到有序序列中,从而增加记录的有序子序列的长度
主要包括:直接插入排序、折半插入排序、希尔排序
  交换类:通过 “交换” 无序序列中的一个或几个记录 “插入” 到有序序列中,从而增加记录的有序子序列的长度
主要包括:冒泡排序、快速排序
  选择类:从记录的无序子序列中 “选择” 关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度
主要包括:选择排序、树形选择排序、堆排序
  归并类:通过 “归并” 两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度
主要包括:2-路归并排序
  分配类:是唯一一类不需要进行关键字比较的排序方法,排序时主要利用分配和收集两个基本操作完成
主要包括:基数排序

1.3 待排序记录的存储方式

顺序表:记录之间的次序关系由其存储位置决定,实现排序需要移动记录

链表排序:记录之间的次序关系由指针指示,实现排序不需要移动记录,仅需修改指针即可

地址排序:待排序记录本身存储在一组地址连续的存储单元内,同时另设一个指示各个记录存储位置的地址向量,在排序过程中不移动记录本身,而移动地址向量中那些记录的 “地址” ,在排序结束之后再按照地址向量中的值调整记录的存储位置

#define MAXSIZE 10						// 顺序表的最大长度

typedef struct {
    int key;							// 关键字项
    int value;							// 其他数据项
} RedType;								// 记录类型

typedef struct {
    RedType r[MAXSIZE+1];				// r[0] 闲置或用做哨兵单元
    int length;							// 顺序表长度
} SQList;								// 顺序表类型

1.4 排序算法效率的评价指标

执行时间:对于排序操作,时间主要消耗在关键字之间的比较和记录的移动上,排序算法的时间复杂度由这两个指标决定
辅助空间:除了存放待排序记录占用的空间之外,执行算法所需要的其他存储空间,空间复杂度由排序算法的辅助空间决定

二、插入排序

2.1 直接插入排序

直接插入排序(Straight Insertion Sort):将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增 1 的有序表

算法步骤:
  a.从第一个元素开始,该元素可以认为已经被排序;
  b.取出下一个元素,在已经排序的元素序列中从后向前扫描;
  c.如果该元素(已排序)大于新元素,将该元素移到下一位置;
  d.重复步骤 c,直到找到已排序的元素小于或者等于新元素的位置;
  e.将新元素插入到该位置后;
  f.重复步骤 b~e。
在这里插入图片描述

时间复杂度为 O(n^2),空间复杂度为 O(1)

算法特点
 ①.稳定排序
 ②.算法简便,且容易实现
 ③.适用于链式存储结构,只是单链表上无需移动记录,只需修改相应的指针
 ④.适用于初始记录基本有序(正序)的情况,当初始记录无序,n 较大时,此算法时间复杂度较高,不宜采用

代码如下:

void InsertSort(SQList* L) {
	for (int i = 2; i < MAXSIZE + 1; i++)
	{
		if (L->r[i].key < L->r[i-1].key)
		{
			L->r[0] = L->r[i];
			for (int j = 1; j < i; j++)
			{
				if (L->r[0].key < L->r[j].key)
				{
					L->r[i] = L->r[j];
					L->r[j] = L->r[0];
					L->r[0] = L->r[i];
				}
			}
		}
	}
	L->r[0].key = L->r[0].value = 0;
	printf("InsertSort Success\n");
}

2.2 折半插入排序

折半插入排序(Binary Insertion Sort):折半插入排序也叫二分插入排序,是插入排序算法的变体。与普通的插入排序不同的是,折半插入排序使用二分查找来确定要插入的位置,从而减少了比较的次数。

算法步骤:
  a.将第一个元素看作已经排序好的部分,将后面的元素依次插入到已排序部分中。
  b.从已排序部分的中间位置开始,使用二分查找找到要插入的位置。
  c.将要插入位置后的所有元素后移一位,空出要插入的位置
  d.将要插入的元素插入到空出的位置。
  e.重复上述步骤,直到所有元素都被插入到已排序部分中。

时间复杂度为 O(n^2),空间复杂度为 O(1)

算法特点
 ①.稳定排序
 ②.要进行折半查找,只能用于顺序存储,不能用于链式存储
 ③.适合初始记录无序、n 较大时的情况

代码如下:

void BInsertSort(SQList* L) {
	for (int i = 2; i < MAXSIZE + 1; i++)
	{
		int low = 1;
		int high = i - 1;
		L->r[0] = L->r[i];						// 将待插入的记录暂存到监视哨中
        
		while (low <= high)						// 在 r[low~high] 中折半查找插入的位置
		{
			int m = (low + high) / 2;			// 折半
			if (L->r[0].key < L->r[m].key)
			{
				high = m - 1;					// 插入点在前一子表
			}
			else
			{
				low = m + 1;					// 插入点在后一子表
			}
		}

		for (int j = i - 1; j >= high + 1; j--)
		{
            L->r[j + 1] = L->r[j];				// 记录后移
		}
		
		L->r[high + 1] = L->r[0];				// 将原 r[i] 插入到正确位置
	}
	L->r[0].key = L->r[0].value = 0;
	printf("BInsertSort Success\n");
}

2.3 希尔排序

希尔排序(Shell’s Sort)(缩小增量排序(Diminishing Increment Sort)):它是插入排序的改进版,通过将待排序的数据分成若干个子序列进行插入排序,从而使整个序列在初始时具有较好的有序性,然后逐步缩小子序列的长度,直到整个序列成为有序序列。

算法步骤:
  a.将待排序的数据按照步长gap进行分组(最初的gap为待排序序列长度的一半),对每组数据进行插入排序。
  b.缩小步长gap,重复第一步,直到gap为1,即对所有数据进行插入排序,排序完成。
在这里插入图片描述
在这里插入图片描述

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

时间复杂度为 O(nlogn)到O(n^2)之间,取决于步长序列的选择,
空间复杂度为 O(1)

算法特点
 ①.记录跳跃式地移动导致排序方法是不稳定的
 ②.只能用于顺序结构,不能用于链式结构
 ③.增量序列可以有各种取法,但应该使增量序列中的值没有除 1 之外的公因子,并且最后一个增量必须等于 1
 ④.记录总的比较次数和移动次数都比直接插入排序要少,n 越大时,效果越明显
 ⑤.适合初始记录无序、n 较大时的情况

代码如下:

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

#define MAXSIZE 10

void InsertSort(SQList);
void BInsertSort(SQList);
void ShellInsert(SQList);
void ShellSort(SQList);
void PrintList(SQList);

typedef struct {
	int key;
	int value;
} RedType;

typedef struct {
	RedType r[MAXSIZE + 1];
	int length;
} SQList;

int main() {
	int keys[MAXSIZE] = { 2,4,6,8,5,3,7,9,10,1 };
	SQList L;
	SQList Copy;
	L.r[0].key = L.r[0].value = 0;
	L.length = 0;
	
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		L.r[i].key = L.r[i].value = keys[i-1];
		L.length++;
	}

	Copy = L;

	InsertSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");

	BInsertSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");

	int dt[] = { 5,3,1 };
	ShellSort(&L, dt, 3);
	PrintList(L);
	L = Copy;
	printf("****************\n");
}

void InsertSort(SQList* L) {
	for (int i = 2; i < MAXSIZE + 1; i++)
	{
		if (L->r[i].key < L->r[i-1].key)
		{
			L->r[0] = L->r[i];
			for (int j = 1; j < i; j++)
			{
				if (L->r[0].key < L->r[j].key)
				{
					L->r[i] = L->r[j];
					L->r[j] = L->r[0];
					L->r[0] = L->r[i];
				}
			}
		}
	}
	L->r[0].key = L->r[0].value = 0;
	printf("InsertSort Success\n");
}

void BInsertSort(SQList* L) {
	for (int i = 2; i < MAXSIZE + 1; i++)
	{
		int low = 1;
		int high = i - 1;
		L->r[0] = L->r[i];

		while (low <= high)
		{
			int m = (low + high) / 2;
			if (L->r[0].key < L->r[m].key)
			{
				high = m - 1;
			}
			else
			{
				low = m + 1;
			}
		}

		for (int j = i - 1; j >= high + 1; j--)
		{
			L->r[j + 1] = L->r[j];
		}
		
		L->r[high + 1] = L->r[0];
	}
	L->r[0].key = L->r[0].value = 0;
	printf("BInsertSort Success\n");
}

void ShellInsert(SQList* L, int step) {
	for (int i = step + 1; i < MAXSIZE + 1; i++)
	{
		if (L->r[i].key < L->r[i - step].key)
		{
			L->r[0] = L->r[i];

			int j = 0;
			for (j = i - step; j > 0 && L->r[0].key < L->r[j].key; j -= step)
			{
				L->r[j + step] = L->r[j];
			}
			L->r[j + step] = L->r[0];
		}
	}
}

void ShellSort(SQList* L, int dt[], int num) {
	for (int i = 0; i < num; i++)
	{
		ShellInsert(L, dt[i]);
	}
	printf("ShellSort Success\n");
}

void PrintList(SQList L) {
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		printf("%d ", L.r[i].key);
	}
	printf("\n");
}

三、交换排序

3.1 冒泡排序

冒泡排序(Bubble Sort):两两比较相邻记录的关键字,如果发生逆序,则进行交换,从而使关键字小的记录如气泡一般逐渐往上 “漂浮” (左移),或者使关键字大的记录如石块一样逐渐向下 “坠落” (右移)。这个过程一直持续到没有任何交换操作发生,也就是列表已经有序。

算法步骤:
  a.从列表的第一个元素开始,重复遍历到倒数第二个元素
  b.在每次遍历中,比较当前元素和下一个元素的值
  c.如果当前元素大于下一个元素,就交换它们的位置
  d.重复以上步骤,直到遍历到倒数第二个元素
  e.如果这一轮遍历没有进行任何交换操作,说明列表已经有序,停止遍历
在这里插入图片描述
时间复杂度为 O ( n ^2 ) ,空间复杂度为O(1)

算法特点
 ①.稳定排序
 ②.可用于链式存储结构
 ③.移动记录次数较多,算法平均时间性能比直接插入排序差
 ④.当初始记录无序,n 较大时,此算法不宜采用

代码如下:

void BubbleSort(SQList* L) {
	int flag = 0;								// flag 标记某一趟排序是否发生交换
	int index = MAXSIZE;

	while ((flag == 0) && (index > 1))
	{
		flag = 1;								// flag 置为 1 ,如果本趟没有发生交换,则不会执行下一趟排序

		for (int i = 1; i < index; i++)
		{
			if (L->r[i].key > L->r[i+1].key)
			{
				flag = 0;						// flag 置为 0 ,表示本趟排序发生了交换
				
				L->r[0] = L->r[i];				// 交换前后两个记录
				L->r[i] = L->r[i + 1];
				L->r[i + 1] = L->r[0];
			}
		}

		index--;
	}
	L->r[0].key = L->r[0].value = 0;
	printf("BubbleSort Success\n");
}

3.2 快速排序

快速排序(Quick Sort):通过两个(不相邻)记录的交换,消除多个逆序

算法步骤:
  a.选取一个基准值(pivot)
  b.将数组中小于等于pivot的元素,放在pivot左边,大于pivot的元素,放在pivot右边
  c.对pivot左右两边的子数组,分别递归地执行步骤a、b,直到子数组只剩下一个元素或为空
  d.合并所有子数组,排序完成

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

时间复杂度为O(nlogn)
最好情况下的空间复杂度为 O ( l o g 2 n ) ,最坏情况下的空间复杂度为 O ( n )

算法特点
 ①.记录非顺次的移动导致排序方法是不稳定的
 ②.排序过程中需要定位表的上界和下界,所以适合用于顺序结构,很难用于链式结构
 ③.当 n 较大时,在平均情况下快速排序是所有内部排序方法中速度最快的一种
 ④.适合初始记录无序、n 较大时的情况

代码如下:

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

#define MAXSIZE 10

void BubbleSort(SQList);
int Partition(SQList);
void QSort(SQList);
void QuickSort(SQList);
void PrintList(SQList);

typedef struct {
	int key;
	int value;
} RedType;

typedef struct {
	RedType r[MAXSIZE + 1];
	int length;
} SQList;

int main() {
	int keys[MAXSIZE] = { 2,4,6,8,5,3,7,9,10,1 };
	SQList L;
	SQList Copy;
	L.r[0].key = L.r[0].value = 0;
	L.length = 0;
	
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		L.r[i].key = L.r[i].value = keys[i-1];
		L.length++;
	}

	Copy = L;

	BubbleSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");

	QuickSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");
}

void BubbleSort(SQList* L) {
	int flag = 0;
	int index = MAXSIZE;

	while ((flag == 0) && (index > 1))
	{
		flag = 1;

		for (int i = 1; i < index; i++)
		{
			if (L->r[i].key > L->r[i+1].key)
			{
				flag = 0;
				
				L->r[0] = L->r[i];
				L->r[i] = L->r[i + 1];
				L->r[i + 1] = L->r[0];
			}
		}

		index--;
	}
	L->r[0].key = L->r[0].value = 0;
	printf("BubbleSort Success\n");
}

int Partition(SQList* L, int low, int high) {
	L->r[0] = L->r[low];

	int key = L->r[low].key;

	while (low < high)
	{
		while ((low < high) && (L->r[high].key >= key))
		{
			high--;
		}

		L->r[low] = L->r[high];

		while ((low < high) && (L->r[low].key <= key))
		{
			low++;
		}

		L->r[high] = L->r[low];

	}
	L->r[low] = L->r[0];

	return low;
}

void QSort(SQList* L, int low, int high) {
	if (low < high)
	{
		int middle = Partition(L, low, high);
		QSort(L, low, middle - 1);
		QSort(L, middle + 1, high);
	}
}

void QuickSort(SQList* L) {
	QSort(L, 1, MAXSIZE);
	printf("QuickSort Success\n");
}

void PrintList(SQList L) {
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		printf("%d ", L.r[i].key);
	}
	printf("\n");
}

四、选择排序

4.1 简单选择排序

简单选择排序(Simple Selection Sort)(直接选择排序)是一种简单直观的排序算法,其基本思路是先将数组中最小的元素找到,将其放置在数组的最前面,然后再从剩余的元素中找到最小的元素,放置在已排好序的元素的后面,依此类推,直到整个数组都排好序。

在这里插入图片描述

时间复杂度为 O ( n^2 ) ,空间复杂度为 O ( 1 )

算法特点
 ①.稳定的排序方法
 ②.可用于链式存储结构
 ③.移动记录次数较少,当每一记录占用的空间较多时,此方法比直接插入排序快

代码如下:

void SelectSort(SQList* L) {
	for (int i = 1; i < MAXSIZE; i++)			// 从 L->r[low···high] 中选择关键字最小的记录
	{
		int index = i;

		for (int j = i + 1; j < MAXSIZE + 1; j++)
		{
			if (L->r[j].key < L->r[index].key)
			{
				index = j;						// index 指向此趟排序中关键字最小的记录
			}
		}

		if (index != i)
		{
			L->r[0] = L->r[i];					// 交换 r[i] 与 r[index]
			L->r[i] = L->r[index];
			L->r[index] = L->r[0];
		}
	}
	printf("SelectSort Success\n");
}

4.2 树形选择排序

树形选择排序(Tree Selection Sort)是一种基于树形数据结构的排序算法。其基本思想是利用完全二叉树的性质,将待排序序列构建成一棵完全二叉树,并从根节点开始,每一层选择最小(或最大)的元素,并将其放置在已排序序列的末尾。重复执行以上步骤直到所有元素都排好序为止。

算法步骤:
  a.将待排序序列构建成一棵完全二叉树,根节点为序列的第一个元素
  b.从根节点开始,依次遍历每一层节点,选择该层节点中最小(或最大)的元素,并将其与该层最后一个元素交换位置
  c.重复执行以上步骤直到所有元素都排好序为止

时间复杂度为O(nlogn),空间复杂度为O(n)

相比于选择排序,树形选择排序的比较次数更少,但由于需要构建完全二叉树,其相对于其他排序算法的实现难度较大。

4.3 堆排序

堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法。 堆排序的基本思想是:
  将待排序的序列构建成一个大根堆(或小根堆),根据大根堆(或小根堆)的性质,完全二叉树的根节点一定是最大值(或最小值)。
  将根节点和最后一个节点进行交换,使最大值(或最小值)放到了序列的最末尾。
  排除最后一个节点,重新构建一个大根堆(或小根堆),让剩余序列中的根节点再成为最大值(或最小值),重复2、3步骤,直到所有节点都被排完序。

算法步骤:
  a.建立大根堆:从最后一个非叶子节点开始向上遍历,对每一个节点进行堆化操作,即比较该节点和其子节点的大小,若该节点比其子节点小,则交换该节点和其子节点的位置,直到该节点没有子节点或比其子节点的值都大。
  b.排序:将堆顶元素与最后一个元素交换位置,此时最后一个元素为有序序列中的最大值,接着重新进行堆化操作,对除了有序序列外的剩余节点进行堆化,重复上述步骤,直到所有元素都被排完序。

调整堆 代码如下:

void HeapAdjust(SQList* L, int s, int m) {
	L->r[0] = L->r[s];

	for (int i = 2 * s; i <= m; i *= 2)					// 沿 key 较大的孩子结点向下筛选
	{
		if ((i < m) && (L->r[i].key < L->r[i+1].key))	// i 为 key 较大的记录的下标
		{
			i++;
		}

		if (L->r[0].key >= L->r[i].key)					// L->r[0] 应插入在位置 s 上
		{
			break;
		}

		L->r[s] = L->r[i];
		s = i;
	}

	L->r[s] = L->r[0];
}

建初堆 代码如下:

void CreatHeap(SQList* L) {
	for (int i = MAXSIZE / 2; i > 0; i--)
	{
		HeapAdjust(L, i, MAXSIZE);
	}
}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
堆排序算法的实现:
代码如下

void HeapSort(SQList* L) {
	CreatHeap(L);								// 把无序序列 L->r[1···MAXSIZE] 建成大根堆 

	for (int i = MAXSIZE; i > 1; i--)
	{
		L->r[0] = L->r[1];						// 将堆顶记录和当前未经排序子序列 L->r[1···i] 中最后一个记录互换
		L->r[1] = L->r[i];
		L->r[i] = L->r[0];
		HeapAdjust(L, 1, i - 1);				// 将 L->r[1···i-1] 重新调整为大根堆
	}

	printf("HeapSort Success\n");
}

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

在这里插入图片描述

时间复杂度为O(nlogn),空间复杂度为O(1)

代码如下:

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

#define MAXSIZE 10

void SelectSort(SQList);
void HeapAdjust(SQList);
void CreatHeap(SQList);
void HeapSort(SQList);
void PrintList(SQList);

typedef struct {
	int key;
	int value;
} RedType;

typedef struct {
	RedType r[MAXSIZE + 1];
	int length;
} SQList;

int main() {
	int keys[MAXSIZE] = { 2,4,6,8,5,3,7,9,10,1 };
	SQList L;
	SQList Copy;
	L.r[0].key = L.r[0].value = 0;
	L.length = 0;
	
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		L.r[i].key = L.r[i].value = keys[i-1];
		L.length++;
	}

	Copy = L;

	SelectSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");

	HeapSort(&L);
	PrintList(L);
	L = Copy;
	printf("****************\n");
}

void SelectSort(SQList* L) {
	for (int i = 1; i < MAXSIZE; i++)
	{
		int index = i;

		for (int j = i + 1; j < MAXSIZE + 1; j++)
		{
			if (L->r[j].key < L->r[index].key)
			{
				index = j;
			}
		}

		if (index != i)
		{
			L->r[0] = L->r[i];
			L->r[i] = L->r[index];
			L->r[index] = L->r[0];
		}
	}
	printf("SelectSort Success\n");
}

void HeapAdjust(SQList* L, int s, int m) {
	L->r[0] = L->r[s];

	for (int i = 2 * s; i <= m; i *= 2)
	{
		if ((i < m) && (L->r[i].key < L->r[i+1].key))
		{
			i++;
		}

		if (L->r[0].key >= L->r[i].key)
		{
			break;
		}

		L->r[s] = L->r[i];
		s = i;
	}

	L->r[s] = L->r[0];
}

void CreatHeap(SQList* L) {
	for (int i = MAXSIZE / 2; i > 0; i--)
	{
		HeapAdjust(L, i, MAXSIZE);
	}
}

void HeapSort(SQList* L) {
	CreatHeap(L);

	for (int i = MAXSIZE; i > 1; i--)
	{
		L->r[0] = L->r[1];
		L->r[1] = L->r[i];
		L->r[i] = L->r[0];
		HeapAdjust(L, 1, i - 1);
	}

	printf("HeapSort Success\n");
}

void PrintList(SQList L) {
	for (int i = 1; i < MAXSIZE + 1; i++)
	{
		printf("%d ", L.r[i].key);
	}
	printf("\n");
}

优点:堆排序是一种不稳定的排序算法,但其空间复杂度为O(1),且时间复杂度为O(nlogn),比冒泡、直接插入、选择等排序算法具有更优的时间复杂度。

缺点:堆排序需要额外的辅助空间来存储堆结构,且其常数因子较大,因此在实际应用中不如快速排序等算法。

五、归并排序

归并排序是一种排序算法,它采用分治法的思想。它将待排序数组分成若干个子数组,每个子数组都是有序的,然后合并这些子数组,最终得到一个完整的有序数组。

算法步骤
  a.将待排序数组不断分成两个子数组,直到每个子数组里只有一个元素。这个过程可以使用递归来实现。
  b.将相邻的两个子数组合并成一个有序数组。合并方法是比较两个子数组的首元素,将较小的元素放到新数组中,然后比较较小元素所在子数组的下一个元素和较大元素,重复这一过程,直到将两个子数组都全部合并到新数组中。
  c.重复步骤b,直到所有子数组都合并成一个有序数组。
在这里插入图片描述

时间复杂度为 O(nlogn),空间复杂度为O(n)

算法特点
 ①.稳定排序
 ②.可用于链式结构,且不需要附加存储空间,但递归实现时仍需要开辟相应的递归工作栈

代码如下:

void Merge(RedType R[], RedType T[], int low, int mid, int high) {
	int i = low;
	int j = mid + 1;
	int k = low;

	while (i <= mid && j <= high )
	{
		if (R[i].key <= R[j].key)
		{
			T[k++] = R[i++];
		}
		else {
			T[k++] = R[j++];
		}
	}

	while (i <= mid)
	{
		T[k++] = R[i++];
	}

	while (j <= high)
	{
		T[k++] = R[j++];
	}
}

void MSort(RedType R[], RedType T[], int low, int high) {
	if (low != high)
	{
		int mid = (low + high) / 2;
		RedType S[MAXSIZE + 1];
		MSort(R, S, low, mid);
		MSort(R, S, mid + 1, high);
		Merge(S, T, low, mid, high);
	}
	else {
		T[low] = R[low];
	}
}

void MergeSort(SQList* L) {
	MSort(L->r, L->r, 1, MAXSIZE);
	printf("MergeSort Success\n");
}

六、基数排序

基数排序是一种通过将待排序元素按照位数逐个比较的方法进行排序的算法。是一种非比较算法,其原理是将整数按每个位数分别比较。它利用了桶的思想。。

  基数排序的实现方法是:首先根据待排序元素的最高位进行排序,然后按照次高位进行排序,直到按照最低位进行排序。每次排序时,可以使用计数排序、桶排序或者桶排+计数排序等算法完成。
在这里插入图片描述
在这里插入图片描述

代码如下:

#include<bits/stdc++.h>
using namespace std;
 
int temp[100];
int bucket[10];
 
int maxBit(int data[],int n)
{
	//行这些代码在求n个元素的最大值 
	int maxData = data[0];
	for(int i=1;i<n;i++)
	{
		if(maxData<data[i])
			maxData=data[i];
	}
	
	//这些代码在求最大值的位数是多少 
	int d=1;    //d用来计数最大值的位数,因为既然是一个数,肯定至少有1位,所以d初始化为1 
	while(maxData>=10)  //将最大值不断/10,计算位数 
	{
		maxData/=10;
		d++;
	}
	return d;
} 
void radixsort(int data[],int n)  //基数排序 
{
	int d = maxBit(data,n);  //求出最大位数
	int i,j,k;
	int radix = 1;
	for(i=1;i<=d;i++)   //进行d次排序
	{
	    for(j=0;j<10;j++)   //每次分配前清空计数器
		{
			bucket[j]=0;
		}
		for(j=0;j<n;j++)    //统计每个桶的元素个数 
		{
			k=(data[j]/radix)%10;
			bucket[k]++;
		}
		
		//关键代码1 
	    for(j = 1; j < 10; j++)
            bucket[j] = bucket[j - 1] + bucket[j]; 
       
       //关键代码2 
		for(j = n-1; j>=0; j--) 
        {
            k = (data[j] / radix) % 10;
            temp[bucket[k] - 1] = data[j];
            bucket[k]--;
        }
        for(j = 0; j < n; j++) //将临时数组的内容复制到data中
            data[j] = temp[j];
            
        radix = radix * 10;  //个位  -》 十位  -》百位 -》…… 
	} 
}
 
int main()
{
	int a[4]={2,1,34,4};   
	radixsort(a,4);         //a十待排序的数组 ,4是元素个数 
	for(int i=0;i<4;i++)
		cout<<temp[i]<<" ";
	return 0;
}

时间复杂度为O(n)

算法特点
  ①.基数排序法是效率高的稳定排序法
  ②.当元素取值范围较大,但元素个数较少时可以利用基数排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值