排序算法的优化 拓展

、、

摘要

此前,,,大致讲了一下编程中的一些排序算法。。。
但是这些算法    的实现过程 中  ,,可能会遇到很多特殊的排序场景,,,所以就会出现一些不易察觉的错误 ,,,,下面我就来简要的说说可能遇到的错误 。。。。。
还有就是一些,,,排序算法的优化使用。。。。



选择排序的优化

我们经常说说的 排序 选择的基本思路 ;;;;就是 从 一个数组中个每次找到一个 最大(最小)值,放到适当的位置 ,,,此算法的时间复杂度大致为O(N*N);;;;
但是 我们在面试 的时候  ,,,经常会遇到    这种问题 "让我们写一个选择排序的算法"
一般情况下,,我们写出来的代码是这个样子的
//选择排序
 //时间复杂度 为 N*N
void SelectSort(int * array,const size_t n)
{
	assert(array);
	int end  = n;//endl
	while(end > 0)
	{
		end--;
		int maxidex = 0 ;
		//单趟排序 
		for(size_t  i = 0 ;i <= end;++i)
		{
			//找到最大的值的下标
			if(array[i] > array[maxidex])
			{
				maxidex = i;
			}
		}
		swap(array[maxidex],array[end]);
	}	
}

如果要是 写成这样子之后 ,,,面试官肯定是不满意的,,,,,
让我们尝试优化优化,,,,;;;
那么我们就要思考了 ,,要怎么样  优化呢 ???

我们 可以从本质上找解决方法,,,,,,
选择排序的思想是   找一个极端值 ,,,放到适当的位置  ,,,,,
那么,,我们 为什么 不能找到两个极值,,,(一个最大值,,,一个最小值 )
(最小值放到左边,最大值放到右边)   假设是排升序;;;
代码写法
  
//选择排序的优化  
//同时 找的最大的还有最小的 
//时间复杂度为  N*N
//求时间复杂度  此时 不要 只看循环的次数 ,,,要看结构 
//两个循环都是 都是从两边到中间 N次
void SelectSort(int *  array,const size_t  n)
{
	assert(array);
	int  left =  0;//left表示的是最左位   
	int right  = n-1;   
	while(left < right)
	{
		int  maxidex  = left ;//先定义一个基值
		int  minidex  = left;
		//单趟排序    从  left 走到right
		for(size_t   i =  left;i <=right;++i)
		{
			if(array[i]> array[maxidex])//得到最大值的下标
			{
				maxidex  =  i;
			}
			if(array[i] < array[minidex] )//得到最小值的下标 
			{
				minidex  =  i ;
			}
		}
		//此处要注意 的是  ,,,要是 maxidex的值正好就是left
		//如果要是交换的话 ,,那么就要将 最大值的位置  更改 
		swap(array[left],array[minidex]);
		swap(array[right],array[maxidex]);
		++left;
		--right;
	}
} 

  代码写完之后一调试,,没有错误 ,,,得到的结果也是正确的,,但是 这就正确了 吗???。。。

但是要是使用这组数据 
{9,5,4,9,3,6,8,7,1,0};
进行调试的话,,,,
最后排序生成的数列就是 这个样子的


为什么会出现这个情况呢????
那是因为当第一次进行单趟排序的时候 ,,,
得到的
maxidex  =  0  ;
minidex   = 9   ;
left  =  0;
right  =  9;

用图案来看看吧


我们明显可以看出  
最大值的下标maxidex  与  和left相等的话,,,,,
那么这段代码就会出现问题 ,,,,(也就是说 maxidex的值已经 被改变了)

所以,我们必须在交换最大值 的时候 判断一下,,,最大的值 是不是 已经改变了。。

//此处要注意 的是  ,,,要是 maxidex的值正好就是left
		//如果要是交换的话 ,,那么就要将 最大值的位置  更改 
		swap(array[left],array[minidex]);

		if(maxidex == left)//更改最大的值的位置  
		{
			maxidex  = minidex;
		}
		swap(array[right],array[maxidex]);
		++left;
		--right;

快速排序 优化。。。

快速排序是面试官很喜欢考的一个问题,,,,因为它看上去不是很好理解。。。
关于,,,,
对于 一个排序 来说 ,,,
要写的就是 先写单趟排序 ,,,,

快速排序的主要思想的 取一个值作为关键值 ,,
每一趟排序,,,,就是 把大于关键值的放到右边,,,,小于关键值的放到左边。。。。


对于这样的 单趟排序   ,,,,网上有 提供了三种方法,,,来解决这种方法。。。。

1、、、左右指针交换法

主要思想就是,,,从左边找到大于关键值的    ,,,从右边找到小于关键值的  然后将两个值交换 

代码的实现
//1、左右指针交换法
int PartSort(int *  array,int left,int right)
{
	//取一个中间值作为   关键字
	int key  =  right;
	while(left < right)
	{
		//从左边找到一个比关键值大的
		while(left<right&& array[left] <=array[key] )//等号必须加上  否则出现错误(死循环)
		{
			++left;
		}
		//从右边找到一个 比 关键值小的
		while(left < right && array[right] >= array[key])
		{
			--right;
		}
		swap(array[left],array[right]);
	}
	swap(array[left],array[key]);
	return  left;//需要返回一个中间值  
}

在这里 有一个 ,,,重要的地方 必须提一下;;就是这句代码
while(left<right&& array[left] <=array[key] )//等号必须加上  否则出现错误(死循环)
要是 不加上等号的话


2、、、挖坑法

所谓的挖坑法,,,与左右指针交换法的原理 是 差不多的,,,,就是 

先选一个值作为key值,,,, 并且将此位置 当做 一个坑,,,
从 另一端开始 ,,,向前走,,,
假设将 最右端的值 当做 是 key ,,,, ,,,那么将 此位置当做是坑,,,

在从左端,,开始 找到一个比 key大的值 ,,将此位置的值 ,,,放到坑里 ,,,,再将这个位置 当做是 坑 ,,,从 右往前继续判断找比key小的值 


代码实现
int PartSort(int * array,int left,int right)
{
	int key  =  array[right];//得到key值 
	while(left < right)
	{
		//将right作为坑
		//找到比key大的数  填到 坑中
		while(left < right  && array[left]<= key)
		{
			left++;
		}
		array[right] = array[left];
		if(left< right)
			--right;
		while(left <right&& array[right]>= key)
		{
			--right;
		}
		array[left]  = array[right];
		if(left<right)
			left++;
		//          0 1 2 3 4 5
	}
	array[left]  =key;
	return  left;
}


3、、、前后指针法


前后指针是一个非常实用的方法,,,

如果要是有一个题 ,,让我们将 一个单链表里的数据 进行排序的话,,那么这里的前两种方法都不能 使用了;;;
在这只能使用 下面这种方法了。。。

所谓前后 指针法就是 ,,,

找一个key值   ,,,

定义两个  变量 cur  ,,prev  ,,一个指向当前的数据 ,,一个指向的是 前一个数据。。。

假设key值取最右边的值;;;
那么我们 cur  可取下标 0;
prev 取-1;

然后 判断 ,,要是 cur上的值 比 key小的话 ,,,,那么++prev;;;;交换  cur与prev上的值 ,,,
否则 prev  不变 ,,, cur 继续向前走  。。。

代码实现
int  PartSort(int *array,int left,int right)
{
	//assert(array);
	int cur = left;
	int prev = left-1;//用来表示 的是 比key大的前一个下标 
	int key  = array[right];
	while(cur < right)
	{
		if(array[cur] <= key)//找到一个比 key小的
		{
			prev++;//找到比 key大的 
			swap(array[cur],array[prev]);//交换 把  大的   放到后面 
		}
		cur++;//cur向后走 
	}
	++prev;
	swap(array[right],array[prev]);
	return prev;
}


总体代码的实现
void QuickSort(int * array,int left,int right)
{
	assert(array);
	//如果要是 
	if(right-left  <20)
	{
		InsertSort(array+left,right-left+1);
		return ;
	}
	int ret = PartSort2(array,left,right);//得到分割点
	//将左边的进行 排序  
	QuickSort1(array,left,ret-1);
	//将右边的进行 排序
	QuickSort1(array,ret+1,right);
}


对于快速排序的优化

快速排序的时间复杂度,,,大致  可以看成是  O(N*logN)
但是 ,,当每次选的key值 是 一个极值的话,,,,那么,,算法时间复杂度就是 最坏的情况。。O(N*N)


对于此种场景我们提供了一种方法,,,就是 ,, 三数取中法
就是每次的单趟排序时,,都要,将左右端的值 与中间的值比较找到中间值,,,当做是 key,,,
可以明显的增加速率

还有的就是 如果当单趟排序元素的个数小的时候,,,使用快速排序法,,,,,

所以当排序元素小的时候我们可以使用插入排序,,,,,来减少时间复杂度。。。。
我们称之为   

小区间优化。。。
 

冒泡排序  优化

我们都知道冒泡排序很是很简单,,,但是这类算法可以

优化

要是 ,,数组元素依然有序,,,那么


就不需要交换了 。。 。。 
代码实现
void BubbleSort(int *array,const size_t n  )
{
	assert(array);
	for(size_t i = 0 ;i < n-1;++i)
	{
		//单趟排序 
		//优化  使用一个数 来记录交换的次数 
		int j = 0;
		int count = 0 ;
		for(;j < n-1-i;++j)
		{
			if(array[j] > array[j+1])
			{
				swap(array[j],array[j+1]);
				count++;
			}
		}
		if(count == 0)//要是交换的次数 为0 表示 已经 是有序的了
			break;
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值