排序【常见的七大排序详解】

全文目录

  • 排序的概念
    • 排序的概念
    • 内外排序
    • 常见排序算法
    • 稳定性
  • 插入排序
    • 算法思想
    • 实现
    • 特性总结
  • 希尔排序
    • 算法思想
    • 实现
    • 特性总结
  • 选择排序
    • 算法思想
    • 实现
    • 特性总结
  • 冒泡排序
    • 算法思想
    • 实现
    • 特性总结
  • 堆排序
    • 算法思想
    • 实现
    • 特性总结
  • 快速排序
    • 算法思想
    • hoare版
    • 挖坑法
    • 前后指针法
    • 优化
    • 精简版
    • 非递归实现
    • 特性总结
  • 归并排序
    • 算法思想
    • 递归实现
    • 非递归实现
    • 特性总结
  • 计数排序
    • 算法思想
    • 实现
    • 特性总结
  • 基数排序
    • 算法思想
    • 实现
    • 特性总结

排序的概念

排序的概念

将一组“无序”的记录序列调整为“有序”(递增或者递减)的记录序列。

内外排序

内部排序:整个排序过程不需要访问外存便能完成

外部排序:若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成

常见排序算法

常见的排序算法都是比较类排序算法:

排序【常见的七大排序详解】_第1张图片

稳定性

稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字( a = = b , 且 a 在 b 前面 a == b, 且a在b前面 a==b,ab前面),在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变( a 依旧在 b 前面 a依旧在b前面 a依旧在b前面),则这种排序方法是稳定的。

其中冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,归属于不稳定排序。

插入排序

插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增 1 1 1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

算法思想

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

排序【常见的七大排序详解】_第2张图片

实现

// 插入排序
void InsertSort(int* a, int n)
{
	assert(a);

	for (int i = 1; i < n; i++)
	{
		int t = a[i];
		int j = i - 1;
		while (j >= 0 && a[j] < t)
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = t;
	}
}

特性总结

  • 稳定性: 稳定

  • 时间复杂度:

    • 最坏: O ( n 2 ) O(n^2) O(n2) ,逆序序列
    • 最好: O ( n ) O(n) O(n) ,有序序列
    • 平均: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。

算法思想

先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:

  • 选择一个增量序列 t 1 , t 2 , … , t k ,其中 t i > t j , t k = 1 t1,t2,…,tk,其中t_i>t_j,t_k=1 t1t2tk,其中ti>tjtk=1
  • 按增量序列个数 k k k,对序列进行 k k k 趟排序
  • 每趟排序,根据对应的增量 t i t_i ti,将待排序列分割成若干长度为 m m m的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 1 1时,整个序列作为一个表来处理,表长度即为整个序列的长度。

排序【常见的七大排序详解】_第3张图片

排序【常见的七大排序详解】_第4张图片

实现

这里我们采取Knuth的方法: g a p = ⌊ g a p / 3 ⌋ + 1 gap = \lfloor gap / 3 \rfloor + 1 gap=gap/3+1

其中增量越大,越无序,排序次数越小,增量越小越有序,排序次数越多。一般的增量一开始取 n n n,随后每次 / 3 / 3 /3,直到为1

// 希尔排序
void ShellSort(int* a, int n)
{
	int grap = n;
	while (grap > 1)
	{
		grap = grap / 3 + 1;
		for (int i = grap; i + grap - 1 < n; i++)
		{
			int t = a[i];
			int j = i - grap;
			while (j >= 0 && a[j] > t)
			{
				a[j + grap] = a[j];
				j -= grap;
			}
			a[j + grap] = t;
		}
	}
}

特性总结

  • 稳定性: 不稳定

  • 时间复杂度:
    排序【常见的七大排序详解】_第5张图片

    • 最坏: O ( 1.6 n 1.25 ) O(1.6n^{1.25}) O(1.6n1.25)
    • 最好: O ( n 1.25 ) O(n^{1.25}) O(n1.25)
    • 平均: O ( n 1.3 O(n^{1.3} O(n1.3)
  • 空间复杂度: O ( 1 ) O(1) O(1)

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。

算法思想

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

排序【常见的七大排序详解】_第6张图片

排序【常见的七大排序详解】_第7张图片

实现

优化: 找最大(小)元素的的同时找到最小(大)元素,一次解决两个元素。

// 选择排序
void SelectSort(int* a, int n)
{
	assert(a);

	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mini = begin, maxi = end;
		for (int i = begin; i <= end; i++)
		{
			if (a[maxi] < a[i]) maxi = i;
			if (a[mini] > a[i]) mini = i;
		}

		Swap(&a[begin], &a[mini]);
		if (begin == maxi) maxi = mini;		// 防止maxi与begin重叠
		Swap(&a[end], &a[maxi]);
		
		begin++;
		end--;
	}
}

特性总结

  • 稳定性: 不稳定

举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。

  • 时间复杂度:

    • 最坏: O ( n 2 ) O(n^2) O(n2)
    • 最好: O ( n 2 ) O(n^2) O(n2)
    • 平均: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。

它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。

算法思想

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

排序【常见的七大排序详解】_第8张图片

实现

优化: 如果一趟排序中没有出现交换的情况说明已经有序,提前结束。

// 冒泡排序
void BubbleSort(int* a, int n)
{
	assert(a);

	for (int i = 0; i < n; i++)
	{
		bool flag = true;
		for (int j = 0; j < n - i - 1; j++)
		{
			if (a[j] > a[j + 1])
			{
				Swap(&a[j], &a[j + 1]);
				flag = false;
			}
		}
		if (flag) break;
	}
}

特性总结

  • 稳定性: 稳定

  • 时间复杂度:

    • 最坏: O ( n 2 ) O(n^2) O(n2)
    • 最好: O ( n ) O(n) O(n)
    • 平均: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

堆排序

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

算法思想

利用堆的删除思想:

  1. 建堆
  2. 交换堆顶与堆尾元素
  3. 堆内元素 - 1
  4. 向下调整堆顶

向下调整: 每次取左右孩子中较大(小) 的元素,如果其比父节点要大(小) 交换两者的值, 依次向下迭代,直到子节点超出堆底。

  1. 重复以上步骤n - 1次

排序【常见的七大排序详解】_第9张图片

实现

// 堆排序
// 向下调整算法
void AdjustDwon(int* a, int n, int parent)
{
	assert(a);

	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] > a[child])
			child++;

		if (a[child] > a[parent])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

void HeapSort(int* a, int n)
{
	assert(a);

	// 建堆
	for (int i = n - 1 - 1 / 2; i >= 0; i--)
		AdjustDwon(a, n, i);

	while (n > 0)
	{
		Swap(&a[0], &a[--n]);
		AdjustDwon(a, n, 0);
	}
}

特性总结

  • 稳定性: 不稳定

  • 时间复杂度:

    • 最坏: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
    • 最好: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
    • 平均: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

快速排序

快速排序(Quicksort),计算机科学词汇,适用领域Pascal,c++等语言,是对冒泡排序算法的一种改进。

算法思想

设要排序的数组是 A [ 0 ] … … A [ N − 1 ] A[0]……A[N-1] A[0]……A[N1],首先任意选取一个数据(通常选
用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:

  1. 设置两个变量 i 、 j i、j ij,排序开始的时候: i = 0 , j = N − 1 i=0,j=N-1 i=0j=N1
  2. 以第一个数组元素作为关键数据,赋值给 k e y key key,即 k e y = A [ 0 ] ; key=A[0]; key=A[0]
  3. j j j开始向前搜索,即由后开始向前搜索 ( j − − ) (j--) (j),找到第一个小于 k e y key key的值 A [ j ] A[j] A[j],将 A [ j ] A[j] A[j] A [ i ] A[i] A[i]的值交换;
  4. i i i开始向后搜索,即由前开始向后搜索 ( i + + ) (i++) (i++),找到第一个大于 k e y key key A [ i ] A[i] A[i],将 A [ i ] A[i] A[i] A [ j ] A[j] A[j]的值交换;
  5. 重复第 3 、 4 3、4 34 步,直到 i = = j i == j i==j ( 3 , 4 (3,4 (3,4 步中,没找到符合条件的值,即 3 3 3 A [ j ] A[j] A[j]不小于 k e y key key 4 4 4 A [ i ] A[i] A[i]不大于 k e y key key的时候改变 j 、 i j、i ji的值,使得 j = j − 1 , i = i + 1 , j=j-1,i=i+1, j=j1i=i+1直至找到为止。找到符合条件的值,进行交换的时候 i , j i, j ij指针位置不变。另外, i = = j i==j i==j这一过程一定正好是 i + i+ i+ j − j- j完成的时候,此时令循环结束)。

排序【常见的七大排序详解】_第10张图片

hoare版

void PartSort1(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	int key = a[left];
	int l = left, r = right;
	while (l < r)
	{
		while (l < r && a[r] >= key) r--;
		while (l < r && a[l] <= key) l++;

		Swap(&a[l], &a[r]);
	}

	Swap(&a[left], &a[r]);

	PartSort1(a, left, r - 1);
	PartSort1(a, r + 1, right);
}

注意:

key取左边界时,需要让右边先走,这样才能保证leftright是相遇的位置是小于 key

key取右边界时,需要让左边先走,这样才能保证leftright是相遇的位置是大于 key

挖坑法

在hoare版上进行改进,好处是对于最后的相遇更好理解,但还是要按照规则来。

void PartSort2(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	int piti = left;
	int key = a[left];
	int l = left, r = right;
	while (l < r)
	{
		while (l < r && a[r] >= key) r--;
		a[piti] = a[r];
		piti = r;

		while (l < r && a[l] <= key) l++;
		a[piti] = a[l];
		piti = l;
	}

	a[piti] = key;

	PartSort2(a, left, l - 1);
	PartSort2(a, l + 1, right);
}

前后指针法

前后指针法,不再是从左右两边往中间走,而是两个指针同时从头向尾走,这算法可以不用考虑最后一次相遇的情况。

单趟思路:

  1. 两个指针同时从头向尾走,key取左端点
  2. 后指针不断向后找小于key的值
  3. 找到后,前指针先 + 1 + 1 +1,再与后指针交换
  4. 重复 2 、 3 2、3 23步骤直到后指针超过序列的尾部
  5. 交换key和前指针的值

排序【常见的七大排序详解】_第11张图片

// 快速排序前后指针法
void PartSort3(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	
	int key = a[left];

	int cur = left + 1, pre = left;
	while (cur <= right)
	{
		if (a[cur] < key && ++pre != cur)
			Swap(&a[cur], &a[pre]);

		cur++;
	}
	Swap(&a[pre], &a[left]);

	PartSort3(a, left, pre - 1), PartSort3(a, pre + 1, right);
}

优化

快速排序最大影响是key的取值和递归的消耗,所以对于快速排序最大的优化就是对key 的取值和递归的优化。

三数取中优化:

快速排序最大的影响点是key,如果每次key取得是序列中的最大值或者最小值,那么整个排序的时间复杂度就是 O ( n 2 ) O(n^2) O(n2),所以我们可以加一些优化,使得key不至于取到最值。

keyleft, right, mid三个数中的中间值,这样的即可避免key取最值:

// 三数取中
int GetMid(int* a, int left, int right)
{
	int mid = left + right >> 1;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right]) return mid;
		else if (a[right] < a[left]) return left;
		else return right;
	}
	else	// a[mid] <= a[left]
	{
		if (a[left] < a[right]) return left;
		else if (a[right] < a[left]) return right;
		else return mid;
	}
}

小区间优化法:

当区间比较小时,如果还是进行快速排序的话递归的消耗会比使用其他排序算法的消耗更高,所以当区间较小时,我们可以使用其他的插入排序来改善这种情况:

if (right - left < 20)
	InsertSort(a, left, right);
else 
	QuickSort(a, left, right);

精简版

具体的详情看这里:快速排序精简版

void QuickSort(int* a, int left, int right)
{
	if (left >= right) return;

	
	int i = left - 1, j = right + 1, x = a[left + right >> 1];
	while (i < j)
	{
		do i++; while (a[i] < x);
		do j--; while (a[j] > x);
		if (i < j) Swap(&a[i], &a[j]);
	}

	QuickSort(a, left, j), QuickSort(a, j + 1, right);
}

非递归实现

如果递归程度太深会导致栈溢出,所以我们可以使

非递归快速排序我们需要借助栈来模拟函数栈帧,栈用来维护排序区间。

typedef struct Range
{
	int l, r;
} Range;

// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	Stack st;
	StackInit(&st);

	Ranges range = { left, right };
	StackPush(&st, range);

	while (!StackEmpty(&st))
	{
		range = StackTop(&st);
		StackPop(&st);
		if (range.l >= range.r) continue;	// 区间不存在
		
		// 处理当前区间
		int i = range.l - 1, j = range.r + 1, x = a[range.l + range.r >> 1];
		while (i < j)
		{
			do i++; while (a[i] < x);
			do j--; while (a[j] > x);
			if (i < j) Swap(&a[i], &a[j]);
		}
		
		// 加入新区间
		Ranges l = { range.l, j };
		Ranges r = { j + 1, range.r };
		StackPush(&st, l);
		StackPush(&st, r);
	}
}

特性总结

  • 稳定性: 不稳定

  • 时间复杂度:

    • 最坏: O ( n 2 ) O(n^2) O(n2)
    • 最好: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
    • 平均: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
  • 空间复杂度: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)(递归的消耗)

归并排序

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

算法思想

归并操作的工作原理如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

  4. 重复步骤3直到某一指针超出序列尾

  5. 将另一序列剩下的所有元素直接复制到合并序列尾

排序【常见的七大排序详解】_第12张图片

递归实现

// 归并排序递归实现
void MergeSort(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	int mid = left + right >> 1;
	MergeSort(a, left, mid), MergeSort(a, mid + 1, right);
	int* b = (int*)malloc(sizeof(int) * (right - left + 1));
	int i = left, j = mid + 1, k = 0;
	while (i <= mid && j <= right)
	{
		if (a[i] < a[j]) b[k++] = a[i++];
		else b[k++] = a[j++];
	}

	while (i <= mid) b[k++] = a[i++];
	while (j <= right) b[k++] = a[j++];

	for (int i = 0, j = left; j <= right; j++, i++) a[j] = b[i];

	free(b);
}

非递归实现

归并排序算是后续遍历,不能直接借助栈来实现,我们可以反过来,先处理区间长度为1的区间,然后区间长度依次上升,直到大于n:

排序【常见的七大排序详解】_第13张图片

// 归并排序非递归实现
void MergeSortNonR(int* a, int left, int right)
{
	assert(a);
	if (left >= right) return;

	int n = right - left + 1;
	int grap = 1;

	int* b = (int*)malloc(sizeof(int) * (right - left + 1));


	while (grap < n)
	{
		int j = 0;
		for (int i = 0; i <= right; i += 2 * grap)
		{
			//[i, i + grap - 1], [i + grap, i + 2 * grap - 1]
			int begin1 = i, end1 = i + grap - 1;
			int begin2 = i + grap, end2 = i + 2 * grap - 1;

			// 区间修正,防止越界
			if (end1 > right)
			{
				end1 = right;
				begin2 = right + 1, end2 = right;
			}
			else if (begin2 > right)
			{
				begin2 = right + 1, end2 = right;
			}
			else if (end2 > right)
			{
				end2 = right;
			}

			
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2]) b[j++] = a[begin1++];
				else b[j++] = a[begin2++];
			}

			while (begin1 <= end1) b[j++] = a[begin1++];
			while (begin2 <= end2) b[j++] = a[begin2++];
		}

		grap *= 2;
		memcpy(a, b, sizeof(int) * n);
	}
	
	free(b);
}

特性总结

  • 稳定性: 稳定

  • 时间复杂度:

    • 最坏: O ( n 2 ) O(n^2) O(n2)
    • 最好: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
    • 平均: O ( n l o g 2 n ) O(nlog_2^n) O(nlog2n)
  • 空间复杂度: O ( n ) O(n) O(n)

计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为 O ( n + k ) Ο(n+k) O(n+k)(其中 k k k是整数的范围),快于任何比较排序算法。 [1] 当然这是一种牺牲空间换取时间的做法,而且当 O ( k ) > O ( n ∗ l o g ( n ) ) O(k)>O(n*log(n)) O(k)>O(nlog(n)) 的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是 O ( n ∗ l o g ( n ) ) O(n*log(n)) O(nlog(n)), 如归并排序,堆排序)

算法思想

  1. 遍历序列,找出序列中值的范围range
  2. 开辟range大小的计数序列,并将原序列中的值映射到计数序列中
  3. 遍历计数序列,将计数序列中映射的值一次填入原序列中

排序【常见的七大排序详解】_第14张图片

实现

// 计数排序
void CountSort(int* a, int n)
{
	assert(a);

	int min = a[0], max = a[0];
	for (int i = 1; i < n; i++)
	{
		if (a[i] > max) max = a[i];
		if (a[i] < min) min = a[i];
	}

	int range = max - min + 1;
	int* count = (int*)malloc(sizeof(int) * range);
	memset(count, 0, sizeof(count) * range);

	for (int i = 0; i < n; i++) count[a[i] - min]++;

	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--) a[j++] = i + min;
	}

	free(count);
}

特性总结

  • 稳定性: 稳定

  • 时间复杂度:

    • 最坏: O ( n + r a n g e ) O(n + range) O(n+range)
    • 最好: O ( n + r a n g e ) O(n + range) O(n+range)
    • 平均: O ( n + r a n g e ) O(n + range) O(n+range)
  • 空间复杂度: O ( r a n g e ) O(range) O(range)

基数排序

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为 O ( n l o g r m ) O (nlog^rm) O(nlogrm),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

算法思想

  1. 按照低位进行基数排序
  2. 对基数排序结果计数复原到原数组中
  3. 位数依次升高,循环最高位数次

排序【常见的七大排序详解】_第15张图片

实现

#define RADIX 10
#define K 3

queue<int> q[RADIX];

int GetKey(int a, int k)
{
	for (int i = 0; i < k; i++) a /= 10;
	return a % 10;
}

void Distribute(int* a, int left, int right, int k)
{
	for (int i = left; i < right; i++)
	{
		int key = GetKey(a[i], k);
		q[key].push(a[i]);
	}
}

void Collect(int* a, int left, int right)
{
	int j = 0;
	for (int i = 0; i < RADIX; i++)
	{
		while (!q[i].empty())
		{
			a[j++] = q[i].front();
			q[i].pop();
		}
	}
}

void RadixSort(int* a, int left, int right)
{
	for (int i = 0; i < K; i++)
	{
		Distribute(a, left, right, i);

		Collect(a, left, right);
	}
}

特性总结

  • 稳定性: 稳定

  • 时间复杂度:

    • 最坏: O ( n ∗ r a d i x ) O(n * radix) O(nradix)
    • 最好: O ( n ∗ r a d i x ) O(n * radix) O(nradix)
    • 平均: O ( n ∗ r a d i x ) O(n * radix) O(nradix)
  • 空间复杂度: O ( r a d i x + n ) O(radix + n) O(radix+n)

你可能感兴趣的:(算法,排序算法,算法,数据结构)