八大排序算法的冒泡排序,快速排序(C实现)

 开局先说一些小概念

什么是排序?

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

什么是稳定性?

 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i] = r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

什么是时间复杂度?

      时间复杂度:算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一 个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。习惯用O(n)渐进表达式

什么是空间复杂度?

       空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度,空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。 空间复杂度计算规则基本跟时间复杂度类似,也使用大O渐进表示法。

注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因 此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。

一,冒泡排序算法

1,思想

非常基础也是非常经典的排序算法,冒泡排序两两进行比较,遍历完一次都会把最大或者最小的元素放在后面,而缺点是,走完一趟只确定了一个数字,整个数组需要走n-1。而第一趟时候,需要比较n-1次,第二趟需要比较n-2次........以此类推,外面大循环是n-1次,里面比较大小的循环是n-1-i次。

时间复杂度:O(N^2);             空间复杂度:O(1);         稳定性:稳定

void bubble_sort(int arr[] ,int sz)
{
	int i = 0;
	int flag = 1;       //优化,要是有序数组,
	for (i = 0; i<sz - 1; i++)        //最好状态,时间复杂度O(n)
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		if (flag == 1)        //要是有序数组
		{
			break;           //跳出大循环
		}
	}
}

二,快速排序

非常重要的排序算法,笔试面试的重点考察知识点!!!!!!

1,思想

      任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{
 if(right - left <= 1)
 return;
 

 // 按照基准值对array数组的 [left, right)区间中的元素进行划分
 int div = partion(array, left, right);
 

 // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
 // 递归排[left, div)
 QuickSort(array, left, div);
 

 // 递归排[div+1, right)
 QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后续只需分析如何按照基准值来对区间中数据进行划分的方式即可。

将区间按照基准值划分为左右两半部分的常见方式有:
hoare法

利用左右两个下标,分别指向头(left)和尾(right),将数组第一个元素设为基准值(key),left往右走去找比key大的值,right往左走去找比key小的值,但是right先走。双方都找到后停止小循环,交换left和right所指向的数组元素值,直到left和right相遇。相遇后交换key和相遇点的元素值(由于right先走,所以相遇点一定比key值小,所以需要交换值),

单趟排序后,1,key达到他的最终位置,排好了key这个数。2,分割了两个子区间,左右子区间,保证了相遇点左边元素都比他小,右边元素都比他大。所以要right先走是非常必要的。

先走和不先走也是有逻辑的,一,左边第一个作为基准值key,那就右边先走,保证了相遇点比key小。

二,右边第一个作为基准值key,那就左边先走,保证了相遇点比key大。

void Swap(int& a, int& b)  //交换值函数,用的引用
{
	int c = a;
	a = b;
	b = c;
}

int part_sort(int* a, int left, int right)    //单趟排序函数
{
	int keyi = left;
	while (left < right)
	{
		//r找小
		while (left < right && a[right] >= a[keyi])   //left < right 
		{                                             //一定要加,不然可能会错过
			--right;
		}
		//L找大
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		if (left < right)
		{
			Swap(a[left], a[right]);
		}
	}
		int meeti = left;
	Swap(a[meeti], a[keyi]);
	return meeti;
}
 
void QuickSort(int* a, int begin, int end)   //快速排序函数
{
	if (begin >= end)
	{
		return;
	}
	
	int keyi = part_sort(a, begin, end);
	//[begin,keyi-1]  keyi    [keyi+1,end]

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);

}

复杂度:快排的单趟排序复杂度是O(n),最好的情况每次key都是中间,一共要递归logN次。最坏的情况是O(n^2),所以总体复杂度是O(N*lonN)。

稳定性:不稳定

改良版本1:三数取中法

优化选择key,主要是针对有序或者接近有序,有序反而复杂度很高,是最坏情况。

用三数取中法,来优化。第一个,中间的,最后一个,选出中间值。

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

 三数取中之后,刚刚的单趟排序只需要加上这个就行了,达到了优化效果。

void Swap(int& a, int& b)  //交换值函数,用的引用
{
	int c = a;
	a = b;
	b = c;
}

int PartSort(int* a,int left,int right)
{
	 int mid = GetMidIndx(a, left, right);   //
	 Swap(mid, left);
	int keyi = left;
	while (left < right)
	{
		//
		while (left < right && a[right]>= a[keyi])
		{
			--right;
		}
		//
		while (left < right && a[left] <= a[keyi])
		{
			++left;
		}
		if(left<right)
		Swap(a[left], a[right]);
	}
	int meeti = left;
	Swap(a[meeti], a[keyi]);
	return meeti;
}

挖坑法(比heore法好一点)建议兄弟们掌握,可以芜湖起飞

思想:

先让右边先走,找到比key小的值,放进刚刚的坑位之中,而这个数的位置就变成了坑,然后左边再走找到比key大的值,放到右边这个新的坑位之中,以此类推。 

int PartSort2(int* a, int left, int right)   //单趟排序
 {
	 int mid = GetMidIndx(a, left, right);   //用了三数取中法,来优化
	 Swap(mid, left);            //刚刚实现了交换函数,参考上一个代码
	 int key = a[left]; 
	 int hole = left;
	 while(left<right)
	 {
		 //                   右边找小,填到左边的坑
		 while (left < right && a[right] >= key)
		 {
			 --right;

		 }
		 a[hole] = a[right];
		 hole=right;
		 //                 左边找大,填到右边的坑
		 while (left < right && a[left] <= key)
		 {
			 ++left;
		 }
		 a[hole] = a[left];
		 hole=left;
	 }
	 a[hole] = key;       //
	 return hole;
 }


void QuickSort(int* a, int begin,int end)   //快速排序算法函数
{
	if (begin >= end)                   //调用上面的单趟排序
	{
		return;
	}

	int keyi = PartSort2(a, begin, end);
	//[begin,keyi-1]  keyi    [keyi+1,end]

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);

}

  前后指针法:

设置两个指针,cur找小,prev紧跟着cur,当prev的下一个值不与cur重合时候,prev的值与key的值的关系是大于等于,交换prev++处与cur处的值。以此类推,直到cur走到了末尾后,交换prev和key的值。相当于一直把大于key的值慢慢往后推了。单趟走完,可以达到在prev的左边都比右边要小。

void Swap(int& a, int& b)  //交换值函数,用的引用
{
	int c = a;
	a = b;
	b = c;
}

 
int PartSort3(int* a, int left, int right)
 {
	 int mid = GetMidIndx(a, left, right);   //三数取中法
	 Swap(mid, left);
	 int keyi = left;
	 int prev = left;
	 int cur = left+1;
	 while (cur <= right)
	 {
          //找小
		if(a[cur]<a[keyi] && ++prev!=cur)   //
		 {
            
       Swap(a[prev], a[cur]);
          }

		++cur;
	 }
	 Swap(a[keyi], a[prev]);
	
	 return prev;
 }


void QuickSort(int* a, int begin,int end)   //快排函数
{
	if (begin >= end)
	{
		return;
	}
	
	int keyi = PartSort3(a, begin, end);
	//[begin,keyi-1]  keyi    [keyi+1,end]

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);

}

总结一下:3种方法都是一个目的,单趟排序之后,确定了一个数的位置,以他为基准点。达到左边的一半,都比右边的一半要小。就是左边一半里面最大的数都比右边一半最小的数,要小。

快速排序就是用二叉树递归的思想,再把左边一半当作整体去递归,右边一半当作整体去递归。一直到有序为止。。。。 

重点,快排还有非递归的实现方式,需要用到数据结构----栈,来实现。

那个太复杂,用起来挫挫的,有兴趣的兄弟们,可以去了解一下。应该没人喜欢用那种方法。

兄弟们,下篇博客写直接插入排序和希尔排序。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值