1.冒泡排序
1>比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2>每趟从第一对相邻元素开始,对每一对相邻元素作同样的工作,直到最后一对。
3>针对所有的元素重复以上的步骤,除了已排序过的元素(每趟排序后的最后一个元素),直到没有任何一对数字需要比较。
经过 i次扫描后,数列的末尾 i 项必然是最大的 i 项,因此冒泡排序最多需要扫描 n-1遍数组就能完成排序。
时间复杂度:
在序列完全有序时,冒泡排序只需遍历一遍数组,不用执行任何交换操作,时间复杂度为 O(n)。
在最坏情况下,冒泡排序要执行 (n-1)n/2次交换操作,时间复杂度为 O(n^2)。
冒泡排序的平均时间复杂度为 O(n^2)。
稳定性:
在冒泡排序中,遇到相等的值,是不进行交换的,只有遇到不相等的值才进行交换,所以是稳定的排序方式。
#include <stdio.h>
void bubble (int a[ ], int n);
int main()
{
int n, a[10];
scanf("%d", &n);
for (int i=0; i<=n-1;i++)
scanf("%d",&a[i]);
bubble(a,n);//冒泡函数调用
for (int i=0; i<=n-1;i++)
printf("%d ",a[i]);
return 0;
}
void bubble (int a[ ], int n)
{
int t;
for(int i=0;i<n-1;i++)
{
for(int j=i;j<n;j++)
{
if(a[i]>a[j])
{
t=a[i];
a[i]=a[j];
a[j]=t;
}
}
}
}
或者写成这样
//初始化的时候从a[1]开始
void bubble (int a[ ], int n)
{
int t;
for(int i=1;i<=n-1;i++)
{
for(int j=1;j<=n-i;j++)
{
if(a[j]>a[j+1])
{
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
}
外循环优化
如果某次比较过程中,发现没有任何元素移动,则不再进行接下来的比较。
//初始化的时候从a[1]开始
void bubble (int a[ ], int n)
{
int t;
bool flag; //用来判断该趟是否有元素交换;
for(int i=1;i<=n-1;i++)
{
flag=false;
for(int j=1;j<=n-i;j++)
{
if(a[j]>a[j+1])
{
flag=true;//发生交换进行更新
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
if(!flag)break;
}
}
内循环优化
外循环优化方式,只能在“趟”的级别上优化。
内循环优化方式,也就是要实现在“次”的级别进行优化,其思路是“记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可”。
void bubble (int a[ ], int n)
{
int t;
bool flag; //用来判断该趟是否有元素交换;
int p=n-1;
for(int i=1;i<=n-1;i++)
{
int np=0;//用来记录每次交换的位置
flag=false;
for(int j=1;j<=p;j++)
{
if(a[j]>a[j+1])
{
flag=true;//发生交换进行更新
t=a[j];
a[j]=a[j+1];
a[j+1]=t;
np=j;
}
}
if(!flag)break;
p=np;
}
}
双向遍历
2.选择排序
它的工作原理是每次找出第 i小的元素(也就是 a 中最小的元素),然后将这个元素与数组第 i 个位置上的元素交换。
时间复杂度:
选择排序的时间复杂度为O(n^2)。
稳定性:
由于 swap(交换两个元素)操作的存在,选择排序是一种不稳定的排序算法。
#include<stdio.h>
void selection(int a[], int n)
{
for (int i = 1; i < n; i++)
{
int ith = i;//
for (int j = i + 1; j <= n; ++j)
{
if (a[j] < a[ith])
{
ith = j;//记录最小的数的位置
}
}
//进行交换
int t = a[i];
a[i] = a[ith];
a[ith] = t;
}
}
int main()
{
int a[10001];
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
selection(a,n);
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
同时查找最大和最小优化
选择排序的优化思路一般是在一趟遍历中,同时找出最大值与最小值,放到数组两端,这样就能将遍历的趟数减少一半。
void selection(int a[],int n)
{
/*初始化左端、右端元素索引*/
int left = 1;
int right = n;
while (left < right)
{
/*初始化最小值、最大值元素的索引*/
int min = left;
int max = right;
for (int i = left; i <= right; i++)
{
/*标记每趟比较中最大值和最小值的元素对应的索引min、max*/
if (a[i] < a[min])
min = i;
if (a[i] > a[max])
max = i;
}
/*最大值放在最右端*/
int temp = a[max];
a[max] = a[right];
a[right] = temp;
/*此处是先排最大值的位置,所以得考虑最小值(arr[min])在最大位置(right)的情况*/
if (min == right)
min = max;
/*最小值放在最左端*/
temp = a[min];
a[min] = a[left];
a[left] = temp;
/*每趟遍历,元素总个数减少2,左右端各减少1,left和right索引分别向内移动1*/
left++;
right--;
}
}
3.插入排序
它的工作原理为将待排列元素划分为“已排序”和“未排序”两部分,每次从“未排序的”元素中选择一个插入到“已排序的”元素中的正确位置。
时间复杂度
插入排序的最优时间复杂度为O(n) ,在数列几乎有序时效率很高。
插入排序的最坏时间复杂度和平均时间复杂度都为O(n^2) 。
稳定性
插入排序是一种稳定的排序算法。
#include<stdio.h>
void insertion(int a[], int n)//对a1-an进行插入排序
{
for (int i = 2; i <= n; i++)
{
int key = a[i];//用来判断元素是否有序
int j = i - 1;
while (j > 0 && a[j] > key)
{
a[j + 1] = a[j];
--j;
}
a[j + 1] = key;
}
}
int main()
{
int a[100];
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
insertion(a,n);
for(int i=1;i<=n;i++)
printf("%d ",a[i]);A
return 0;
}