选择排序+快速排序递归版(二)

目录

选择排序

推荐更优化一点的版本:选出最小的数和最左边位置的数交换,选出最大的和最右边的位置的数交换

代码

cpp 复制代码
// 选择排序
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;
	while (begin < end)
	{
		int mint = begin, maxt = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] < a[mint]) mint = i;
			else if (a[i] > a[maxt]) maxt = i;
		}
		swap(&a[begin], &a[mint]);
		if (a[begin] == a[maxt])
		// 如果maxt和begin位置重叠,begin和mint换了,mint位置的值就是最大的了
		{
			maxt = mint;
		} 
		swap(&a[end], &a[maxt]);
		begin++;
		end--;
	}
}

int main()
{
	int a[] = { 2,32,13,4,4,53,12 };
	int n = sizeof(a) / sizeof(a[0]);
	SelectSort(a, n);
	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	return 0;
}

对于if条件那里的解释:

时间复杂度分析:

最好情况是O(N^2) 数组是有序的,也要过一边数组

外面走一遍数组,里面也走一遍数组

最坏情况:O(N^2) 最坏情况也一样

快速排序递归版

快排有三种方法:

1.霍尔排序

2.前后指针

3.挖坑法

  1. 左边找大,右边找小,跟key比较大小,(设key是左边第一个位置的数)然后交换,小的放左边,大的放右边
  2. 这样快排就类型于一个完全二叉树的结构,左边递归,右边也递归
  3. 相遇就停止,让相遇位置的数和最左边的数交换

代码

c 复制代码
// 得到的是数是中间大的(下标)
int Getmidi(int* a, int left, int right)
{
	int mid = left + (right - left) / 2;// 防溢出
	// left mid right
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[left] > a[right])//left right mid
			return left;
		else
			return right;
	}
	else// mid left
	{
		if (a[right] > a[left])
			return left;
		else if (a[right] < a[mid])
			return mid;
		else
			return right;
	}
}
// 快排
void QuickSort(int* a, int left, int right)
{
	// [0,0] [2,1]保证没有这两种情况
	if (left >= right) return;

	// 小区间优化,小于10个数不用分割区间,再递归
	if ((right - left + 1) < 10)
	{
		// 数组不一定在开头0位置
		InsertSort(a + left, right - left + 1);
	}
	else 
	{
		// 三数取中
		int midi = Getmidi(a, left, right);
		swap(&a[midi], &a[left]);

		int keyi = left;
		int begin = left, end = right;
		while (begin < end)
		{
			// 右边找小
			while (begin < end && a[keyi] <= a[end])
				end--;
			// 左边找大
			while (begin < end && a[keyi] >= a[begin])
				begin++;
			// 交换两个数
			swap(&a[begin], &a[end]);
		}
		// 交换keyi位置的值
		swap(&a[begin], &a[keyi]);
		// [begin,keyi - 1] keyi [keyi + 1,end]
		keyi = begin;
		// 递归左区间和右区间
		QuickSort(a, left,keyi - 1);
		QuickSort(a, keyi + 1, right);
	}
}
// 简单快速排序实现
int main()
{
	// 对三数取中的测试
	/*int a[10] = { 10,2,3,4,2,5,7,8,9,6 };
	int left = 0, right = 9;
	int mid = Getmidi(a, left, right);
	printf("%d\n", mid);*/

	int a[10] = { 1,3,342,3442,3,4,2,3,9,10 };
	
	QuickSort(a, 0, 9);
	for (int i = 0; i < 10; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

时间复杂度分析

有logN层,每层都是N个数(遍历了一遍数组)

时间复杂度O(N*logN)

提问

非常重要

  1. int key = a[left]
    swap(&key,&a[begin]) 为什么这样是错的?
    原因是key是局部变量,只是key变成a[begin]的值,而本身数组中的a[left]没有改变
  2. 有序的场景为什么快排会溢出?
    <>递归的深度太深会溢出
    <>左边找大找一次就找到了,右边找小要找到他两相遇也找不到小,相当于遍历一遍数组,keyi = begin,少一个数,如此下去就是等差数列,时间复杂度是O(N*N),这样会导致栈溢出
  3. 为什么Dubug版本会爆,而release不爆?
    <> 因为Dubug版本下有调试信息,优化不高
    <> 有调试的话,每个栈桢会大些,>= 1万就爆了
    <> release版本无调试,栈桢开得小,可容纳量增大
  4. 怎么解决有序的情况(有序的话,时间复杂度为O(N))?
    <> 选keyi可以解决这种情况
    <> 选keyi的方法:选到中间大的数
    <> 1.随机数选keyi
    <> 2.三数取中,最左边,最右边,中间的三个数取中间大的数(用下标,方便选出来的值和最左边交换,保证逻辑不变)
  5. 快排的优化?
    <> 快排的递归类型于完全二叉树,最后几层占总递归数的90%左右
    <> 比如: 对最后五个值的递归进行优化,最后五个值要递归7次

    <> 小区间优化,不再递归分割排序,减少递归的次数
    比如:小于10个数就进行插入排序,因为插入排序是小范围排序中最优的(比冒泡,选择好),大于等于10个数就进行快排
  6. <> a1[i] = rand() + i; 重复不多
    <> a1[i] = rand(); 重复较多,rand()产生的随机数不重复的有3万多个
  7. 为什么相遇位置的数比keyi位置的数小?
  8. 挖坑法对单趟排序是怎么进行优化的?
    挖坑法没有效率的提升,但是更容易理解
    挖坑发是左边是坑,右边先走找小,值放入左边,然后右边是坑,左边走找大,左边值放入右边,然后左边又是坑,如此类推
相关推荐
小奕同学A25 分钟前
坚如磐石的安全堡垒
算法·安全·哈希算法
爱排球爱跳舞的赵小包1 小时前
北航软件算法C4--图部分
算法
IT古董2 小时前
【机器学习】机器学习中用到的高等数学知识-5. 函数空间和泛函分析 (Functional Analysis)
人工智能·算法·机器学习
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-04
人工智能·神经网络·算法·机器学习·语言模型·数据挖掘
Wils0nEdwards2 小时前
Leetcode 回文数
算法·leetcode·职场和发展
可可可可可人2 小时前
leetCode——二进制手表
算法·leetcode
兔兔爱学习兔兔爱学习2 小时前
leetcode219. Contains Duplicate II
javascript·数据结构·算法
Lemon_man_2 小时前
算法——两两交换链表中的节点(leetcode24)
数据结构·算法·链表
vampire-wpre3 小时前
我要成为算法高手-前缀和篇
算法