一、冒泡排序
每次从头开始依次比较相邻的两个元素,如果后面一个元素比前一个要大,说明顺序不对,则将它们交换,本次循环完毕之后再次从头开始扫描,直到某次扫描中没有元素交换,说明每个元素都不比它后面的元素大,至此排序完成,是一种稳定的排序最好时间复杂度:O(n)
平均时间复杂度:O(n
2
^2
2)
最坏时间复杂度:O(n
2
^2
2)
void bubble(int a[],int n){
for(int i=n-1;i>0;i--){
for(int j=0;j<i;j++){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
}
}
}
}
二、直接插入排序
假如有n个数,第一趟排序就是比较前两个数将它们排好(默认从小到大),然后在来一个数比较他们三个再排好(即插入到前面的排序),是一种稳定的排序最好时间复杂度:O(n)
平均时间复杂度:O(n
2
^2
2)
最坏时间复杂度:O(n
2
^2
2)
void insert(int a[],int n){
for(int i=1;i<n;i++){
for(int j=i;j>0;j--){
if(a[j]<a[j-1]){
swap(a[j-1],a[j]);
}
}
}
}
三、选择排序
选择排序每一趟排序会确定一个最小值,不稳定最好时间复杂度:O(n
2
^2
2)
平均时间复杂度:O(n
2
^2
2)
最坏时间复杂度:O(n
2
^2
2)
void select(int a[],int n){
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(a[i]>a[j]){
swap(a[i],a[j]);
}
}
}
}
四、快速排序
快速排序是一种对冒泡排序的改进,第一趟排序以中间的一个数为基准,将数组中比他小的数放在此数的左边,比他大的数放在此数的右边,第二趟排序以第一趟排好的左右的中间一个数为基准,在分别重复上面操作,不稳定最好时间复杂度:O(n
l
o
g
n
logn
logn)
平均时间复杂度:O(n
l
o
g
n
logn
logn)
最坏平均复杂度:O(n
2
^2
2)
void quick(int a[],int left,int right){
if(left>=right){
return ;
}
int i=left;
int j=right;
int key=a[i];
while(i<j){
while(i<j && a[j]>=key){
j--;
}
a[i]=a[j];
while(i<j && a[i]<=key){
i++;
}
a[j]=a[i];
}
a[i]=key;
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
五、归并排序
归并算法的核心思想就是分解在合并,也就是分治,分解可以采用递归,设一个数组最右边的元素索引为low,最左边的元素的索引为height,中间元素索引为(low+height)/2,每一次分解可以发现当low==height的时候,整个数组被分解成每一个元素,合并就是将两个有序归并段归并为一个有序的归并段,直到有序为止,是一种稳定的排序最好时间复杂度:O(n
l
o
g
n
logn
logn)
平均时间复杂度:O(n
l
o
g
n
logn
logn)
最坏平均复杂度:O(n
l
o
g
n
logn
logn)
int merge(int left, int mid, int right)
{
int i = left;
int j = mid + 1;
int k = left;
while(i <= mid && j <= right)
{
if(arr[i] <= arr[j])
{
temp[k++] = arr[i++];
}
else
{
temp[k++] = arr[j++];
}
}
while(i <= mid)
{
temp[k++] = arr[i++];
}
while(j <= right)
{
temp[k++] = arr[j++];
}
for(int i = left; i <= right; i++)
{
arr[i] = temp[i];
}
return 0;
}
int merge_sort(int left, int right)
{
int mid;
if(left < right)
{
mid = (left + right) / 2;
merge_sort(left, mid);
merge_sort(mid + 1, right);
merge(left, mid, right);
}
return 0;
}
六、希尔排序
希尔排序相当于对插入排序进行优化,是一种缩小增量排序希尔排序第一趟按照n/2进行分组,每组分别进行直接插入排序,第二趟按照n/2/2进行分组,每组分别进行直接插入排序,直到增量减至为一,整个文件恰好被分为一组,不稳定
void shell(int arr[], int n){
int i, j, k;
int cur, gap;
for (gap = n / 2; gap > 0; gap /= 2){//步长的选取
for (i = 0; i < gap; i++){
//接下来就是插入排序
for (j = i + gap; j < n; j += gap){//每次加上步长
if (arr[j] < arr[j - gap]){
cur = arr[j];
k = j - gap;
while (k >= 0 && arr[k] > cur){//记录后移,查找插入位置
arr[k + gap] = arr[k];
k -= gap;
}
arr[k + gap] = cur;//找到位置插入
}
}
}
}
}
七、堆排序
首先将无需数组构造成一个大根堆(新插入的数据与其父结点比较),固定一个最大值,将剩余的数重新构造成一个大根堆,重复这样的过程。(升序建大堆,降序建小堆),不稳定最好时间复杂度:O(n
l
o
g
n
logn
logn)
平均时间复杂度:O(n
l
o
g
n
logn
logn)
最坏平均复杂度:O(n
l
o
g
n
logn
logn)
父节点:(i-1)/2
左孩子:2*i+1
右孩子:2*i+2
void Heapify(int arr[],int n,int k)//最大堆调整
{
if(k<n)
{
int max=k;//根结点
int s1=2*k+1;//左子节点
int s2=2*k+2;//右子结点
//找出最大结点
if(arr[s1]>arr[max]&&s1<n)
max=s1;
if(arr[s2]>arr[max]&&s2<n)
max=s2;
//交换最大子节点到根结点并做递归
if(max!=k)
{
swap(arr[max],arr[k]);
Heapify(arr,n,max);
}
}
}
void CreateHeap(int arr[],int n)//创建最大堆
{
int last=n-1; //最后一个子结点位置
int parent=(last-1)/2; //最后一个子结点的父结点
for(int i=parent;i>=0;i--)
{
Heapify(arr,n,i); //从最后一个父结点开始做最大堆调整
}
}
void HeapSort(int arr[],int n)//堆排序
{
CreateHeap(arr,n); //创建最大堆
for(int i=n-1;i>=1;i--) //依次将最大堆的根结点(最大值)取出
{
//将最大堆的根(最大值)换到最后
swap(arr[i],arr[0]);
//除去最大值,对交换后的二叉树做最大堆调整,使二叉树根结点始终为最大值
Heapify(arr,i,0);
}
}