简单的几个排序算法

       1.首先介绍最简答的冒泡排序算法,顾名思义就是像水中冒泡一样,每次把当前混乱集合中的第一个数据依次和后面的数据进行比较,然后依次往后面行进。那么每次排一个数据,则需要n-1次排序,每次要和n-1个数据比较

void Sort(int *number,int numberSize)
{
   int i,j,temp;
   for(i = 0;i < numberSize-1;i++)                           //需要n-1趟排序(最后一个数据自然就会有顺序了)
   {
      for(j = 0;j < numberSize-i-1;j++)                      //因为前面的数据和n-1个数据比较了,那么当前数据一定和后面的有序集合的数据
	  {                                                  //已经有大小关系了,那么只需要比较混乱集合即可
	     if(number[j] > number[j+1])                     //从小到大的排序
		 {
		        temp = number[j];
			number[j] = number[j+1];
			number[j+1] = temp;
		 }
	  }
   }
}

显然这段代码的复杂度是O(n^2)时间复杂度。该冒泡算法还可以进行一些小的优化,但是时间复杂度级别还是接近O(n^2)。

       2.直接插入排序,该算法是基于这么一个思想。我给定一串有序的数据,我现在要插入一个数据并且我还想保持数据的顺序。那么我采取的做法就是从前往后或者从后往前挨个比较,然后插入就行。这样对于一串无序的数,我们可以假设第一个数据是有序的,其余的数据属于(n-1)次输入的数据,也就是调用了n-1次插入排序。最后混乱集合就变成了有序集合。

void Sort(int *number,int numberSize)
{
   int i =0,j,Soldier;                                          //这个是存放当前的比较数,为了方便后面减少交换次数的小技巧
   for(i = 1;i < numberSize;i++)
   {
	  Soldier = number[i];
          for(j = i-1;j >= 0;j--)
	  {
	      if(Soldier < number[j])
		  {
		     number[j+1] = number[j];                  //减少交换次数
		  }
		  else
		  {
		     number[j+1] = Soldier;
			 break;
		  }
	  }
	  if(j < 0)
		  number[0] = Soldier;
   }
}

该算法虽然时间复杂度还是在O(n^2)但是却是进行了很多的优化,我们不妨来计算一下,假设输入(n+1)个数据,那么最坏的情况就是初始顺序和我们要排列的顺序相反,完全逆序。那么第一次需要排列1次,第二次2次,。。。。依次类推。那么最后是

(1+2+3+4+。。。。+n) = O(n^2);但是实际上是O(n*(n+1)/2),还是比冒泡的直接O(n^2)要快上不少的。

       3.希尔排序,其实希尔排序就是上面直接插入排序的优化版本。但是希尔算法非常好的利用了直接插入算法的长处,对于尽可能有序的数列进行插入排序的时候可以达到O(n)时间复杂度这个特性,所以希尔算法就是先将数据进行分组,具体怎么分组这个大家可以网上百度,有非常多的分组方法,因为并没有一个定论怎么样分组是最优的。我们这里采用分组数是原始数据个数不停的二分。

void FUZHU_Sort(int *number,int numberSize,int step)
{
   int i,j,k,Soldier;
   for(k = 0;k < step;k++)
   {
	   for(i = k+step;i < numberSize;i+=step)                  //这里和直接插入排序是有区别的,因为并不是0,1,2这样连着排序的,而是
	   {                                                       //0,0+step,0+step+step这样排序的
		  Soldier = number[i];
		  for(j = i-step;j >= 0;j-=step)
		  {
			  if(Soldier < number[j])
			  {
				 number[j+step] = number[j];
			  }
			  else
			  {
				 number[j+step] = Soldier;
				 break;
			  }
		  }
		  if(j < 0) 
			  number[k] = Soldier;                               //多组的起始位置
	   }
   }
}

void Shell_Sort(int *number,int numberSize)
{
   int step = numberSize/2;
   while(step > 0)
   {
       FUZHU_Sort(number,numberSize,step);
	   step = step/2;
   }
}

     4.归并排序,归并排序的思想是,把带排序的数据不停的折半,最后折半到每一半都只有一个数据,那么这个数据肯定是有序的,然后再把左右的归并,归并就是用一个循环来挨个比较两边的数据,小(大)的进入一个临时数组,最后比较完毕再把临时数组放回去。

void Merge_Sort(int *number,int numberSize,int low,int high)
{
        if(low == high)
          return;
        int mid = (low+high)/2;
        //分治
        Merge_Sort(number,numberSize,low,mid);
        Merge_Sort(number,numberSize,mid+1,high);
        //归并
        int i =  low,j = mid+1;
	int *temp = (int *)malloc(sizeof(int)*(high-low+1));
	int count = 0;
	while(i <= mid && j <= high)
	{
	    if(number[i] < number[j])
	      temp[count++] = number[i++];
	    else
	      temp[count++] = number[j++];
	}
	//还有没做完的:
	while(i <= mid)
             temp[count++] = number[i++];
	while(j <= high)
	     temp[count++] = number[j++];
	for(i = low;i <= high;i++)
		number[i] = temp[i-low];
}

归并排序的分治归并的状态方程:T(n) = 2 * T(n/2)+O(n)很容易推出其时间复杂度是O(nlogn)(主定理法是可以直接看出来的,详细见前面的时间复杂度的分析。)

       5.快速排序,思想是每次选取一个基值,然后将每个数和这个数比较,大于它的放后面,小于它的放前面。然后从这个点断开分成两边继续重复这个操作,直到最后一个数。其实也是分治法。

void quick_Sort(int *number,int numberSize,int low,int high)
{
	if(low == high)
	  return;
       //先排序后分治
	int i = low,j = high;                              //首尾计数器
	int mid = (low+high)/2;
	int base_number = number[low];                     //每次以第一个作为基准
	int temp;
	while(i < j)
	{
	    while(number[j] >= base_number && j > i)
			j--;
		if(j <= i)
		  break;
		else
		{
			temp = number[j];
			number[j] = number[i];
			number[i] =temp;
		}
		while(number[i] < base_number && i < j)
			 i++;
		if(i >= j)
          break;
		else
		{
			temp = number[j];
			number[j] = number[i];
			number[i] =temp;
		}
	}
	for(int k = low;k < numberSize;k++)
	{
		if(number[k] == base_number)
			break;
	}
	quick_Sort(number,numberSize,low,k);
	quick_Sort(number,numberSize,k+1,high);
}

本段代码是设置了一个首位计数器,先从尾部开始,大于基数不管,一旦小于基数就和首指针指向的数交换,然后首指针开始运作,一旦首指针指向的数大于基数,那么就和尾指针指向的数交换,然后尾指针开始运作,最后到首尾指针重合结束一次。快速排序的时间复杂度不难看出是O(nlogn)因为分治分方程基本是一样的和前面的。

       以上都是一些简单的常用的排序,当然都是有很大优化空间的,这些可以自己思考,然后一些稍微复杂的排序将会在后序更新。

扫描二维码关注公众号,回复: 2260687 查看本文章

       

猜你喜欢

转载自blog.csdn.net/c_living/article/details/79877888