七种排序算法(C++)

引言

排序中主要包含数据元素的比较交换,本文以C++实现以下七种排序算法,以从小到大排序为例。

如有错误,万望指正。笔者的邮箱为:wuxiaofang555555@163.com 。本文的代码亦可以从笔者的GitHub上获取:https://github.com/wuerfang/Sort/tree/master

  • 排序分类一

    • 简单算法:冒泡排序简单选择排序直接插入排序
    • 改进算法:希尔排序堆排序归并排序快速排序
  • 排序分类二
    在这里插入图片描述

  • 复杂度对比

    排序方法平均情况最好情况最坏情况辅助空间稳定性
    冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)稳定
    简单选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)稳定
    直接插入排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)稳定
    希尔排序 O ( n l o g n ) O(nlogn) O(nlogn)~ O ( n 2 ) O(n^2) O(n2) O ( n 1.3 ) O(n^{1.3}) O(n1.3) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定
    堆排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( 1 ) O(1) O(1)不稳定
    归并排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n ) O(n ) O(n)稳定
    快速排序 O ( n l o g n ) O(nlogn) O(nlogn) O ( n l o g n ) O(nlogn) O(nlogn) O ( n 2 ) O(n^2 ) O(n2) O ( l o g n ) O(logn) O(logn)~ O ( n ) O(n) O(n)不稳定
  • 交换函数 swap

    void swap(vector<int> &v, int i, int j) {
    	int temp = v[i];
    	v[i] = v[j];
    	v[j] = temp;
    }
    

冒泡排序

冒泡排序:两两比较相邻记录的关键字,如果反序则交换,直到没有反序。较小的数字如同气泡般慢慢浮到上面(或是较大的数字慢慢沉到下)。
在这里插入图片描述
时间复杂度为 O ( n 2 ) O(n^2) O(n2),有三种实现方法,如下所示。

  • BubbleSort0(不是真正意义上的冒泡排序)

    void BubbleSort0(vector<int> &v) {
    	for (int i = 0; i < v.size(); ++i) {
    		for (int j = i + 1; j < v.size(); ++j) {
    			if (v[i] > v[j])
    				swap(v, i, j);
    		}
    	}
    }
    
  • BubbleSort(正宗的冒泡排序)

    void BubbleSort(vector<int> &v) {
    	for (int i = 0; i < v.size(); ++i) {
    		for (int j = v.size() - 1; j > i; --j) {
    			if (v[j - 1] > v[j])
    				swap(v, j - 1, j);
    		}
    	}
    }
    
  • BubbleSort2(优化的冒泡排序方法)

    void BubbleSort2(vector<int> &v) {
    	bool flag = true;
    	for (int i = 0; i < v.size() && flag; ++i) {
    		flag = false;
    		for (int j = v.size() - 1; j > i; --j) {
    			if (v[j - 1] > v[j]) {
    				swap(v, j - 1, j);
    				flag = true;
    			}				
    		}
    	}
    }
    

简单选择排序

选择排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2),但性能略优于冒泡排序,因为选择排序交换次数少

在这里插入图片描述

  • SelectionSort

    void SelectionSort(vector<int> &v) {
    	int min;
    	for (int i = 0; i < v.size(); ++i) {
    		min = i;
    		for (int j = i + 1; j < v.size(); ++j) {
    			if (v[min] > v[j])
    				min = j;
    		}
    		if (min != i)
    			swap(v, i, min);
    	}
    }
    

直接插入排序

插入排序的基本思想是将待排序的元素逐个插入已经安排好的序列中,时间复杂度为 O ( n 2 ) O(n^2) O(n2),但性能略优于冒泡排序和选择排序。

在这里插入图片描述

  • InsertSort(两种实现方式)

    //方式一
    void InsertSort(vector<int> &v) {
    	for (int i = 1, j; i < v.size(); ++i) {
    		int t = v[i];				//待插入的元素
    		for (j = i; j > 0; --j) {	//查找插入的位置
    			if (v[j-1] < t)
    				break;
    			v[j] = v[j-1];			//逐个向后移动元素			
    		}
    		v[j] = t;					//将待插入的元素放入正确的位置
    	}
    }
    
    //方式二
    void InsertSort(vector<int> &v) {
      	for (int i = 1, j; i < v.size(); ++i) {
            if (v[i] < v[i-1]) {							//判断待插入元素是否移动位置
                int t = v[i];								//待插入的元素
      			for (j = i; j > 0 && v[j - 1] > t; --j) {	//查找插入的位置		
      				v[j] = v[j - 1];						//逐个向后移动元素
      			}
      			v[j] = t;									//将待插入的元素放入正确的位置
            }  		
      	}
      }
    

希尔排序

希尔排序是对插入排序的一种改进,其将排序的元素分组,分别使用插入排序方法后得到基本有序的排列,时间复杂度为 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2)

  • 排序原理

在这里插入图片描述

  • ShellSort

    void ShellSort(vector<int> &v) {
    	int gap = v.size();
    	do {
    		gap = gap / 3 + 1;
          //gap = gap / 2;
    		for (int i = gap, j; i < v.size(); ++i) {
    			int t = v[i];
    			for (j = i - gap; j >= 0; j -= gap) {
    				if (v[j] < t)
    					break;
    				v[j + gap] = v[j];
    			}
    			v[j + gap] = t;
    		}
    	} while (gap > 1);
    }
    

堆排序

堆排序是对简单选择排序的一种改进,其利用进行排序,将待排序的数据构造成一个大顶堆,此时整个序列最大的最大值就是堆顶的根结点,将其移走(即将其与数组尾元素交换),此时末尾元素就是最大值,然后再将前n-1个数据进行重新构造成一个大顶堆,依次进行。 时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

  • 排序原理

在这里插入图片描述

  • HeapSort

    void HeapSort(vector<int> &v) {
    	for (int i = v.size() / 2 - 1; i >= 0; --i) { //构建大顶堆(利用了完全二叉树的概念)
    		HeapAdjust(v, i, v.size() - 1);
    	}
    	for (int i = v.size() - 1; i > 0; --i) {		
    		swap(v, 0, i);				//将堆顶元素与当前未经排序的尾元素交换
    		HeapAdjust(v, 0, i - 1);	//将子数组v[0...i-1]重新调整为大顶堆			
    	}
    }
    
  • HeapAdjust

    void HeapAdjust(vector<int> &v, int s, int m) {
    	int temp = v[s];
    	for (int j = 2 * s + 1; j <= m; j = 2 * j + 1) {//完全二叉树中左节点为2s+1,右节点为2s+2
    		if (j < m && v[j] < v[j + 1])
    			j++;
    		if (v[j] < temp)
    			break;
    		v[s] = v[j];
    		s = j;
    	}
    	v[s] = temp;
    }
    

归并排序

归并排序就是利用归并的思想实现的排序方法,其假设初始序列含有n个元素,则可以堪称是n个有序的子序列,每个子序列的长度为1,然后两两合并,得到n/2个长度为2或1 的有序序列,再两两合并,…,如此重复,亦称谓2路归并排序。 时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

  • 排序原理

在这里插入图片描述

  • MergeSort

    void MergeSort(vector<int> &v) {
    	MSort(v, v, 0, v.size() - 1);
    }
    
  • MSort

    void MSort(const vector<int> &v, vector<int> &v1, int s, int t) {
    	int m;
      vector<int> v2(v.size());
    	if (s == t) {
    		v1[s] = v[s];
    	}
    	else {
    		m = (s + t) / 2;
    		MSort(v, v2, s, m);		//递归将v[s...m]归并为有序的v2[s...m]
    		MSort(v, v2, m + 1, t);	//递归将v[m+1...t]归并为有序的v2[m+1...t]
    		Merge(v2, v1, s, m, t);	//将有序的v2[s...m]和v2[m+1...t]归并为v1中
    	}
    }
    
  • Merge

    void Merge(const vector<int> &v, vector<int> &v1, int s, int m, int t) {
    	int j, k;
    	for (j = m + 1, k = s; s <= m && j <= t; ++k) {
    		if (v[s] < v[j])
    			v1[k] = v[s++];
    		else
    			v1[k] = v[j++];
    	}
    	if (s <= m) {
    		for (; s <= m; ++s, ++k)
    			v1[k] = v[s];
    	}
    	if (j <= t) {
    		for (; j <= t; ++j, ++k)
    			v1[k] = v[j];
    	}
    }
    

快速排序

快速排序是对冒泡排序的一种改进,其通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行。 时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

  • 排序原理

在这里插入图片描述

  • QuickSort

    void QuickSort(vector<int> &v) {
    	QSort(v, 0, v.size() - 1);
    }
    
  • QSort

    //对子数组进行快速排序
    void QSort(vector<int> &v, int low, int high) {
    	int pivot;	//枢轴
    	if (low < high) {		
    		pivot = Partition(v, low, high);	//将v[low...high]一分为二,并计算出枢轴值pivot
    		QSort(v, low, pivot - 1);			//对低子数组递归排序
    		QSort(v, pivot + 1, high);			//对高子数组递归排序
    	}
    }
    
  • Partition

    //交换待排序数组中的顺序,是位于枢轴pivot左边的值都小于该值,使其右边的值都大于该值
    int Partition(vector<int> &v, int low, int high) {
    	int pivotvalue = v[low];
    	while (low < high) {
    		while (low < high&&v[high] >= pivotvalue)
    			high--;
    		swap(v, low, high);
    		while (low < high&&v[low] <= pivotvalue)
    			low++;
    		swap(v, low, high);
    	}
    	return low;
    }
    
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值