几种常见的排序算法<学习笔记>

排序算法

排序算法分为内部排序外部排序,内部排序是数据记录在内存中进行排序,而外部排序由于数据量大,排序过程中需要访问外存。

内部算法:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。

1.冒泡排序[bubble sort]

算法思想

  1. 比较相邻的元素。如果a[m]>a[m+1],交换这两个元素

  2. 对所有的元素重复以上步骤,从a[0]到a[n-2]。直到没有任何一个数字需要比较。

时间复杂度:O(n2)

核心代码:

void BubbleSort(int arr[],int length){
	for(int i=0;i<length;i++){
		for(int j=0;j<length-i-1;j++){
			if(arr[j]>arr[j+1]){
				swap(arr[j],arr[j+1]);
			}
		}
	}
}

2.选择排序[selection sort]

算法思想:

  1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  2. 依次找到最小(大)元素,排列。

时间复杂度:O(n2):

核心代码:

void SelectionSort(int arr[],int len){
	for(int i=0;i<len-1;i++){
        int min=i;
        for(int j=i+1;j<len;j++){//遍历未排序元素
            if(arr[i]<arr[min]){
                min=j;//记录最小值
            }
        }
        swap(arr[min],arr[i]);
    }
}

3. 插入排序[insertion sort]

算法思想:

  1. 以第一个元素为基准,取下后续元素,和基准对比,找到相应位置插入,形成新的基准。
  2. 重复以上步骤。

时间复杂度:O(n2)

核心代码:

void InsertionSort(int arr[],int length){
	int key;
	for(int i=1;i<length;i++){
		key=arr[i];
		j=i-1;
		while((j>=0)&&(arr[j]>key)){
			arr[j+1]=arr[j];
			j--;
		}
        arr[j+1]=key;
	}
}

4.希尔排序[shell’s sort]

又称增量递减算法,是冲破O(n2)的第一批算法之一。

算法思想:

  1. 对插入算法的改进,属于非稳定排序算法
  2. 采取跳跃式分组的策略,通过某个增量将数组元素划分为若干组,在组内进行插入排序。然后再逐步缩小增量。
  3. 在初始阶段从宏观上看基本有序。到增量为1时,多数情况下只需微调。

时间复杂度:O(nlogn)

核心代码:

void shell_sort(int arr[],int len){
    int increasement=len;
    do{
        increasment=increasement/3+1; //确定增量大小
        for(int i=0;i<creasement;i++){
            for(int j=i+creasement;j<len;j+=increasement){//对每组进行直接插入排序
                if(arr[j]<arr[j-increasement]){//找到正确位置
                    int tmp=arr[j];
                    for(int k=j-increasement;k>=0&&tmp<arr[k];k-=increasement){
                        arr[k+increasement]=arr[k];
                    }
                    arr[k+increasement]=tmp;
                }
            }
        }
    }
    while(increasement>1);//直到增量为1
}

5.归并排序[merge sort]

是建立在归并操作上的一种有效的排序算法。采用分治法的思想,先使每个子序列有序,再使子序列段间有序。代价是要额外的内存空间。若将两个有序表合并成一个有序表,成为2-路归并。

算法思想:

  1. 将序列分为子序列
  2. 对子序列分别采用归并排序
  3. 合并

时间复杂度:O(nlogn)

核心代码:

void MergeSort(int arr[],int start,int end,int *tmp){
    if(start>=end) return;
    int mid=(start+end)/2;
    MergeSort(arr,start,mid,tmp);
    MergeSort(arr,mid+1,end,tmp);
    //合并,利用辅助空间
    int i_start=start,i_end=mid;
    int j_start=mid+1,j_end=end;
    while(i_start<=i_end&&j_start<=j_end){
    	if(arr[i_start]<arr[j_start]){//由于子序列有序,仅需要比较首个数字
            tmp[length]=arr[i_start];
            length++;
            i_start++;
        }else{
            tmp[length]=arr[j_start];
            length++;
            j_start++;
        }    
    }
    //合并序列的剩余部分
    while(i_start<=i_end){
        tmp[length]=arr[i_start];
        length++;
        i_start++;
    }
    while(j_start<=j_end){
        tmp[length]=arr[j_start];
        length++;
        j_start++;
    }
    //释放辅助空间,数据写回初始地
    for(int i=0;i<length;i++) arr[start+i]=tmp[i];
}

6.快速排序

算法思想:

  1. 从子序列中挑出一个元素,成为基准
  2. 重新排序数列,所有比该基准小的放在前面,比基准大的放在后面。【分区操作】
  3. 递归的把小于基准值元素的子序列和大于基准值的子序列排序

时间复杂度:O(nlogn)

核心代码:

void QuickSort(int arr[],int start,int end){
    if(start>=end) return;
    int pivot=arr[start];//基准数
    int i=start,j=end;
    while(i<j){
        //从右向左找比基准数小的数
        while(i<j&&arr[j]>=pivot) j--;
        if(i<j){
            arr[i]=arr[j];
            i++;  
        }
        //从左向右找比基准数大的数
        while(i<j&&arr[i]<pivot) i++;
        if(i<j){
            arr[j]=arr[i];
            j++;
        }
    }
    arr[i]=pivot;//把基准数放到i的位置上
    QuickSort(arr,start,i-1);
    QuickSort(arr,i+1,end);
}

7.堆排序[heap sort]

大顶堆:每个节点的值都大于等于其左右孩子节点的值

小顶堆:每个节点的值都小于等于其左右孩子节点的值

算法思想:

  1. 将初始待排序关键字序列构建成大顶堆,此堆为初始的无序区
  2. 堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区Rn。且满足前者全都小于Rn
  3. 递归将前者无序区变成有序区,直至排序结束。

时间复杂度:O(nlogn)

核心代码:

void max_heapify(int arr[],int start,int end){
    int father=start;
    int son=father*2+1;
    while(son<=end){
        if(son+1<=end&&arr[son]<arr[son+1]) son++;
        if(arr[father]>arr[son]) return;
        else{
            swap(&arr[father],&arr[son]);
            father=son;
            son=father*2+1;
        }
    }
}
void HeapSort(int arr[],int len){
    //初始化,从最后一个父节点开始调整
    for(int i=len/2-1;i>=0;i--){
        max_heapify(arr,i,len-1);
    }
    //将第一个元素和有序区前一位交换,再调整
    for(int i=len-1;i>=0;i--){
        swap(&arr[0],&arr[i]);
        max_heapify(arr,0,i-1);
    }
}

8.计数排序

核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。该算法不是基于比较的排序算法,是一种线性空间复杂度的排序。当输入的元素是n个0-k之间的整数时,它的运行时间时O(n+k)

算法原理:

  1. 找出待排序的数组中的Max和Min
  2. 统计数组中每个值为i的元素出现的次数,存入c[i]

时间复杂度:O(n+k)

核心代码:

void counting_sort(int arr[],int *res,int n){
    int *c=(int *)malloc(sizeof(int)*100);
    for(int i=0;i<100;i++) c[i]=0;
    for(int i=0;i<n;i++){
        c[arr[i]]++;
    }
    for(int i=1;i<100;i++){
        c[i]+=c[i-1];
    }
    for(int i=n;j>0;j--){
        res[--c[arr[i-1]]]=arr[i-1];
    }
    free(c);
}

9.桶排序[bucket sort]

是计数排序的升级版。利用了函数的映射关系。工作原理是:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序。为了使桶排序更加高效,我们要在额外空间充足的情况下,尽量增大桶的数量。

算法原理:

  1. 设置一个定量的数组当作空桶
  2. 将数据放入对应的桶中,放完对不是空的桶进行排序
  3. 拼接桶

算法复杂度O(n+k)

10.基数排序

将整数按位数切割成不同的数字,然后按每个位数分别比较。可以用于浮点数等的比较。

算法思想:

  1. 取数组中的最大数,并取得位数i
  2. 对所有数进行i轮排序,从最低位开始取每个位组成radix数组,并对该数组进行计数排序

时间复杂度O(n+k)
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值