常见排序算法总结及性能比较

版权声明:本文为博主原创文章,欢迎转载,转载请声明出处! https://blog.csdn.net/hansionz/article/details/82828547

一.常见排序算法有哪些

在这里插入图片描述

二. 插入排序

1.直接插入排序

  • 基本思想:插入排序是每次将一个待排序的记录,按照大小,插入到前面已经排好的有序区中适当的位置,直到将所有记录插入完位置。大概思想是将待排序记录放在数组R[0..N]中,R[0]是有序区R[1..N]为无序区,无序区从R[1]开始按照大小插入到有序区中。

  • 代码实现:

//直接插入排序
int InsertSort(int *num,int len)
{
    assert(num);
    int i = 0;
    //第一个元素已经为有序序列,所以要进行len-1次排序
    for (; i < len - 1; i++)
    {
        int end = i;
        int tmp = num[i + 1];//保存非有序区间第一个元素,否则在后边的移动中会改变
        //比较后移
        while (end >= 0 && num[end]>tmp)
        {
            num[end+1] = num[end];
            --end;
        }
        //插入到适当位置
        num[end + 1] = tmp;
    }
}

单独总结于我的另一篇博客:直接插入排序及优化

2.希尔排序

  • 基本思想:希尔排序是对直接插入排序的优化,它先进行预排序,使得待排序列基本有序,在进行一次直接插入排序,使得待排序列完全有序

  • 具体实现方法:单独总结于我的另一篇博客:希尔排序

三.选择排序

1.直接选择排序

  • 基本思想:刚开始的时候,有序区没有元素,每一趟在无序区中选出来一个最小的元素,然后与无序区第一个元素交换,则此时有序区多一个元素,而无序区少一个元素,直到无序区剩下一个元素结束。 既第一趟排序在R[0…n]中选出一个最小的元素与R[0]交换,第二趟在R[1…n]中选择最小元素,与第一个与R[1]交换,直到无序区的元素只剩下一个排序完成。
  • 代码实现:
//选择排序
void SelectSort(int* num, int len)
{
	 if (num == NULL || len <= 0)
	  return;
	 int i = 0;
	 //1.确定循环躺数
	 for (; i < len - 1; i++)
	 {
	  int minindex = i;
	  int j = i + 1;
	  //2.找到无序区的最小值
	  for (; j < len; j++)
	  {
		   if (num[minindex] > num[j])
		   {
		    minindex = j;
		   }
		  }
		  //与无序区第一个元素交换
		  if (minindex != i)
		   Swap(&num[minindex], &num[i]);
		 }
}
  • 直接排序优化:同时在无序区找出最大值和最小值下标,先后分别和无序区最左边最右边交换(一次确定两个值)
  • 具体实现方法见我的另一篇博客:直接插入排序优化

2.堆排序

  • 基本思想:堆排序本质上是一种树形选择排序。它也是对直接选择排序的一种优化,堆结构在物理存储上也是一种数组,但是它在逻辑上是一棵完全二叉树,在进行堆排序(升序)时,我们可以先建一个大堆最大的元素在堆顶上,我们可以以O(1)的时间找到最大的元素,然后和最后一个元素交换。此时,这个堆的左右子树仍然是一个堆,我们只要把[n-1]个数向下调整一次重新建个大堆即可,直到堆中剩下一个元素,既排序完成

  • 排升序–>建大堆 && 排降序–>建小堆

  • 具体实现见我的另一篇博客:堆排序实现

  • 代码实现:


//向下调堆
void  AdjustDown(int* num, int n, int parent)
{
	 if (num == NULL || n <= 0)
	  return;
	 int child = 2 * parent + 1;
	 while (child < n)
	 {
		  //处理让child指向左右孩子中较大的哪一个
		  if ((child + 1 < n) && (num[child] < num[child + 1]))
		   child++;
		  //比较交换并调整
		  if (num[child]>num[parent])
		  {
		   Swap(&num[child], &num[parent]);
		   parent = child;
		   child = 2 * parent + 1;
		  }
		  else
		  {
		   break;
		  }
	 }
}
//堆排序
void HeapSort(int* num, int len)
{
	 if (num == NULL || len <= 0)
	  return;
	 //1.建堆(升序->大堆,降序->小堆)
	 for (int i = (2 * len - 2) / 2; i >= 0; i--)
	 {
	  AdjustDown(num, len, i);
	 }
	 //2.交换最后一个元素和第一个元素
	 int end = len - 1;
	 while (end > 0)
	 {
		  Swap(&num[0], &num[end]);
		  AdjustDown(num, end, 0);
		  --end;
	 }
}

四.交换排序

1.冒泡排序

  • 基本思想:一次确定一个最大值或者最小值,两两比较,将最大值或者最小交换到最右边或者最左边,N个元素需要N-1趟排序。
  • 代码实现:
//冒泡排序
void BubbleSort(int* num, int len)
{
	 if (num == NULL || len <= 0)
	  return;
	 //确定循环躺数
	 for (int i = 0; i < len - 1; i++)
	 {
		  //确定比较次数
		  for (int j = 0; j < len - 1 - i; j++)
		  {
		   if (num[j]>num[j + 1])
		    Swap(&num[j], &num[j + 1]);
	  	  }
	 }
}

2.快速排序

  • 基本思想:在待排序序列中任意取一个元素作为基准元素,按照该基准元素将待排序序列分为两个子序列左边子序列的值都小于基准值,右边子序列的值都大于基准值。然后把左右子序列当做一个子问题,以同样的方法处理左右子序列,直到所有的元素都排列在相对应的位置上为止。快排是一个递归问题,它是按照二叉递归树的前序路线去划分的。
  • 关于快速排序,我详细将快排的细节总结于我的另一篇博客:快排总结

五.归并排序

1.归并排序

  • 基本思想:归并排序是一个外排序,它可以对磁盘的文件进行排序。它将待排序的元素序列分成两个长度相等的子序列,对每一个子序列排序,然后在将他们合并为一个序列。合并两个子序列的过程称为二路归并。归并排序主要分为两步分组和归并
  • 代码实现:
//归并排序
void MergeSort(int* num, int len)
{
	 if (num == NULL || len <= 0)
	  return;
	 //开辟临时空间,用来存放每次合并后的子序列
	 int* tmp = (int*)malloc(sizeof(int)*len);
	 _MergeSort(num, 0, len - 1, tmp);
	 //释放空间
	 free(tmp);
 	 tmp = NULL;
}
//归并排序分开过程(递归树按照前序路线展开)
void _MergeSort(int* num, int begin, int end,int* tmp)
{
	 assert(num&&tmp);
	 int mid = begin + (end - begin) / 2;
	 //只有一个元素,说明这个序列已经有序
	 if (begin == end)
	  return;
	 //子问题划分左子序列
	 _MergeSort(num, begin, mid, tmp);
	 //子问题划分右子序列
	 _MergeSort(num, mid + 1, end, tmp);
	 //合并两个有序数组
	 Merge(num, begin, mid, mid + 1, end, tmp);
}
//归并排序合并过程
void Merge(int* num, int start1, int end1, int start2, int end2, int* tmp)
{
	 assert(num&&tmp);
	 int begin = start1;
	 int index = start1;//从start1的地方合并
	 //和两条有序单链表的合并的过程类似
	 while ((start1 <= end1) && (start2 <= end2))
	 {
		  if (num[start1] < num[start2])
		  {
		  	 tmp[index++] = num[start1++];
		  }
		  else
		  {
		 	  tmp[index++] = num[start2++];
		  }
	 }
	 //把剩余的合并到tmp上
	 while (start1 <= end1)
	 	 tmp[index++] = num[start1++];
	 while (start2 <= end2)
	  	tmp[index++] = num[start2++];
	 //tmp是个临时空间,最后到把合并的内容拷贝到num上
 	memcpy(num + begin, tmp + begin, sizeof(int)*(end2 - begin + 1));
}

六.各算法性能比较

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hansionz/article/details/82828547