纯手打常见基础排序

冒泡排序

相邻的比较,复合条件就交换,需要走n-1趟
时间复杂度O(n^2)

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
//核心思想:
//相邻的两个比较,满足条件就交换
//从第一个元素开始,每结束一次循环就能确定一个最值
//所以需要循环n-1趟 
int main(){
	int n;
	cin>>n;
	int a[N];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	//核心开始了
	//冒泡排序,只需要走n-1趟 
	for(int i=1;i<n;i++)
		for(int j=1;j<=n-i;j++)
			if(a[j]>a[j+1]) 
				swap(a[j],a[j+1]);
				
	for(int i=1;i<=n;i++)
		cout<<a[i]<<' '; 
	return 0;
}

选择排序

每次选出最小值放在前面
时间复杂度O(n^2)

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
//核心思想
//从第一个开始,每次选出后面最小值,然后交换
// 
int main(){
	int n;cin>>n;
	int a[N];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	//正式开始
	//核心思想: 遍历数组,每次选出最小的放在前面
	for(int i=1;i<n;i++)
		for(int j=i+1;j<=n;j++){
			int min=i;//记录最小值下标 
			if(a[min]>a[j]) 
				min=j;
		swap(a[i],a[min]);
	}
	for(int i=1;i<=n;i++)
		cout<<a[i]<<' ';
	return 0;
}

插入排序

时间复杂度O(n^2)

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
int main(){
	int n;
	cin>>n;
	int a[N];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	//核心
	for(int i=1;i<=n;i++){
		int key=a[i];
		int j=i-1;
		while((j>=1)&&a[j]>key){
			//后移 
			a[j+1]=a[j];
			j--;
		}
		a[j+1]=key;
	} 
	 for(int i=1;i<=n;i++)
	 	cout<<a[i]<<' ';
	return 0;
} 

快速排序

选出一个基数,大于基础的放右边,小于基数的放左边
快速排序的优点在于: 对于 规模大且无序 的数组具有非常高的效率
时间复杂度O(nlogn)

#include<bits/stdc++.h>
using namespace std;
const int N=10005;
//核心思想
//选出一个基数,比基数大的放右边,小的放左边 
void quick_sort(int a[],int l,int r){
	//结束条件 
	if(l>=r)
		return;
	//在做循环的时候,要先自增,所以要先减1 
	int i=l-1,j=r+1,x=a[l+r>>1];
	//循环结束之后,
	//1~~j  <=a[x]
	// j+1~r  >=a[x] 
	while(i<j){
		do{
			i++;
		}while(a[i]<x);
		do{
			j--;
		}while(a[j]>x);
		if(i<j)
			swap(a[i],a[j]); 
	} 
	
	//子问题 
	quick_sort(a,l,j);//左边 
	quick_sort(a,j+1,r);//右边 
}
int main(){
	int n;
	cin>>n;
	int a[N];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	//正式开始
	quick_sort(a,1,n);
	for(int i=1;i<=n;i++)
		cout<<a[i]<<' ';
	return 0;
}

归并排序

本质是: 两个有序数组的合并
时间复杂度 O(nlogn)

#include<bits/stdc++.h>
using namespace std;
//核心思想:
//两个有序数组的合并 
//归并排序,时间复杂度是O(nlogn),但是需要额外的内存 
const int N=100005;
int tmp[N];
void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;
	//先划分为n个独立的子序列 
    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) 
			tmp[k ++ ] = q[i ++ ];
        else 
			tmp[k ++ ] = q[j ++ ];

    while (i <= mid) 
		tmp[k ++ ] = q[i ++ ];
    while (j <= r) 
		tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i <= r; i ++, j ++ ) 
			q[i] = tmp[j];
}
int  main(){
	int n;
	cin>>n;
	int a[N];
	for(int i=1;i<=n;i++)
		cin>>a[i];
	merge_sort(a,1,n);
	for(int i=1;i<=n;i++)
		cout<<a[i]<<' ';
	return 0;
}

堆排序

堆的本质就是一个完全二叉树,只不过哦满足特殊的要求


1. 大顶堆: 每一个 父节点 >= 子节点
2. 小顶堆: 每一个父节点 <= 子节点


根据这一特性,可以使用堆的性质来对元素进行排序


时间复杂度O(nlogn)

步骤:
1.从a[n/2] 到a[1] ,不断调整,每个分支结点与其孩子的值,使其满足大顶堆的性质
2.堆排序:
(1) 头尾交换 堆的长度减1
(2) 把"新堆"调整为大顶堆
(3)循环以上两步,直到只剩下对顶元素

//堆排序时间复杂度 O(nlogn) 
#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int a[N];
void adjust_heap(int a[] , int i , int len){
	// n/2就是父节点的坐标  , i * 2 就指向子节点
	for( i = i * 2 ; i <= len ; i *= 2){
		//此时i指向左孩子
		if(i < len && a[i] < a[i+1])//右孩子大
			i ++ ;
		if(a[i] > a[i/2])//如果孩子 > 父节点,则交换
			swap(a[i], a[i/2]);
		else//父节点大于两个子节点,则不需要交换 
			break; 
	}
}
int main(){
	int n; 
	cin >> n;
	for(int i = 1; i <= n; i ++)	cin >> a[i] ;
	// 建 大顶堆, 必须从下向上 调整
	// [n/2] 为最后一个分支结点(父节点) 
	for(int i = n/2;i >= 1; i--)
		adjust_heap(a,i,n) ;
	cout << endl;
	//堆排序 
	// 堆顶 的元素 肯定是最大的,每次把堆顶的值放最后  
	for(int i = n; i >= 2; i--){
		swap(a[1],a[i]);// 最大值已经到了最后 
		adjust_heap(a,1,i-1);//调整剩余n-1个 
	}
//	 输出
	for(int i = 1; i <= n; i++)	
		cout << a[i] << ' '; 
}

我们常用的 priority_queue的底层实现就是堆,因此可以使用priority_queue来模拟堆排序,但是效率较数组模拟较差

posted @ 2022-11-13 22:27  秋天Code  阅读(19)  评论(0)    收藏  举报