【数据结构】八大排序算法详解_数据结构排序算法(1)

空间复杂度为O(1)
简单选择排序是不稳定的

2.2 堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

想要完成堆排序,首先要通过向下调整算法将二叉树这样的数据结构建成一个堆,然后再利用堆删除的思想完成排序。

2.2.1 堆向下调整算法

假若现在给你一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整

int array[] = {27,15,19,18,28,34,65,49,25,37};

在这里插入图片描述

void Swap(HPDataType\* left, HPDataType\* right)
{
	HPDataType temp = \*left;
	\*left = \*right;
	\*right = temp;
}
void AdjustDown(Heap\* hp,int n,int parent)
{
	int child = parent \* 2 + 1; //调整结点的左孩子
	while (child < n) //当child大于结点个数时调整完毕
	{
		//判断是否有右孩子并且右孩子大于左孩子
		if (child + 1 < n&&hp->array[child] < hp->array[child + 1])
		{
			child += 1;
		}
		if (hp->array[child]>hp->array[parent])
		{
			Swap(&hp->array[child], &hp->array[parent]); //若孩子大于父亲则交换
			//继续向下调整继续判断
			parent = child;
			child = parent \* 2 + 1;
		}
		else
		{
			return;
		}
	}
}

2.2.2 堆排序

在堆建立完毕之后,利用堆删除思想来进行排序,建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

思路: 1)完成建堆,让其拥有父亲结点大于孩子结点的特性(或者父亲结点小于孩子结点)
      2)交换根结点与最后一个孩子结点,那么此时最大的结点就来到了堆的最后一位,将堆的元素个数减一,然后在从根结点(刚交换上去的结点)完成向下调整算法。《注意: 堆的顺序结构是用数组实现的,所以说将堆元素个数减一并不是将其删除,而是将其放在了数组的最后一个位置》
      3)一直持续到最后两个结点将其完成交换即可完成堆排序。

void Swap(HPDataType\* left, HPDataType\* right)
{
	HPDataType temp = \*left;
	\*left = \*right;
	\*right = temp;
}
void AdjustDown(int\* array, int root, int sz)
{
	// 用child标记parent较大的孩子,默认先标记parent的左孩子
	// 先标记左孩子的原因是:如果parent有孩子,parent一定是先有左孩子,然后才有右孩子
	int parent = root;
	int child = parent \* 2 + 1;
	while (child < sz)
	{
		// 找parent中较大的孩子:用parent左右孩子比较
		// 必须先保证parent的右孩子存在
		if (child + 1 < sz && array[child + 1] > array[child])
		{
			child += 1;
		}
		// 检测parent是否满足堆的性质
		if (array[parent] < array[child])
		{
			Swap(&array[parent], &array[child]);
			parent = child;
			child = parent \* 2 + 1;
		}
		else
		{
			break;
		}
	}

}

void HeapSort(int\* array, int sz)
{
	int root = sz - 2 / 2;
	// 1. 建堆
	// 注意从倒数第一个非叶子节点的位置开始使用堆调整,一直调整到根节点的位置
	for (int i = root; i >= 0; --i)
	{
		AdjustDown(array, i, sz);
	}
	// 2. 排序--->利用堆删除的思想进行排序
	for (int i = sz - 1; i > 0; --i)
	{
		Swap(&array[0], &array[i]);
		AdjustDown(array, 0, i);
	}
}

堆排序的时间代价主要花费在建堆和排序上;

建堆的时间复杂度:
在这里插入图片描述
因此:建堆的时间复杂度为O(n)。

排序的时间复杂度:
  首先要明白的是建好堆之后,然后通过堆删除的思想来排序。而堆删除的思路是怎样?先将根结点与最后一个结点交换,然后把根结点放在数组地末位,然后数组元素减一,再将刚换上去地结点去执行向下调整算法,直到将所有地元素都交换完毕。我们发现对于每一个元素都要执行一次向下调整算法,而向下调整算法向下走的是单支树,所以向下调整算法的时间复杂度为O(logn),那么对于n个节点来说,完成整个排序的时间复杂度就为O(nlogn)。

所以,堆排序的时间复杂度为O(n)+O(nlogn),因为大O的渐进表示法的表示,堆排序的时间复杂度为O(nlogn)
空间复杂度为O(1)
堆排序是一种不稳定的排序算法。

3. 交换类排序

交换类排序算法的基本思想为,将待排序序列的元素两两比较,只要发现逆序就进行交换,知道没有逆序为止。如果要将整个序列调整为递增序列,那么元素之间是递减关系就为逆序。冒泡排序和快速排序就是典型的交换类排序算法。

3.1 冒泡排序

动图演示:

在这里插入图片描述
  冒泡排序也叫做“相邻比逆法”,即在扫描待排序记录序列时顺次比较相邻两个元素大小,如果逆序就交换位置。如果以将序列调整成升序为例,则逆序为两个关键字是降序序列。

具体地,各趟排序过程如下:

第1趟比较第1和第2个元素,如果逆序就交换,再依次比较第2个和第3个元素、第3个和第 4 个…若是逆序则交換。经过该趟比较和交换,最大的数必然“沉到”最后一个位置。
 第2趟用同样的方法,在前面的 n-1 个元素中依次进行比较和交换,第2大的数“沉到”倒数第2个位置上。
 第 i 趟仍用同样方法,在剩下的 n-i+1 个元素中依次进行比较和交换,第 i 大的数“沉到”倒数第 i 个位置上。
 重复此过程,直到 i=n-1最后一趟比较完为止。

代码实现:

void BubbleSort(int\* array, int sz)
{
	// 控制冒泡的趟数
	for (int i = 0; i < sz- 1; ++i)  // -1的目的是可以少冒一趟,因为最后一次冒泡区间中只剩余一个元素
	{
		// 具体冒泡的方式:用相邻位置的元素进行比较,如果不满足条件,就进行交换
		// j:表示后一个元素的下标,j要取到最后一个元素
		for (int j = 1; j < sz- i; ++j)  // -1目的:j最多只能取到冒泡区间的倒数第二个元素
		{
			if (array[j-1] > array[j])
				Swap(&array[j], &array[j - 1]);
		}
	}
}

同样我们可以对上面的代码进行优化,优化的点在哪呢?就是万一在排序的过程中序列已经顺序了,那就没有必要再冒泡下去了,此时我们便设置一个标记 flag ,每次冒泡前将其设置为0,若在一趟冒泡中发生了元素交换,就说明元素并没有顺序,便将其改为1,若在一趟冒泡中没有发生任何元素的交换,就说明该序列顺序,那么 flag 就不会被更改,然后就直接退出最外层的循环,完成了冒泡排序。

void BubbleSortOP(int\* array, int sz)
{
	int flag = 0;
	for (int i = 0; i < sz- 1; ++i)  // -1的目的是可以少冒一趟,因为最后一次冒泡区间中只剩余一个元素
	{
		flag = 0;  // 该趟冒泡还没有比较,因此将falg设置为0
		for (int j = 1; j < sz- i; ++j)  // -1目的:j最多只能取到冒泡区间的倒数第二个元素
		{
			if (array[j - 1] > array[j])
			{
				Swap(&array[j], &array[j - 1]);
				flag = 1;  // 在该趟冒泡时区间还无序
			}
		}
		if (!flag)
			return;
	}
}

冒泡排序总结:

冒泡排序在最好的情况下是序列为顺序,那么外层循环只进行一次就结束整个排序过程,最好的时间复杂度位O(n),但在最差的情况下外层循环最多进行 n-1 次,每一次外层控制的内层循环进行 n-i 次,最坏的时间复杂度为O(n^2)
 空间复杂度:O(1)
 冒泡排序是一种不稳定的排序算法。

3.2 快速排序

快速排序是一种应用非常广泛的排序算法,从它的名字就可以看出它的排序效率是比较高的。在1962年Hoare提出了一种划分交换排序,由于它几乎最快的排序算法,所以就被称为“快速排序”。

快速排序采用了一种分治的策略,分治法的基本思想是将原问题分解为若干个规模更小的问题但结构与原问题相似的子问题,递归的解这些子问题,然后将这些子问题的解组合称为原问题的解。
  基于这样的思想,快速排序的基本思想为:取待排序元素序列中的某一元素作为基准值(我们假设取最左边的值为基准值),按照该基准值将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后在左右子序列重复该过程,直到所有元素都排列在相应位置上为止。可以看出快速排序的遍历规则与二叉树的前序遍历非常类似的

接下来将从以下几个方面来对快排做一个详细介绍:
在这里插入图片描述

3.2.1 hoare版本

动图演示:
在这里插入图片描述

基本思想:选择待排序序列最左边的元素为基准值key,左右标记分别指向序列的第一个元素和最后一个元素,先让右标记往左走,当找到比key小的数字停下来,然后再让左标记往右走,当找到比key大的数字停下来,交换左右标记对应的数字,然后继续这样的操作,直到左右标记指向同一个元素(左右标记相遇),然后再交换基准值key和左右标记指向的元素。
  这样快排的一轮排序就完成了,可以看到每当完成一轮排序总会有一个元素落位到它该在的位置

代码实现:

void swap(int\* left, int\* right)
{
	int temp = \*left;
	\*left = \*right;
	\*right = temp;
}
int Partion(int\* array, int begin, int end)
{
	int left = begin;
	int left = end;
	int keyi = left;//基准值
	while (left < right)
	{
		//右标记向左走,直到找到小于key的元素就停下
		while (left < right&&array[right] >= array[keyi])
		{
			--right;
		}
		//左标记向右走,直到找到大于key的元素就停下
		while (left < right&&array[left] <= array[keyi])
		{
			++left;
		}
		swap(&array[left], &array[right]);
	}
	//交换基准值key和左右标记指向的元素
	//加判断的原因在于key右边的元素都比它小,所以就没有必要进行交换操作
	if (end != left)
	{
		swap(&array[left], &array[keyi]);
	}
	//返回已经被放好元素的位置,也就是key最终所在的位置
	return begin;
}
void quicksort(int\* array, int left, int right)
{
	//排序的终止条件(左右标记指向同一个元素,也就是待排序序列只有一个元素)
	if (left >= right)
	{
		return;
	}
	int div = Partion(array, left, right);
	//排序刚放好位置元素的左半边
	quicksort(array, left, div - 1);
	//排序刚放好位置元素的右半边
	quicksort(array, div + 1, right);
}

3.2.2 挖坑法

动图演示:
在这里插入图片描述

基本思想:同样选择序列的最左边值为基准值,左右标记分别指向序列的第一个元素和最后一个元素,那个坑位就是key所在的位置,先让右标记向左走,当找到比key小的元素停下,然后将该元素放置到那个坑位,此时就有了一个新坑位,而刚才的坑位就被填补了,然后再让左标记向右走,当找到比key大的元素停下,然后将该元素放置到新坑位,直到左右标记共同指向一个位置,也就是指向了一个坑位,然后再把基准值放到那个坑位就结束了一次排序过程。
  和hoare版本一样,每当完成一轮排序总会有一个元素落位到它该在的位置

代码实现:

int Partion(int\* array, int begin, int end)
{
	int left = begin, right = end;
	int key = array[begin];
	while (left < right)
	{
		while (left < right && array[right] >= key)
		{
			--right;
		}
		if (left != right)
		{
			array[left] = array[right];
		}
		while (left < right && array[left] <= key)
		{
			++left;
		}
		if (left != right)
		{
			array[right] = array[left];
		}
	}
	array[left] = key;
	return left;
}
void quicksort(int\* array, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int div = Partion(array, left, right);
	quicksort(array, left, div - 1);
	quicksort(array, div + 1, right);
}

3.2.3 双指针(前后指针)

动图演示:

在这里插入图片描述

基本思想:同样选择序列的最左边值为基准值,cur指向第二个元素,prev指向第一个元素,。cur先向右移动,当遇到比key小的元素停下来,然后再与prev的下一个元素交换。一直持续这样的操作,直到cur将所有元素都遍历完,然后再将prev指向的元素与key交换。这样该序列也就被分为两块。在这过程中可以发现,cur和prev中间的元素都是比key大的元素。

代码实现:

int Partion(int\* array, int begin, int end)
{
	int key = array[begin];
	int cur = begin+ 1, prev = begin;
	while (cur <= end)
	{
		if (array[cur] < key && ++prev != cur)
		{
			Swap(&array[cur], &array[prev]);
		}
		++cur;
	}
	Swap(&array[prev], &array[begin]);
	return prev;
}
void quicksort(int\* array, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int div = Partion(array, left, right);
	quicksort(array, left, div - 1);
	quicksort(array, div + 1, right);
}

3.2.4 栈实现 (非递归)

上面三种快速排序算法都是以递归的方法实现的,下面这个方法借用栈来实现非递归的排序算法。
  第一步:将序列的左右标记分别入栈,然后进入循环(判断条件为栈是否为空),再将左右标记出栈;
  第二步:利用刚出栈的这两个参数(左右标记)使用排序算法 Partion(array, left, right),以key为基准,将序列分割称为两部分,左边比key小,右边比key大,然后返回key所在的位置;
  第三步:现在就有两个待排序的序列,它们之间隔着一个刚落好位置的key,将这两部分序列的左右标记分别入栈,要做到与快排同样的基本思想(前序遍历),我们得先将key的右半部分入栈,再入栈key的左半部分。
  最后,当key的左右两边都只有一个元素或没有元素时就停止入栈,那么再进入到循环判断条件时,因为栈空就退出循环,至此非递归的快排就结束了。

int Partion(int\* array, int begin, int end)
{
	int key = array[begin];
	int cur = begin+ 1, prev = begin;
	while (cur <= end)
	{
		if (array[cur] < key && ++prev != cur)
		{
			Swap(&array[cur], &array[prev]);
		}
		++cur;
	}
	Swap(&array[prev], &array[begin]);
	return prev;
}
void QuickSortNor(int\* array, int begin, int end)
{
	Stack s;
	StackInit(&s);
	StackPush(&s, begin);
	StackPush(&s, end);
	while (!StackEmpty(&s))
	{
		int right = StackTop(&s);
		StackPop(&s);
		int left = StackTop(&s);
		StackPop(&s);
		int keyi = Partion(array, left, right);
		if (keyi + 1 < right)
		{
			StackPush(&s, keyi + 1);
			StackPush(&s, right);
		}
		if (left < keyi - 1)
		{
			StackPush(&s, left);
			StackPush(&s, keyi - 1);
		}
	}
	StackDestroy(&s);
}

3.2.5 快排优化(三数取中法+小区间优化)
3.2.5.1 三数取中法

为什么要用到三数取中法来进行优化就是因为,万一选取的基准值刚好是该序列的最小值,那么在一趟排序完成之后并没有什么效果,只是那个基准值的位置刚好落位了,但是它右边的元素都没有任何变化,所以说,为了避免这样的情况发生,我们采取三数取中法来进行优化。
  三数取中法是怎样操作的呢?选序列的最左、右边和中间的三个值比大小,将那个中间值放在最左边作为基准值,然后再去执行排序算法。

代码实现

int GetMiddleIndex(int\* array, int left,int right)
{
	int mid = (left + right) / 2;
	if (array[left] < array[mid])
	{
		if (array[mid] < array[right])
			return mid;
		if (array[right] < array[left])
			return left;
		else
			return right;
	}
	else
	{
		if (array[left] < array[right])
			return left;
		if (array[right] < array[mid])
			return mid;
		else
			return right;
	}
}

3.2.5.2 小区间优化

想一下,快排的思想就是不断地分割小序列,然后再递归实现,它的每一层的递归次数以2倍的次数进行增长。当元素较多时以递归的方法实现是不错的,但是当序列元素较少时,再使用递归就没有必要了,我们可以选择使用其他的排序方法来实现小序列的排序。

void QuickSort(int\* array, int left, int right)
{
	if (right - left < 16)
	{
		// [left, right)区间中数据少到一定程度,使用插入排序来优化
		InsertSort(array + left, right - left);
	}
	else
	{
		int div = Partion(array, left, right);
		QuickSort(array, left, div);
		QuickSort(array, div + 1, right);
	}
}

将这两种优化算法应用到双指针排序算法中:

int GetMiddleIndex(int\* array, int left,int right)
{
	int mid = (left + right) / 2;
	if (array[left] < array[mid])
	{
		if (array[mid] < array[right])
			return mid;
		if (array[right] < array[left])
			return left;
		else
			return right;
	}
	else
	{
		if (array[left] < array[right])
			return left;
		if (array[right] < array[mid])
			return mid;
		else
			return right;
	}
}
int Partion(int\* array, int begin, int end)
{
	int midex = GetMiddleIndex(array, begin, end);
	if (midex != begin) 
	{
		Swap(&array[midex], &array[begin]);
	}
	int key = array[begin];
	int cur = begin+ 1, prev = begin;
	while (cur <= end)
	{
		if (array[cur] < key && ++prev != cur)
		{
			Swap(&array[cur], &array[prev]);
		}
		++cur;
	}
	Swap(&array[prev], &array[begin]);
	return prev;
}
void QuickSort(int\* array, int left, int right)
{
	if (right - left < 16)
	{
		// [left, right)区间中数据少到一定程度,使用插入排序来优化
		InsertSort(array + left, right - left);
	}
	else
	{
		int div = Partion(array, left, right);
		QuickSort(array, left, div);
		QuickSort(array, div + 1, right);
	}
}

快速排序总结:

快速排序的时间复杂度为O(nlogn)
在平均情况下,快速排序总共需要分割 logn 次,即需要 logn 个辅助空间,所以空间复杂度为O(logn)
快速排序是一种不稳定的排序算法。

4. 归并类排序

4.1 归并排序 (二路归并)

动图演示:

在这里插入图片描述

归并排序也是一种基于分治法的排序。归并排序是将原始无序序列分成两个子序列,然后分别对这两个子序列递归地进行排序,最后再将有序子序列合并。

在这里插入图片描述

归并排序是通过不断划分子序列·,直到每个子序列只有一个元素时就停止划分,然后将这两个子序列的元素一一比较合并为一个有序序列。看上图,先对序列不断划分,直到每个子序列只剩下一个元素就开始归并。第一次归并时5和7各自为一个序列, 然后将5和7合并为(5,7),然后再合并1和2为(1,2),下一步将刚刚合并的两个已经各自顺序的序列(5,7)(1,2)进行合并为一个序列为(1,2,5,7),然后再去合并另外一半,当另一半合并完(3,6,8,9),再将这两半序列合并为一个序列就结束了。它的遍历过程大致和后序遍历类似。

void \_MergeSort(int\* array, int begin, int end, int\* tmp)
{
	//当整个序列只剩下一个元素就不再划分
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	//不断地划分子区间
	\_MergeSort(array, begin, mid, tmp);
	\_MergeSort(array, mid + 1, end, tmp);
	//开始归并
	int left1 = begin, right1 = mid;  //第一个序列的左右标记
	int left2 = mid + 1, right2 = end;  //第二个序列的左右标记
	int i = begin;
	while (left1 <= right1&&left2 <= right2)
	{
		//一、先将该归并的元素的放在一个新数组中
		//(新数组的作用仅仅是一个过渡的作用,最终还是要归还已排好序的元素给原数组)
		if (array[left1] < array[left2])
		{
			tmp[i++] = array[left1++];
		}
		else
		{
			tmp[i++] = array[left2++];
		}
	}
	//将另外一个序列中还未添加的元素递补进新数组中
	while (left1 <= right1)
	{
		tmp[i++] = array[left1++];
	}
	while (left2 <= right2)
	{
		tmp[i++] = array[left2++];
	}
	//二、待该归并的元素归并完之后再将这几个元素拷贝到原数组中
	memcpy(array + begin, tmp + begin, sizeof(int)\*(end - begin + 1));
}

void MergeSort(int\* array, int sz)
{
	int\* tmp = (int\*)malloc(sizeof(int)\*sz);
	\_MergeSort(array, 0, sz - 1, tmp);
}

4.1 归并排序(非递归)

归并排序的非递归的大致思路为:第一步设置一个步长 rangeN == 1,根据这个步长控制归并序列的元素个数。同时设置四个标记,分别指向左序列的第一个元素(left1)和最后一个元素(right1),右序列的第一个元素(left2)和最后一个元素(right2),然后从两个序列的左标记开始比对归并。
  第一轮归并时,步长为1,先归并前两个元素(左右序列各一个元素),接着在归并后续的两个元素,一直持续这样的方法直到所有的元素都两两归并完;
  第二轮先将步长rangeN*2(左右序列各自两个元素),然后继续从序列左边开始,先归并前四个元素,接着归并后续四个元素,同样直到所有元素归并完开始下一轮;
  后续的做法都是一样,增加步长再归并,当最终只剩下两个待排序的有序序列时,在完成一次归并之后就完成整个序列的归并。
  根据整个过程来看,它的遍历过程类似于二叉树的层序遍历。

注意: 非递归的归并排序大致思路就是这样,但是其中还是有点小问题,就是每次归并的两个序列的左右标记有可能存在越界的问题。

①:此时进行的是每个序列有两个元素的归并(rangeN == 2),但是整个序列只有九个元素,前八个元素都可以正常进行归并,但是从第九个元素开始,只有第一个序列左标记没有越界;
   ②:同样步长为2,序列只有十个元素,那么第一个序列的左右标记是好的,第二序列的左右标记都会越界;
   ③:步长为2,序列有十一个元素,那么只有第二序列的右标记越界。
   当遇到这样不同的情况,我们要做出不同反应:对于第一种和第二种情况,右序列不存在,而左序列是有序的,所以无需任何操作,直接退出开始下一轮的归并即可。对于第三种来说,左右序列都有元素,所以就得将右序列的右标记进行改动,让其指向最后一个元素即可。

void MergeSortNonR(int\* array, int sz)
{
	int\* tmp = (int\*)malloc(sizeof(int)\*sz);
	int rangeN = 1;
	while (rangeN < sz)
	{
		for (int i = 0; i < sz; i += 2\*rangeN)
		{
			int left1 = i, right1 = rangeN + i - 1;


![img](https://img-blog.csdnimg.cn/img_convert/6d3de0a73fc7f52eb5888277985c0042.png)
![img](https://img-blog.csdnimg.cn/img_convert/fb0394623aefedb477e61f6d23845c5c.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

2,序列只有十个元素,那么第一个序列的左右标记是好的,第二序列的左右标记都会越界;  
>     ③:步长为2,序列有十一个元素,那么只有第二序列的右标记越界。  
>     当遇到这样不同的情况,我们要做出不同反应:对于第一种和第二种情况,右序列不存在,而左序列是有序的,所以无需任何操作,直接退出开始下一轮的归并即可。对于第三种来说,左右序列都有元素,所以就得将右序列的右标记进行改动,让其指向最后一个元素即可。
> 
> 
> 



void MergeSortNonR(int* array, int sz)
{
int* tmp = (int*)malloc(sizeof(int)*sz);
int rangeN = 1;
while (rangeN < sz)
{
for (int i = 0; i < sz; i += 2*rangeN)
{
int left1 = i, right1 = rangeN + i - 1;

[外链图片转存中…(img-0vGz65x5-1714273348820)]
[外链图片转存中…(img-5FflB69n-1714273348820)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构中的八大排序算法,是指常见的八种用于对数据进行排序的算法。这八种算法分别是冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序和基数排序。 冒泡排序是一种简单的排序算法,通过不断比较和交换相邻元素的位置,使得最大(或最小)的元素逐渐往后(或往前)移动。 选择排序是一种简单直观的排序算法,每次选择未排序序列中最小(或最大)的元素,放到已排序序列的末尾。 插入排序是一种简单直观的排序算法,将一个待排序的元素插入到已部分排序的数列中的合适位置。 希尔排序是一种改进的插入排序算法,通过将待排序数列分组,并对每个分组进行插入排序,然后逐渐减小分组规模,最后进行一次插入排序。 归并排序是一种分治思想的排序算法,将待排序数列不断分割成较小的数列,然后再将这些较小的数列按照顺序进行合并。 快速排序是一种分治思想的排序算法,通过选择一个中间的基准元素,将数列分割成两部分,然后分别对这两部分进行排序。 堆排序是一种利用堆这种数据结构排序算法,通过将待排序数列构建成一个大(或小)顶堆,然后逐步将堆顶元素与最后一个元素交换,并调整堆结构。 计数排序是一种非比较型的排序算法,通过统计待排序数列中每个元素出现的次数,然后依次输出即可。 基数排序是一种非比较型的排序算法,通过对待排序数列的每个位进行排序,依次从低位到高位进行。 这里简单介绍了八大排序算法的基本思想和实现方法。在实际应用中,不同的排序算法适用于不同的场景和要求,我们需要根据具体情况选择合适的算法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值