C++七大排序算法

选择排序

  • 排序思想:

  1. 在数组中找到最大值,记录下标maxindex,与数组中最后一个数进行交换 swap(a[maxindex],a[n-1]);

  2. 在剩下的数组元素中,重新找到最大值,并重复第一次操作 swap(a[maxindex],a[n-2]),直到数组排序完毕。

  • 时间复杂度

最优最差都是要遍历数组n*n次,所以时间复杂度为O(n2)

选择排序是不稳定算法:举个栗子,5 8 5 2 9,第一遍之后,2会与5交换,那么原序列中两个5的顺序就被破坏了。所以不是稳定的排序算法。

  • 代码实现

#include<iostream>
using namespace std;


void selectSort(int a[], int n) {
	for (int j = n; j > 1; j--) {
		int max = a[0];
		int maxindex = 0;
		for (int i = 1; i < j; i++) {
			if (a[i] > max) {
				max = a[i];
				maxindex = i;
			}
		}
		swap(a[maxindex], a[j - 1]);
	}
}
void print(int a[], int n) {
	for (int i = 0; i < n; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}


int main() {
	int a[] = { 9,7,5,3,2,1 };
	selectSort(a, 6);
	print(a, 6);
	return 0;
}

冒泡排序

  • 排序思想

  1. 从左到右,比较相邻的元素。如果第一个比第二个大,就交换他们两个,将最大的数放在数组的最后(也就是下标n-1位置

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

  3. 针对所有的元素重复以上的步骤,除了最后一个。

  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

  • 时间复杂度

最快:当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。所以时 间复杂度是O(n);

最慢:当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序 呢,我是闲的吗)。所以时间复杂度是O(n2)

稳定;

  • 代码实现

优化:当数组在整个排序遍历过程中,没有发生交换,说明排序数组已经是有序的了,此时可以直接结束排序过程。

#include <iostream>
using namespace std;
void bubbleSort(int a[], int n) {
	for (int j = n; j > 0; j--) {
		bool flag = true;
		for (int i = 0; i < j-1; i++) {
			if (a[i] > a[i + 1]) {
				flag = false;
				swap(a[i], a[i + 1]);
			}
		}
		if (flag == true) {
			return;
		}
	}
}
void print(int a[], int n) {
	for (int i = 0; i < n; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}


int main() {
	int a[] = { 2,3,5,7,0,4 };
	bubbleSort(a, 6);
	print(a, 6);
	return 0;
}

插入排序

  • 排序思想

  1. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

  2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

  • 时间复杂度

  • 代码实现

优化算法,叫做拆半插入。

#include<iostream>
using namespace std;


void insertSort(int a[], int n) {
	//j代表无序表第一个元素下标
	for (int j = 1; j < n; j++) {
		//i代表有序表最后一个元素下标
		int i = j - 1;
		while (i >= 0 && a[i+1] < a[i]) {
			swap(a[i + 1], a[i]);
			i--;
		}
	}
}
void print(int a[],int n) {
	for (int i = 0; i < n; i++) {
		cout << a[i] << " ";
	}
	cout << endl;
}
int main() {
	int a[] = { 2,4,6,8,0,3 };
	insertSort(a, 6);
	print(a, 6);
	return 0;
}

桶排序

  • 排序思想

  1. 遍历原数组,找到最大值max,申请max+1个桶,初始值设为0,,即vector<int> bucket(max+1,0);

  2. 遍历原数组,找到每个数值对应的桶号,并对桶计数++,即bucket[a[i]]++;

  3. 遍历桶数组,看对应的桶内计数为多少,就取出多少下标值,放到原数组,即while(bucket[i]--) a[index++] = i;

  • 时间复杂度

最快:当输入的数据可以均匀的分配到每一个桶中。

最慢:当输入的数据被分配到了同一个桶中。

  • 代码实现

#include<iostream>
#include<string>
#include<vector>
using namespace std;


void bucket(vector<int>&vec) {
	if (vec.size() == 0) {
		return;
	}
	int max = vec[0];
	for (int i = 1; i < vec.size(); i++) {
		if (max < vec[i]) {
			max = vec[i];
		}
	}
	//申请max+1个桶
	vector<int> bucket(max + 1, 0);
	//遍历原数组,把相应的球放到对应的桶里
	for (int i = 0; i < vec.size(); i++) {
		bucket[vec[i]]++;
	}
	int index = 0;
	//把桶中的球取出来,i代表数值,bucket[i]代表的是个数
	for (int i = 0; i < bucket.size(); i++) {
		while (bucket[i]>0) {
			vec[index++] = i;
			bucket[i]--;
		}
	}
}
void print(vector<int>& vec) {
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i] << " ";
	}
	cout << endl;
}
int main() {
	vector<int> vec = { 2,4,6,1,3,5 };
	bucket(vec);
	print(vec);
	return 0;
}

堆排序

  • 排序思想

  1. 将待排序数组形象成一个堆结构,并将其调整为最大堆结构(左孩子2*i+1,右孩子2*i+2;最大堆结构中任何父节点的值都大于其子节点的值)

  2. 将堆顶元素与待排序数组最后一个元素交换,swap(a[0],a[nums-1]);

  3. 待排序的数据量减少一个,即nums--;将待排序数组重新调整最大堆结构,重复第二步,循环n-1次,将所有数据排序完毕

  • 时间复杂度

堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以堆排序时间复杂度最好和最坏情况下都是O(nlogn)级。

  • 代码实现

#include<iostream>
#include<vector>
using namespace std;


void adjust(vector<int>&vec, int start, int end) {
	int dad = start;
	int son = 2 * dad + 1;
	while (son <= end) {
		if (son + 1 < end&&vec[son] < vec[son + 1]) {
			son++;
		}
		if (vec[son] > vec[dad]) {
			swap(vec[son], vec[dad]);
		}
		dad = son;
		son = 2 * dad + 1;
	}
}
void head_sort(vector<int>&vec) {
	int n = vec.size();
	for (int i = n / 2 - 1; i >= 0; i--) {
		adjust(vec, i, n - 1);
	}
	for (int i = n - 1; i >= 0; i--) {
		swap(vec[0], vec[i]);
		adjust(vec, 0, i - 1);
	}
}


int main() {
	vector<int> vec = { 3,5,7,9,4,1 };
	head_sort(vec);
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i] << " ";
	}
	cout << endl;
	return 0;
}

快速排序

  • 排序思想

  • 三色旗思想:

一个数组只有0,1,2三种元素构成,请使用交换,原地排序而不是使用技术进行排序。

  1. 定义两个变量,i和j(下标);

i:<=i的部分都是比1小的,i的初值 == -1;

j:>=j的部分都是比1大的,j的初值 == n;

  1. 从index = 0开始遍历

如果a[index]==0,swap(a[index++],a[++i]);

(这里index++的原因是,index左边不可能有==2的数据了)

如果a[index]==1,index++;

如果a[index]==1,swap(a[index],a[--j]);

#include <iostream>
#include<vector>
using namespace std;


void Quick(vector<int>&vec, int L, int R) {
	int i = L - 1;
	int j = R + 1;
	int temp = 1;
	int index = 0;
	while (index < j) {
		if (vec[index] == temp) {
			index++;
		}
		else if (vec[index] < temp) {
			swap(vec[index++], vec[++i]);
		}
		else {
			swap(vec[index], vec[--j]);
		}
	}
	
}


int main() {
	vector<int> vec = { 2,1,1,0,0,2 };
	Quick(vec,0, vec.size()-1);
	for (auto it : vec) {
		cout << it << " ";
	}
	return 0;
}

  • 快排思想:

第一步三色旗思想:找一个基准值(这里选择排序数组最左边的那个值),然后将排序数组分成三部分

第一部分:数据全都小于基准值

第二部分:数据全都等于基准值

第三部分:数据全都大于基准值

第二步:

将第一部分和第三部分分别进行三色旗问题,直到 i >= j.

#include <iostream>
#include<vector>
using namespace std;


pair<int,int> Quick(vector<int>&vec, int L, int R) {
	int i = L - 1;
	int j = R + 1;
	int temp = vec[L];
	int index = L;
	while (index < j) {
		if (vec[index] == temp) {
			index++;
		}
		else if (vec[index] < temp) {
			swap(vec[index++], vec[++i]);
		}
		else {
			swap(vec[index], vec[--j]);
		}
	}
	return make_pair(i, j);
}


void Quick_sort(vector<int>&vec, int L, int R) {
	if (L >= R) return;
	pair<int,int> p = Quick(vec, L, R);
	Quick_sort(vec, L, p.first);
	Quick_sort(vec, p.second, R);
}
int main() {
	vector<int> vec = { 2,4,1,9,0,6 };
	Quick_sort(vec,0, vec.size()-1);
	for (auto it : vec) {
		cout << it << " ";
		
	}
	return 0;
}

  • 时间复杂度

归并排序

  • 排序思想

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;

  4. 重复步骤 3 直到某一指针达到序列尾;

  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

  • 时间复杂度

  • 代码实现

#include <iostream>
#include<vector>
using namespace std;


void Merg(vector<int>&vec, int L, int mid, int R) {
	//先申请一个辅助数组大小为:R-L+1;
	vector<int> temp(R - L + 1);
	int i = L, j = mid + 1, index = 0;
	//利用while循环比较两个有序数组,分别为:L到mid,mid+1到R
	while (i <= mid && j <= R) {
		if (vec[i] < vec[j]) {
			temp[index++] = vec[i++];
		}
		else {
			temp[index++] = vec[j++];
		}
	}
	while (i <= mid) {
		temp[index++] = vec[i++];
	}
	while (j <= R) {
		temp[index++] = vec[j++];
	}
	index = L;
	for (int i = 0; i < (R - L + 1); i++) {
		vec[index++] = temp[i];
	}
}
void merg_sort(vector<int>&vec, int L, int R) {
	if (L >= R) return;
	int mid = (R - L) / 2 + L;
	merg_sort(vec, L, mid);
	merg_sort(vec, mid + 1, R);
	Merg(vec, L, mid, R);
}
int main() {
	vector<int>vec = { 20,90,60,70,40,30,50 };
	merg_sort(vec, 0, vec.size() - 1);
	for (auto it : vec) {
		cout << it << " ";
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值