文章目录
1.常用排序算法总结对比
选择原则:
稳定算法:插入,冒泡,归并,基数,计数。
不稳定算法:快速(快),希尔(些),选择(选),堆排(堆)
待排序的记录数目n的大小;
1.数据规模
2.时间空间复杂度要求
3.对排序稳定性的要求
当n较大,则应采用时间复杂度为O(nlogn)的排序方法:
快速排序、堆排序或归并排序序。
2)当n较大,内存空间允许,且要求稳定性:归并排序
当n较小,可采用直接插入或直接选择排序。
直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。
直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序
4)一般不使用或不直接使用传统的冒泡排序。
快排:快速排序的空间复杂度主要取决于递归调用的栈空间和额外的辅助空间。在递归过程中,需要存储每个递归调用的参数、局部变量和返回地址等信息。因此,空间复杂度为O(log n),其中log n是递归调用的深度。
计数:计数排序(Counting Sort)是一种非比较排序算法,适用于排序一定范围内的整数数组。首先遍历数组找出最大最小值,创建新的计数数组大小为最大值-最小值+1,用于统计每个元素出现的频率(i+最小值,i为下标),向新数组大小为n中写入,得到排序后的数组
计数排序的时间复杂度和空间复杂度如下:
时间复杂度:
计数排序的时间复杂度是O(n + k),其中n是待排序数组的大小,k是待排序数组中的最大值与最小值的差值加1。
在计数排序中,首先需要统计每个元素出现的次数,这需要遍历整个待排序数组,时间复杂度为O(n)。
然后,需要计算每个元素在排序结果中的位置,这需要遍历计数数组,时间复杂度为O(k)。
因此,总体上计数排序的时间复杂度是O(n + k)。
空间复杂度:
计数排序的空间复杂度主要取决于计数数组和排序结果数组的大小。
计数数组的大小取决于待排序数组中的最大值与最小值的差值加1,即为k,所以计数数组的空间复杂度是O(k)。
排序结果数组与待排序数组的大小相同,空间复杂度为O(n)。
因此,总体上计数排序的空间复杂度是O(n + k)。
2.冒泡排序(交换)
冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。
从要排序序列的第一个元素开始,一次比较相邻元素的值,发现逆序则交换,将值较大的元素逐渐从前向后移动,每一次都会寻出一个最大值放在后面。如数组:3,9,-1,10,20
大循环次数 数组长度-1,循环长度,数组长度-1-当下循环次数,因为每次循环都会确定一个最大值,下次就不用比较了
优化:如果某次排序中,没有发生交换,则可结束排序
- c
#include <stdio.h>
void bubble_sort(int arr[], int len) {
int i, j, temp;
boolean flag = false;//表示没有发生过交换
for (i = 0; i < len - 1; i++){
for (j = 0; j < len - 1 - i; j++){
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;//发生交换
}
}
if (!flag)
break;
else
flag = false;//重置flag,进行下次判断
}
}
int main() {
int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
int len = (int) sizeof(arr) / sizeof(*arr);
bubble_sort(arr, len);
int i;
for (i = 0; i < len; i++)
printf("%d ", arr[i]);
return 0;
}
- C++
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void bubbleSort(vector<int> &q){
for(int i = q.size() - 1; i > 0; i--){
bool flag = false;
for(int j = 0; j + 1 <= i; j++){
if(q[j] > q[j+1]){
swap(q[j], q[j+1]);
flag = true;
}
}
if(!flag)
break;
}
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
bubbleSort(q);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
3快速排序(交换)
间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序
- C
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort_recursive(int arr[], int start, int end) {
if (start >= end)
return;
int mid = arr[end];
int left = start, right = end - 1;
while (left < right) {
while (arr[left] < mid && left < right)
left++;
while (arr[right] >= mid && left < right)
right--;
swap(&arr[left], &arr[right]);
}
if (arr[left] >= arr[end])
swap(&arr[left], &arr[end]);
else
left++;
if (left)
quick_sort_recursive(arr, start, left - 1);
quick_sort_recursive(arr, left + 1, end);
}
void quick_sort(int arr[], int len) {
quick_sort_recursive(arr, 0, len - 1);
}
- C++
#include <iostream>
#include <algorithm>
#include <vector>
#include <stdlib.h>
using namespace std;
//快速排序(从小到大)
//left为0,right=arr.size()-1;
void quickSort(int left, int right, vector<int>& arr)
{
if(left >= right)
return;
int i, j, base, temp;
i = left, j = right;
base = arr[left]; //取最左边的数为基准数
while (i < j)
{
while (arr[j] >= base && i < j)//<=从大到小
j--;
while (arr[i] <= base && i < j)//>=
i++;
if(i < j)
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//基准数归位
arr[left] = arr[i];
arr[i] = base;
quickSort(left, i - 1, arr);//递归左边
quickSort(i + 1, right, arr);//递归右边
}
4.选择排序(简单选择)
- C
void selection_sort(int a[], int len)
{
int i,j,temp;
for (i = 0 ; i < len - 1 ; i++)
{
int min = i; // 记录最小值,第一个元素默认最小
for (j = i + 1; j < len; j++) // 访问未排序的元素
{
if (a[j] < a[min]) // 找到目前最小值
{
min = j; // 记录最小值
}
}
if(min != i)
{
temp=a[min]; // 交换两个变量
a[min]=a[i];
a[i]=temp;
}
/* swap(&a[min], &a[i]); */ // 使用自定义函数交換
}
}
/*
void swap(int *a,int *b) // 交换两个变量
{
int temp = *a;
*a = *b;
*b = temp;
}
*/
- C++
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void selectionSort(vector<int> &q){
for(int i = 0; i < q.size(); i++){
for(int j = i + 1; j < q.size(); j++){
if(q[i] > q[j])
swap(q[i], q[j]);
}
}
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
selectionSort(q);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
5.选择排序(堆排序)
堆是具有以下性质的完全二叉树
1️⃣每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆(没有要求结点的左孩子的值和右孩子的值的大小关系)
2️⃣ 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
- 堆排序基本思想
将待排序序列构造成一个大顶堆
此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
一般升序采用大顶堆,降序采用小顶堆 - C++
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void push_down(vector<int>& heap, int size, int u){
int t = u, left = u * 2, right = u * 2 + 1;
if(left <= size && heap[left] > heap[t])
t = left;
if(right <= size && heap[right] > heap[t])
t = right;
if(u != t){
swap(heap[u], heap[t]);
push_down(heap, size, t);
}
}
void push_up(vector<int>& heap, int u){
while(u / 2 && heap[u / 2] < heap[u]){
swap(heap[u / 2], heap[u]);
u /= 2;
}
}
void heapSort(vector<int> &q, int n){
int size = n;
for(int i = 1; i <= n; i++)
push_up(q, i);
for(int i = 1; i <= n; i++){
swap(q[1], q[size]);
size--;
push_down(q, size, 1);
}
}
int main(){
int n;
vector<int> q;
cin >> n;
q.resize(n + 1);
for(int i = 1; i <= n; i++)
cin >> q[i];
heapSort(q, n);
for(int i = 1; i <= n; i++)
cout << q[i] << ' ';
cout << endl;
return 0;
}
6.插入排序(简单插入)
基本思想:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时 有序表 中只包含一个元素,无序表中包含有 n-1 个元素,排序过程中每次从无序表中取出第一个元素,与有序表中的元素进行比较,将它插入到有序表中的适当位置,使之成为新的有序表
- C
void insertion_sort(int arr[], int len){
int i,j,temp;
for (i=1;i<len;i++){
temp = arr[i];
for (j=i;j>0 && arr[j-1]>temp;j--)
arr[j] = arr[j-1];
arr[j] = temp;
}
}
- C++
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void insertionSort(vector<int> &q){
for(int i = 1; i < q.size(); i++){
int t = q[i], j;
for(j = i - 1; j >= 0; j--){
if(q[j] > t)
q[j+1] = q[j];
else
break;
}
q[j+1] = t;
}
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
insertionSort(q);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
7.希尔排序(改进选择排序)
- C
void shell_sort(int arr[], int len) {
int gap, i, j;
int temp;
for (gap = len >> 1; gap > 0; gap = gap >> 1)
for (i = gap; i < len; i++) {
temp = arr[i];
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
- C++
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void shellSort(vector<int> &q){
int gap = q.size() / 2;
while(gap){
for(int i = gap; i < q.size(); i += gap){
int t = q[i], j;
for(j = i - gap; j >= 0; j -= gap){
if(q[j] > t)
q[j+gap] = q[j];
else
break;
}
q[j+gap] = t;
}
gap /= 2;
}
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
shellSort(q);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
8.归并排序
- C
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
void merge_sort(int arr[], const int len) {
int reg[len];
merge_sort_recursive(arr, reg, 0, len - 1);
}
- C++
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
void mergeSort(vector<int> &q, int l, int r){
if(l >= r)
return;
int mid = l + r >> 1;
mergeSort(q, l, mid);
mergeSort(q, mid + 1, r);
static vector<int> w;
w.clear();
int i = l, j = mid + 1;
while(i <= mid && j <= r){
if(q[i] > q[j])
w.push_back(q[j++]);
else
w.push_back(q[i++]);
}
while(i <= mid)
w.push_back(q[i++]);
while(j <= mid)
w.push_back(q[j++]);
for(int i : w)
q[l++] = i;
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
mergeSort(q, 0, n - 1);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
9.基数排序(桶排序)
桶排序的思想是我们首先要知道所有待排序的数的数据范围,知道里面的最大值和最小值各是多少。然后利用最大值和最小值分成若干个桶(数据区间),将所有待排序的数按照其大小分到这些桶(数据区间)中。在每个桶中分别利用排序算法进行排序,这样就完成了所有数据的排序。
基数排序radix sort属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
基数排序(Radix Sort)是桶排序的扩展
-
C
-
C++
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int get(int x, int i){
while(i--)
x /= 10;
return x % 10;
}
void radixSort(vector<int> &q, int n){
vector<vector<int>> cnt(10);
for(int i = 0; i < 3; i++){
for(int j = 0; j < 10; j++)
cnt[j].clear();
for(int j = 0; j < n; j++)
cnt[get(q[j], i)].push_back(q[j]);
for(int j = 0, k = 0; j < 10; j++){
for(int x : cnt[j])
q[k++] = x;
}
}
}
int main(){
int n;
vector<int> q;
cin >> n;
for(int i = 0, t; i < n; i++){
cin >> t;
q.push_back(t);
}
radixSort(q, n);
for(auto x : q)
cout << x << ' ';
cout << endl;
return 0;
}
10.堆排序
#include <iostream>
#include <vector>
// 调整堆,使其满足大根堆的性质
void heapify(std::vector<int>& arr, int n, int i) {
int largest = i; // 当前节点索引
int left = 2 * i + 1; // 左子节点索引
int right = 2 * i + 2; // 右子节点索引
// 比较当前节点与左子节点的值
if (left < n && arr[left] > arr[largest])
largest = left;
// 比较当前节点与右子节点的值
if (right < n && arr[right] > arr[largest])
largest = right;
// 如果当前节点不是最大值,则交换节点并继续向下调整堆
if (largest != i) {
std::swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
// 堆排序算法
void heapSort(std::vector<int>& arr) {
int n = arr.size();
// 构建初始堆(最大堆)
for (int i = n / 2 - 1; i >= 0; i--)
heapify(arr, n, i);
// 逐步取出堆顶元素,与堆中的最后一个元素交换,并调整堆
for (int i = n - 1; i >= 0; i--) {
std::swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
// 测试代码
int main() {
std::vector<int> arr = {9, 5, 2, 7, 1, 8, 6};
heapSort(arr);
std::cout << "排序结果:";
for (int num : arr) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}