zl程序教程

您现在的位置是:首页 >  后端

当前栏目

排序算法总结

算法排序 总结
2023-09-14 08:57:33 时间

1、冒泡排序

     

//冒泡排序
      void bubbleSort(int A[], int n)
      {
          bool flag = false;
          for (int i = n - 1; i > 0; i--)
          {
              //注意i,j的终止条件
              for (int j = 0; j < i; j++)
              {
                  if (A[j]>A[j + 1])swap(A[j], A[j + 1]);
                  flag = true;
              }

              if (flag == false)return;
          }

      }
View Code

    (上面代码有误,交换和改变flag值应有大括号)

最差时间复杂度 O(n²) 记录是反序的,需要n-1次扫描,每趟扫描进行n-i次比较,每次比较都要移动3次

最优时间复杂度 O(n) 记录是正序的,一趟扫描即可完成,比较n-1次

平均时间复杂度 O(n²)

虽然冒泡排序不一定进行n-1趟,但记录移动次数多,故平均时间性能比直接插入排序差

最差空间复杂度 O(1)

优点:稳定;

缺点:慢,每次只能移动相邻两个数据

 

2、插入排序

     

//插入排序
      void insertSort(int A[], int n)
      {
          for (int i = 1; i < n; i++)
          {
              //注意j从i-1开始
              for (int j = i - 1; j >= 0;j--)
              if (A[i] < A[j])
                  swap(A[i], A[j]);
          }
      }
View Code

 

 

最差时间复杂度 O(n²)最优时间复杂度 O(n)平均时间复杂度 O(n²)最差空间复杂度 O(n) total, O(1) auxiliary

 

3、希尔排序

 

//希尔排序
      void shellSort(int A[], int n)
      {
          int incrment = n;
          do{
              incrment = n / 3 + 1;
              shellPass(A, n, incrment);
          } while (n >= 1);
      }
      void shellPass(int A[], int n, int d)
      {
          for (int i = d; i < n;i++)
          for (int j = i - d; j >= 0 && A[j]>A[i]; j -= d)//注意j每次自减d,A[j]>A[i]放在判断条件效率更高
          {
              swap(A[i], A[j]);
          }
      }
View Code

希尔排序实际上是一种插入排序, 它的时间复杂度和数组初始排序有关。

平均时间复杂度:O(nlogn)

空间复杂度:O(1)

优点:快,数据移动少;

缺点:不稳定,d的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取。

 4、选择排序

 

//选择排序
      void selectSort(int A[], int n)
      {
          for (int i = 0; i < n - 1;i++)
          for (int j = i + 1; j < n; j++)
          {
              if (A[i]>A[j])
                  swap(A[i], A[j]);
          }
      }
View Code

 

和冒泡排序的思想类似。

最差时间复杂度 О(n²)最优时间复杂度 О(n²)平均时间复杂度 О(n²)最差空间复杂度 О(n) total, O(1) auxiliary

5、归并排序

//归并排序

      void merge(int A[], int first, int mid, int last, int temp[])
      {
          int i = first, j = mid + 1;
          int m = mid, int n = last;
          int k = 0;
          while (i <= m&&j <= n)
          {
              if (A[i] < A[j])
                  temp[k++] = A[i++];
              else
                  temp[k++] = A[j++];
          }

          while (i <= m)
              temp[k++] = A[i++];

          while (j <= n)
              temp[k++] = A[j++];

          for (i = 0; i < k; i++)//注意起始位置
              A[first + i] = temp[k++];
      }

      void mSort(int A[], int first, int last, int temp[])
      {
          int mid = (first + last) / 2;
          mSort(A, first, mid, temp);
          mSort(A, mid + 1, last, temp);

          merge(A, first, mid, last, temp);
      }

      bool mergeSort(int A[], int n)
      {
          //统一接口,只在开始声明数组,提高效率
          int *p = new int[n];
          if (p == nullptr)return false;
          mSort(A, 0, n - 1, p);
          delete[] p;
          return true;
      }
View Code

 

平均时间复杂度:O(nlogn),效率较高

6、堆排序

堆排序包含两个过程:

(1)初建堆

(2)调整堆

初建堆:从非终端结点开始一直到根结点,执行调整堆的过程。这样,就会建立一个大顶堆。

调整堆:对于第二个过程,描述如下,第一次,取出堆顶元素与R[n]进行交换,然后从根结点开始调整堆,根结点与左右孩子中较大者交换,这样,

左右子树的堆会被破坏,继续进行上述操作,直到叶子结点为止。第二次,取出堆顶元素与R[n-1]交换,然后调整堆。

如果排序过程中,是按照从小到大正序,那么建立一个大顶堆。如果是逆序就建立一个小顶堆。

 

 //堆排序

      void adjustHeap(int A[], int i, int n)
      {
          //调整为最大堆
          int temp = A[i];
          for (int j = 2 * i + 1; j < n; j=2*j+1)
          {
              //在子结点中找最大的
              if (j + 1 < n&&A[j + 1] > A[j])
                  j++;
              //如果已经是最大,无需调整
              if (temp >= A[j])break;
              //把较大值往上移
              A[i] = A[j];
              i = j;//调整i
          }
          A[i] = temp;
      }

      void heapSort(int A[], int n)
      {
          //建最大堆
          //从最后一个非叶子结点开始调整,分别调整成最大堆
          for (int i = (n - 1) / 2; i >= 0; i--)
              adjustHeap(A, i, n);

          //排序 
          for (int i = n - 1; i >= 0; i--)
          {
              swap(A[0], A[n - 1]);
              //对剩下n-1个结点进行调整
              adjustHeap(A, 0, n - 1);
          }
      }
View Code

 

7、快速排序

 

它采用了分治的思想:先保证列表的前半部分都小于后半部分,然后分别对前半部分和后半部分排序,这样整个列表就有序了。快速排序的基本算法是:
1. 从数列中挑出一个元素,称为 "基准"(pivot),
2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分割之后,该基准是它的最后位置。这个称为分割(partition)操作。
3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。递回的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递回下去,但是这个算法总会结束,

因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

具体做法:

(1)初始化,设置两个指针ij,初值为区间的上下界,即i=low, j=high;选择无序区的第一个记录R[i]作为基准记录,并将它保存在变量pivot中。

(2)另j从high起向左扫描,直到找到第1个关键字小于pivot.key的记录R[j].将R[j]与基准R[i]交换,是关键字小于pivot.key的记录都移到基准的左边,交换后R[j]中是pivot;然后,令i自i+1位置开始向右扫描,

直到找到第1个关键字大于pivot.key的记录R[i],将R[i]与R[j]交换,交换后,R[i]中存的是pivot,将关键字大于pivot.key的都移动到基准的右边;接着继续扫描,两端往中间靠拢,直到i=j,i就为pivot最终位置。

 

快速排序的时间复杂度是O(nlogn),

最坏情况下时间复杂度是O(n²)。

平均时间复杂度 Θ(nlogn)

最好的情况是枢纽元选取得当,每次都能均匀的划分序列。 时间复杂度O(nlogn)
最坏情况是枢纽元为最大或者最小数字,那么所有数都划分到一个序列去了,即为有序的情况 时间复杂度为O(n^2)

就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n‐1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。

优点: 快速,数据移动少

缺点:不稳定

快速排序被认为是目前基于比较的内部排序中较好的方法,当待排序的关键字随机分布时,快速排序的平均时间最短。

 

堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况,但这两种排序都是不稳定的,归并排序是稳定的。

//快速排序


      int median3(int A[], int left, int right)
      {
          //取主元:取首个,中间,最后元素的中位数
          int mid = (left + right) / 2;
          if (A[left] > A[mid])swap(&A[left], &A[mid]);
          if (A[left] > A[right-1])swap(&A[left], &A[right-1]);
          if (A[mid] > A[right-1])swap(&A[mid], &A[right-1]);
          //同时将主元藏在倒数第二个位置上,只需考虑个[left+1,right-3];
          swap(&A[mid], &A[right - 2]);
          return A[right - 2];
      }

      void quick_sort(int A[], int left, int right)
      {
          int pivot = median3(A, left, right);

          int i = left, j = right - 2;
          for (;;)
          {
              while (A[++i] < pivot);
              while (A[--j] > pivot);

              if (i < j)swap(&A[i], &A[j]);
              else break;
          }

          swap(&A[i], &A[right - 2]);//将pivot交换至正确位置
          quick_sort(A, left, i - 1);
          quick_sort(A, i + 1, right);

      }

      void quickSort(int A[], int n)
      {
          quick_sort(A, 0, n);
      }
View Code

 

void quick_sort(vector<int>& a, int start, int end)
{
	if (a.size() == 0 || end - start <= 1 || start < 0)
		return;

	int pivot_pos = start;
	int pivot = a[start];
	int temp;

	for (int i = start + 1; i < end; i++)
	{
		if (a[i] < pivot)
		{
			if (++pivot_pos != i)
			{
				swap(a[pivot_pos], a[i]);
			}
		}
	}
	a[start] = a[pivot_pos];
	a[pivot_pos] = pivot;
	quick_sort(a, start, pivot_pos - 1);
	quick_sort(a, pivot_pos + 1, end);

}