排序算法总结



前言

排序算法,是将一个给定的无序数字数组元素按从小到大的顺序进行排序,得到一个有序数组的过程。排序算法种类丰富,下面总结常见的几种排序算法。
话不多说,先来直观感受一下有毒的排序算法视频:http://www.bilibili.com/video/av685670?share_medium=android&share_source=copy_link&bbid=XY59BB55FA50B46679DEEE1CE8DB9A2D3BC6C&ts=1551879120053

冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

示例如下
第一趟遍历索引0~7。首先比较 a r r [ 0 ] &gt; a r r [ 1 ] arr[0]&gt;arr[1] arr[0]>arr[1],则交换 a r r [ 0 ] arr[0] arr[0] a r r [ 1 ] arr[1] arr[1]元素的位置,然后 a r r [ 1 ] &gt; a r r [ 2 ] arr[1]&gt;arr[2] arr[1]>arr[2],继续交换, 6 = a r r [ 2 ] &lt; a r r [ 3 ] = 8 6=arr[2]&lt;arr[3]=8 6=arr[2]<arr[3]=8,不需要交换,6就“冒泡”到位置2不动了,接下来继续两两比较,发现8是个比较大的数,两两比较过程中会一直交换位置直到8“冒泡”到最大的位置,也表明8是这个数组最大的元素;
第二趟遍历索引0~6,索引7的元素已经是最大的不用动。重复上述比较、交换位置的过程,会找出次最大值填放在索引6位置处;
就这样经历七趟就完成了数组的排序过程

数组arr索引01234567
原始数组arr61582352
第1趟15623528
第2趟15235268
第7趟12235568

冒泡排序算法的时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)
C++示例代码

#ifndef SORT_H
#define SORT_H

#include <iostream>
using namespace std;

class Sort {
public:
	void bubbleSort(int arr[],int len) {
		if (arr == NULL || len < 2) {
			return;
		}
		for (int e = len - 1; e > 0; e--) {
			for (int i = 0; i < e; i++) {
				if (arr[i] > arr[i + 1]) {
					swap(arr, i, i + 1);
				}
			}
		}
	}
	
	void swap(int arr[], int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
	
	void printArray(int arr[],int len) {
		if (arr == NULL) {
			return;
		}
		for (int i = 0; i < len; i++) {
			cout<<arr[i] <<" ";
		}
		cout << endl;
	}
};
#endif
#include "sort.h"

void main()
{
	
	int arr[] = { 6, 1, 5, 8, 2, 3, 5, 2 };
	Sort mySort;
	int len = sizeof(arr) / sizeof(int);
	mySort.printArray(arr,len);
	mySort.bubbleSort(arr,len);
	mySort.printArray(arr,len);

	cin.get();
}

在这里插入图片描述

插入排序

插入排序(Insertion Sort)的思想是把一个额外元素插入到一个有序序列的正确位置,这个排序的过程有点像打扑克牌时理牌的过程。

插入排序算法的原理如下:

  1. 序列第一个元素被视为已经有序的子序列
  2. 将有序子序列后面的元素与子序列的元素依次比较,每个元素放在其不大于的子序列某个元素后面
  3. 重新构成的子序列依然有序,重复步骤2
  4. 直到整个序列有序

示例如下
第一趟插入 a r r [ 1 ] arr[1] arr[1],比较 a r r [ 0 ] &gt; a r r [ 1 ] arr[0]&gt;arr[1] arr[0]>arr[1],则交换 a r r [ 0 ] arr[0] arr[0] a r r [ 1 ] arr[1] arr[1]元素的位置,插入完成,子序列索引0~1有序;
第二趟插入 a r r [ 2 ] arr[2] arr[2],交换 a r r [ 1 ] arr[1] arr[1] a r r [ 2 ] arr[2] arr[2]元素的位置,插入完成,子序列索引0~2有序;
就这样经历七趟就完成了数组的排序过程

数组arr索引01234567
原始数组arr61582352
第1趟16582352
第2趟15682352
第7趟12235568

插入排序算法的时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)

C++示例代码

	void insertionSort(int arr[],int len) {
		if (arr == NULL || len < 2) {
			return;
		}
		for (int i = 1; i < len; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

选择排序

选择排序(Selection Sort)的思想就是每次遍历都找出一个全局最小值,并将该最小值提到序列的最前面(或者找出最大值放在序列最后面),然后缩小全局搜索的范围继续搜索,直到序列全部有序。
示例如下
第一趟:搜索0-7范围内最小值为 a r r [ 1 ] = 1 arr[1]=1 arr[1]=1,则交换 a r r [ 0 ] arr[0] arr[0] a r r [ 1 ] arr[1] arr[1]元素的位置;
第二趟:搜索1-7范围内最小值为 a r r [ 4 ] = 2 arr[4]=2 arr[4]=2,则交换 a r r [ 1 ] arr[1] arr[1] a r r [ 4 ] arr[4] arr[4]元素的位置,0~2有序;
就这样经历七趟就完成了数组的排序过程

数组arr索引01234567
原始数组arr61582352
第1趟16582352
第2趟12586352
第7趟12235568

选择排序算法的时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)

C++示例代码

	 void selectionSort(int arr[],int len) {
		if (arr == NULL || len < 2) {
			return;
		}
		for (int i = 0; i < len - 1; i++) {
			int minIndex = i;
			for (int j = i + 1; j < len; j++) {
				minIndex = (arr[j] < arr[minIndex]) ? j : minIndex;
			}
			swap(arr, i, minIndex);
		}
	}
	void swap(int arr[], int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

归并排序

归并排序(Merge Sort)使用了递归的思想进行流程设计,首先把这个序列从正中间一分为二,分别称之为左序列和右序列,并假设左右两个序列已经排好序了,那么我们只需要把左右两个有序序列合并成一个序列就好了,合并两个有序序列采用外排的方法:从左到右比较两个顺列的元素,哪个元素小哪个放在左边,直到全部排序完成。这样大数组就排序好了,但是怎么才能保证左右两个数组有序呢?答案是采用上述方法对左右两个序列也进行归并排序,如此左右划分下去直到子序列的左右序列只有一个元素为止。

示例如下
归并排序由于采用递归的思想,说起来比较绕口,可结合下面的示例理解。
第一次划分:左序列索引0-3,右序列4-7;
第二次划分:由于左序列无序,无法进行合并操作,先对左序列做归并排序:子左序列索引0-1,子右序列2-3;
第三次划分:左序列:0,右序列1,不能继续划分,开始合并;
第一次合并

数组arr索引01234567
原始数组arr61582352
第1划分(6158 )( 2352)
第2划分((61 )(58 ))(( 23 )(52))
第1次合并((16 )(58 ))(( 23 )(25))
第2次合并(1568 )( 2235)
第三次合并12235568

选择排序算法的时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( n ) O(n) O(n)

	 void mergeSort(int arr[],int len) {
		 if (arr == NULL || len < 2) {
			 return;
		 }
		 mergeSort(arr, 0, len - 1);
	 }

	 void mergeSort(int arr[], int l, int r) {
		 if (l == r) {
			 return;
		 }
		 int mid = l + ((r - l) >> 1);
		 mergeSort(arr, l, mid);
		 mergeSort(arr, mid + 1, r);
		 merge(arr, l, mid, r);
	 }

	 void merge(int arr[], int l, int m, int r) {
		 int* help = new int[r - l + 1];
		 int i = 0;
		 int p1 = l;
		 int p2 = m + 1;
		 while (p1 <= m && p2 <= r) {
			 help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		 }
		 while (p1 <= m) {
			 help[i++] = arr[p1++];
		 }
		 while (p2 <= r) {
			 help[i++] = arr[p2++];
		 }
		 for (i = 0; i < r - l + 1; i++) {
			 arr[l + i] = help[i];
		 }
		 delete[] help;
	 }

快速排序

快速排序(Quick Sort)是一种比较高效的排序方法,它的思想是选取序列中的一个数作为基准,将小于基准的元素全部放在数组左边,大于基准的元素全部放在数组的右边,等于基准的元素放在中间,这样就把原数组分成了左右两个部分,然后对每个部分采用同样的策略递归下去,直到整个数组有序。基准值一般选择待排序列的最后一个元素,但是为了避免最差情况,也可以优化为随机选取一个数作为基准,这时就称之为随机快速排序。

示例如下
快速排序同样采用递归的思想,结合下面的示例理解。
第一次划分:以第八个元素2作为划分基准左边部分只有一个1,不需要排序,右边部分索引3-7;
第二次划分:由于索引3-7上8为最大值,所以划分后右部分为空,左部分索引3-6;
第三次划分:同理,左部分索引3-5;
第四次划分:左部分只有一个3,划分结束,同时排序完成。
可见随机快排还是有必要的。

数组arr索引01234567
原始数组arr61582352
第1划分12253568
第2划分12253568
第3划分12253568
第4划分12235568
最终12235568

选择排序算法的时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( l o g n ) O(logn) O(logn)

 void quickSort(int arr[],int len) {
		 if (arr == NULL || len < 2) {
			 return;
		 }
		 quickSort(arr, 0, len - 1);
	 }

	 void quickSort(int arr[], int l, int r) {
		 if (l < r) {
			 swap(arr, l + (int)((double)rand() / RAND_MAX * (r - l + 1)), r);
			 int *p=partition(arr, l, r);
			 quickSort(arr, l, p[0] - 1);
			 quickSort(arr, p[1] + 1, r);
			 
		 }
	 }

	 int* partition(int arr[], int l, int r) {
		 int less = l - 1;
		 int more = r;
		 while (l < more) {
			 if (arr[l] < arr[r]) {
				 swap(arr, ++less, l++);
			 }
			 else if (arr[l] > arr[r]) {
				 swap(arr, --more, l);
			 }
			 else {
				 l++;
			 }
		 }
		 swap(arr, more, r);
		 return new int[2] { less + 1, more };
	 }

堆排序

堆排序(Heap Sort)利用的是一种特殊的完全二叉树结构–大根堆 这种数据结构的一个特征:大根堆最上面元素为最大元素。于是堆排序的调整策略就变成:首先把一个数组变为大根堆;其次把最大元素与数组最后一个元素交换;将除去最后一个元素的剩余数组调整成大根堆,重复上述操作,数组就排好序了。

示例如下
第一步:调整为大根堆;
第二步:取最大元素后继续调整为大根堆
多次重复上述过程,排序完成。

数组arr索引01234567
原始数组arr61582352
第1步86522351
取最大值16522358
第二步62512358
最终12235568

选择排序算法的时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( 1 ) O(1) O(1)

 void heapSort(int arr[],int len) {
		 if (arr == NULL || len < 2) {
			 return;
		 }
		 for (int i = 0; i < len; i++) {
			 heapInsert(arr, i);
		 }
		 int size = len;
		 swap(arr, 0, --size);
		 while (size > 0) {
			 heapify(arr, 0, size);
			 swap(arr, 0, --size);
		 }
	 }

	 void heapInsert(int arr[], int index) {
		 while (arr[index] > arr[(index - 1) / 2]) {
			 swap(arr, index, (index - 1) / 2);
			 index = (index - 1) / 2;
		 }
	 }

	 void heapify(int arr[], int index, int size) {
		 int left = index * 2 + 1;
		 while (left < size) {
			 int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;
			 largest = arr[largest] > arr[index] ? largest : index;
			 if (largest == index) {
				 break;
			 }
			 swap(arr, largest, index);
			 index = largest;
			 left = index * 2 + 1;
		 }
	 }

桶排序

简单介绍一下桶排序的思想:假设有一亿个数,数值范围保持在0–99之间,要对这样的数组进行排数,可以通过统计0–99这一百个数各出现了多少次,然后再按0–99的顺序对原数组进行复写,每个数统计到了几次就写几个,这样数组就排好了。这一百个数称之为一百个“桶”,用来计数。
桶排序算法的时间复杂度 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)

其他排序

待续,。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值