七种排序算法及其复杂度

1.冒泡排序

时间复杂度:平均:O(N^2),最坏:O(N^2),最好:O(N)

思想:从后往前开始,相邻的两两进行比较,将小的逐渐移到前面。(也可以从前往后开始两两相邻的进行比较)

代码:

void BubbleSort(int *a, int N)
{
	if (a == NULL || N <= 0)
		return ;

	int i, j;
	for (i = 0; i < N; i++)
	{
		for (j = N-1; j > i; j--)
		{
			if (a[j - 1] > a[j])
				swap(&a[j - 1], &a[j]);
		}
	}
}
冒泡排序的优化:若一趟下来没有数据交换,则说明此时数组已经有序,此时算法可以结束。而原来的冒泡排序还是一遍遍比较,直到循环结束。

代码:

void BubbleSort2(int *a, int N)
{
	if (a == NULL || N <= 0)
		return ;

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


2.选择排序

时间复杂度:平均:O(N^2),最坏:O(N^2),最好:O(N^2)

思想:每次从右边中的数选一个最小的和左边当前位置的进行较换

分析:最大特点就是交换移动数据的次数相当少,这样也就节约了相应的时间。性能上略优于冒泡排序。

代码:

void SelectSort(int *a, int N)
{
	if (a == NULL || N <= 0)
		return ;
	
	for (int i = 0; i < N; i++)
	{
		int min = i;
		for (int j = i; j < N; j++)
		{
			if (a[j] < a[min])
				min = j;
		}
		swap(&a[i], &a[min]);
	}
}

3.插入排序

时间复杂度:平均:O(N^2),最坏:O(N^2),最好:O(N)

思想:默认认为位置0-(i-1)是排好序的。将位置i上元素赋给临时变量,与左边一个值判断,若大于左边的,则位置不动。若小于左边的,则将左边的值移到当前位置,临时变量再继续同下一个左边的元素进行比较。

分析:直接插入排序性能要比冒泡排序和选择排序好一些。

代码:

void InsertSort(int *a, int N)
{
	if (a == NULL || N <= 0)
		return ;

	int i, j;
	for (i = 1; i < N; i++)
	{
		int tmp = a[i];
		for (j = i; j > 0; j--)
		{
			if (tmp < a[j - 1])
				a[j] = a[j - 1];
			else
				break;
		}
		a[j] = tmp;
	}
}


4.希尔排序

时间复杂度:平均:O(N^3/2),最坏:O(N^2),最好:O(N)

思想:希尔排序又叫减小增量排序。本质是分组的插入排序。先将元素分成几个分组,每个分组进行插入排序。然后不断减小分组的间隔,直至间隔为1,就变成了插入排序。

分析:增量序列的最后一个增量值必须为1才行。希尔排序并不稳定。
代码:

void ShellSort(int *a, int N)
{
	if (a == NULL || N <= 0)
		return ;

	int gap;
	int i, j;
	for (gap = N/2; gap > 0; gap /= 2)
		for (i = gap; i < N; i++) //实质是插入排序,将a[i]和左边比较,插入到正确位置
		{
			int tmp = a[i];
			for (j = i; j >= gap; j -= gap)  //和插入排序的操作一样
			{
				if (a[j - gap] > tmp)
					a[j] = a[j - gap];
				else
					break; //默认为左边是已经排序的,若比左边的大,说明此时是正确的位置。
			}
			a[j] = tmp;
		}
}


5.堆排序

时间复杂度:平均:O(N*logN),最坏:O(N*logN),最好:O(N*logN)

思想:先将输入数组进行堆化操作,构成一个最小堆,每次将堆顶元素与末尾元素互换,再将剩下的元素恢复堆序。

若使用最小堆,则排序后是递减;若使用最大堆,则排序后是递增。

分析:1.结点i的左右子节点下标分别为2i+1,2i+2;2.结点i的父结点下标为(i-1)/2;3.一颗完全二叉树的最后一个拥有子结点的结点下标为(n/2 - 1),n为结点数

代码:

//下滤操作
void MinHeapFixDown(int *a, int i, int n)
{
	if (a == NULL || i < 0 || n < 0 || i >= n)
		return ;
	
	int tmp = a[i];
	int j = 2 * i + 1;    //左子结点
	while (j < n)
	{
		if (j + 1 < n && a[j + 1] < a[j])    //取左右子结点中小的那个
			j++;
		if (tmp <= a[j])  //父结点比子结点小
			break;
		
		a[i] = a[j];      //将子结点上滤,移到父结点位置
		i = j;            //将父结点移到子结点位置
		j = 2 * i + 1;    //取子结点的子结点
	}
	a[i] = tmp;
}

//堆排序
void HeapSort(int *a, int n)     //用最小堆排序后是递减,用最大堆排序后是递增。
{
	if (a == NULL || n <= 0)
		return ;

	int i;
	for (i = n / 2 - 1; i >= 0; i--)        //堆化操作
		MinHeapFixDown(a, i, n);
	for (i = n - 1; i > 0; i--)
	{
		swap(&a[0], &a[i]);             //交换堆顶元素和堆末尾元素
		MinHeapFixDown(a, 0, i);        //恢复剩余元素的堆序性质
	}
}


6.归并排序

时间复杂度:平均:O(N*logN),最坏:O(N*logN),最好:O(N*logN)

思想:若有两个已排序的数组,合并时谁小就取谁。合并排序就是将左、右边排序后再合并,对于左边部分可递归的再分成左右两部分,每部分拍好序后合并形成左边排序的部分。右边也一样。

void Merge(int *a, int first, int mid, int last, int *tmp)
{
	int lpos = first;
	int rpos = mid + 1;
	int pos = 0;

	while (lpos <= mid && rpos <= last)
	{
		if (a[lpos] < a[rpos])
			tmp[pos++] = a[lpos++];
		else
			tmp[pos++] = a[rpos++];
	}
	while (lpos <= mid)
		tmp[pos++] = a[lpos++];
	while (rpos <= last)
		tmp[pos++] = a[rpos++];

	for (int i = 0; i < pos; i++)
		a[first + i] = tmp[i];
}

void MSort(int *a, int first, int last, int *tmp)
{
	if (first < last)
	{
		int mid = (first + last) / 2;
		MSort(a, first, mid, tmp);        //左边有序
		MSort(a, mid + 1, last, tmp);     //右边有序
		Merge(a, first, mid, last, tmp);  //合并左右两个有序数列
	}
}

void MergeSort(int *a, int n)
{
	if (a == NULL || n <= 0)
		return ;
	int *tmp = new int[n];
	if (tmp == NULL)
		return ;
	MSort(a, 0, n - 1, tmp);
	delete [] tmp;
}

7.快速排序

时间复杂度:平均:O(N*logN),最坏:O(N^2),最好:O(N*logN)

思想:每次从数组中选出一个基准数,再将剩余元素中小于基准数的放在其左边,大于基准数的放在其右边。再继续对左右两部分继续进行相同操作。

分析:下面代码对一般的快速排序进行了优化。

优化部分有:

1.当剩余元素小于5(<=20)个时,进行插入排序,而不是全部都进行快速排序。优点:减小了递归深度,提升了效率。因为对于小数组的排序,快速排序不如插入排序快。

2.采用了三数中值法,而不是取数组中第1个元素。优点:当数组是准排序或逆序时,传统方法会将剩余元素全部移到一边,而三数中值法可避免这种情况,而且还可以提升排序效率。

3.在三数中值法的函数内,对左,中,右三个位置上元素进行排序。优点:本来它们就应该在该位置,提前将它们拍序了,还可以起到警戒标记,不用担心j越界。

4.将基准数放在right-1的位置,不用担心i越界。等一遍排序结束后,再将基准数和i位置元素进行交换。

int Median3(int *a, int left, int right)
{
	int center = (left + right) / 2;
	if (a[left] > a[center])
		swap(&a[left], &a[center]);
	if (a[left] > a[right])
		swap(&a[left], &a[right]);
	if (a[center] > a[right])
		swap(&a[center], &a[right]);
	swap(&a[center], &a[right - 1]);
	return a[right - 1];
}

void QSort(int *a, int left, int right)
{
	if (right - left >= 5)
	{
		int piovt = Median3(a, left, right);
		int i = left;
		int j = right - 1;
		for (;;)
		{
			while (a[++i] < piovt) {}
			while (a[--j] > piovt) {}
			if (i < j)
				swap(&a[i], &a[j]);
			else
				break;
		}
		swap(&a[i], &a[right - 1]);
		QSort(a, left, i - 1);
		QSort(a, i + 1, right);
	}
	else
		InsertSort(a + left, right - left + 1);
}

void QuickSort(int *a, int n)
{
	if (a == NULL || n <= 0)
		return ;
	QSort(a, 0, n - 1);
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中常用的排序算法有冒泡排序、选择排序、插入排序、快速排序、归并排序和堆排序等。每个排序算法时间复杂度不同。 冒泡排序时间复杂度为O(n^2)。每次比较相邻的两个元素,如果顺序错误,则交换位置,重复这个过程直到整个数组排序完成。由于需要多次遍历数组,所以时间复杂度较高。 选择排序时间复杂度也为O(n^2)。每次从未排序的部分中选择最小的元素,然后与未排序部分的第一个元素交换位置。重复这个过程直到整个数组排序完成。 插入排序时间复杂度为O(n^2)。将未排序的元素逐个插入已排序的部分中的正确位置。具体操作是从后往前比较,如果当前元素比前一个元素小,则交换位置,重复这个过程直到整个数组排序完成。 快速排序的平均时间复杂度为O(nlogn)。通过选择一个基准元素,将数组分为两个子数组,其中一个子数组的所有元素小于基准元素,另一个子数组的所有元素大于基准元素。然后对两个子数组分别递归地进行快速排序,最后合并两个子数组得到有序数组。 归并排序时间复杂度也为O(nlogn)。通过将数组递归地拆分成更小的子数组,然后对子数组进行排序,最后将排好序的子数组合并成一个有序数组。 堆排序时间复杂度为O(nlogn)。首先将数组构建成一个最大堆或最小堆,然后不断地将堆顶元素与堆的最后一个元素交换位置,并重新调整堆,重复这个过程直到整个数组排序完成。 综上所述,Python中常用的排序算法及其时间复杂度如上所示。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [常见排序算法及其对应的时间复杂度和空间复杂度](https://blog.csdn.net/weixin_39734493/article/details/110335437)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序](https://blog.csdn.net/weixin_39852276/article/details/110335432)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值