冒泡排序
利用每趟都确定出一个最大值或者最小值
如果需要排一个从小到大的数组,那么我们每一趟都要确定一个最大值放在最后,一共有n个数,我们最多需要排列n-1趟就可以了,我们可以改进自己的代码,利用一个flag标记,最初flag为0,当需要发生交换的时候,flag修改成1,。
冒泡的时间复杂度最优的时候为O(n),最差的时候为O(n^2).
稳定性:稳定
接下来我们写一下代码
void bubblesort(int*nums,int n){
for(int i=1;i<=n-1;i++){
int flag=0;
for(int j=0;j<=n-1-i;j++){
if(nums[j]>nums[j+1]){
swap(&nums[j],&nums[j+1]);
flag=1;
}
}
if(flag==0){
return ;
}
}
}
排序算法都不需要返回值的。
选择排序
根据排序的名字我们就可以了解到这个排序和选择有关,当然我们需要选择一个最大的或者最小的与待排序数组最后一个发生交换。
每次都要查找最后出待排序数组最大的一个元素,我们采用遍历的形式,最好用index标记最大值的数组下标。
void seletsort(int*nums,int n){
for(int i=1;i<=n-1;i++){
int maxindex=0;
for(int j=1;j<n-i;j++){
if(nums[maxindex]<nums[j]){
maxindex=j;
}
}
if(maxindex!=n-i){
swap(&nums[maxindex],&nums[n-i]);
}
}
}
时间复杂度O(n^2),在性能上优于冒泡,稳定性:不稳定;
因为当几个相同的元素,在筛选的时候会优先选择前边的先排列,会造成元素之间位置交换,所以选择排序在稳定性上并不稳定。
插入排序
将数组分为有序区和无序区,在未排序的时候数组有序区元素个数为1个,nums[0],遍历无序区,
将无序区第一个元素与有序区的元素依次比较,如果无序区元素大于有序区元素,那么不用发生位置变化,如果小于有序区元素,有序区元素就要依次向后移动一位。
代码如下
void insertsort(int*nums,int n){
for(int i=1;i<=n-1;i++){
int j=i-1;
int temp=nums[i];
for(;j>=0;j--){
if(nums[j]>temp){
nums[j+1]=nums[j];
}else{
break;
}
}
nums[j+1]=temp;
}
}
时间复杂度:当最好的情况数组本身就是有序的,O(n),
最糟糕的情况O(n^2);
直接插入排序比冒泡选择的性能好一些。
稳定性分析:稳定的
计数排序(桶排序)
望文生义,桶排序就像一个大桶一样把数据都存储进去,我们把数组比喻成了桶,遍历一遍我们的待排序数组,把数组中成功出现的元素,记录在新数组中
这个新数组我们需要利用calloc申请空间,因为calloc申请的数组默认初始化为0,最后记得要释放内存。
一个数可能在待排序数组中出现好多次我们需要解决好这个问题不能遗漏元素。
桶排序不是元素比较而是利用下标来确定元素的正确位置。
代码如下
首先需要一个函数确定待排序数组中最大的元素是几,由此来确定桶的下标范围
int maxVal(int* nums, int n) {
int max = nums[0];
for (int i = 1; i < n; i++) {
if (nums[i] > max) {
max = nums[i];
}
}
return max;
}
void bucketsort(int* nums, int n) {
int max = maxVal(nums, n);//原数组最大值
int* bucket = (int*)calloc((max + 1), sizeof(int));
//用桶计数,记录元素出现的次数
for (int i = 0; i < n; i++) {
bucket[nums[i]]++;
}
int index = 0;
for (int i = 0; i < =max ; i++) {
while (bucket[i]--) {//计数归零的时候停止
nums[index++] = i;
}
}
}
时间复杂度O(n);
空间复杂度O(n);
稳定排序