排序算法

概述

所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序。这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后的数据便于筛选和计算,大大提高了计算效率。对于排序,我们首先要求其具有一定的稳定性,即当两个相同的元素同时出现于某个序列之中,则经过一定的排序算法之后,两者在排序前后的相对位置不发生变化。换言之,即便是两个完全相同的元素,它们在排序过程中也是各有区别的,不允许混淆不清。

分类

排序(Sorting) 是计算机程序设计中的一种重要操作,它的功能是将一个数据元素(或记录)的任意序列,重新排列成一个关键字有序的序列。
排序就是把集合中的元素按照一定的次序排序在一起。一般来说有升序排列和降序排列2种排序,在算法中有8中基本排序:
(1)冒泡排序;
(2)选择排序;
(3)插入排序;
(4)希尔排序;
(5)归并排序;
(6)快速排序;
(7)基数排序;
(8)堆排序;
(9)计数排序;
(10)桶排序。

评价标准

稳定性是一个特别重要的评估标准。稳定的算法在排序的过程中不会改变元素彼此的位置的相对次序,反之不稳定的排序算法经常会改变这个次序,这是我们不愿意看到的。我们在使用排序算法或者选择排序算法时,更希望这个次序不会改变,更加稳定,所以排序算法的稳定性,是一个特别重要的参数衡量指标依据。就如同空间复杂度和时间复杂度一样,有时候甚至比时间复杂度、空间复杂度更重要一些。所以往往评价一个排序算法的好坏往往可以从下边几个方面入手:
(1)时间复杂度:即从序列的初始状态到经过排序算法的变换移位等操作变到最终排序好的结果状态的过程所花费的时间度量。
(2)空间复杂度:就是从序列的初始状态经过排序移位变换的过程一直到最终的状态所花费的空间开销。
(3)使用场景:排序算法有很多,不同种类的排序算法适合不同种类的情景,可能有时候需要节省空间对时间要求没那么多,反之,有时候则是希望多考虑一些时间,对空间要求没那么高,总之一般都会必须从某一方面做出抉择。
(4)稳定性:稳定性是不管考虑时间和空间必须要考虑的问题,往往也是非常重要的影响选择的因素。

常见排序算法

插入排序
插入排序算法是基于某序列已经有序排列的情况下,通过一次插入一个元素的方式按照原有排序方式增加元素。这种比较是从该有序序列的最末端开始执行,即要插入序列中的元素最先和有序序列中最大的元素比较,若其大于该最大元素,则可直接插入最大元素的后面即可,否则再向前一位比较查找直至找到应该插入的位置为止。插入排序的基本思想是,每次将1个待排序的记录按其关键字大小插入到前面已经排好序的子序列中,寻找最适当的位置,直至全部记录插入完毕。执行过程中,若遇到和插入元素相等的位置,则将要插人的元素放在该相等元素的后面,因此插入该元素后并未改变原序列的前后顺序。我们认为插入排序也是一种稳定的排序方法。插入排序分直接插入排序、折半插入排序和希尔排序3类。
冒泡排序
冒泡排序算法是把较小的元素往前调或者把较大的元素往后调。这种方法主要是通过对相邻两个元素进行大小的比较,根据比较结果和算法规则对该二元素的位置进行交换,这样逐个依次进行比较和交换,就能达到排序目的。冒泡排序的基本思想是,首先将第1个和第2个记录的关键字比较大小,如果是逆序的,就将这两个记录进行交换,再对第2个和第3个记录的关键字进行比较,依次类推,重复进行上述计算,直至完成第(n一1)个和第n个记录的关键字之间的比较,此后,再按照上述过程进行第2次、第3次排序,直至整个序列有序为止。排序过程中要特别注意的是,当相邻两个元素大小一致时,这一步操作就不需要交换位置,因此也说明冒泡排序是一种严格的稳定排序算法,它不改变序列中相同元素之间的相对位置关系。 [1]
选择排序
选择排序算法的基本思路是为每一个位置选择当前最小的元素。选择排序的基本思想是,基于直接选择排序和堆排序这两种基本的简单排序方法。首先从第1个位置开始对全部元素进行选择,选出全部元素中最小的给该位置,再对第2个位置进行选择,在剩余元素中选择最小的给该位置即可;以此类推,重复进行“最小元素”的选择,直至完成第(n-1)个位置的元素选择,则第n个位置就只剩唯一的最大元素,此时不需再进行选择。使用这种排序时,要注意其中一个不同于冒泡法的细节。举例说明:序列58539.我们知道第一遍选择第1个元素“5”会和元素“3”交换,那么原序列中的两个相同元素“5”之间的前后相对顺序就发生了改变。因此,我们说选择排序不是稳定的排序算法,它在计算过程中会破坏稳定性。 [1]
快速排序
快速排序的基本思想是:通过一趟排序算法把所需要排序的序列的元素分割成两大块,其中,一部分的元素都要小于或等于另外一部分的序列元素,然后仍根据该种方法对划分后的这两块序列的元素分别再次实行快速排序算法,排序实现的整个过程可以是递归的来进行调用,最终能够实现将所需排序的无序序列元素变为一个有序的序列。
归并排序
归并排序算法就是把序列递归划分成为一个个短序列,以其中只有1个元素的直接序列或者只有2个元素的序列作为短序列的递归出口,再将全部有序的短序列按照一定的规则进行排序为长序列。归并排序融合了分治策略,即将含有n个记录的初始序列中的每个记录均视为长度为1的子序列,再将这n个子序列两两合并得到n/2个长度为2(当凡为奇数时会出现长度为l的情况)的有序子序列;将上述步骤重复操作,直至得到1个长度为n的有序长序列。需要注意的是,在进行元素比较和交换时,若两个元素大小相等则不必刻意交换位置,因此该算法不会破坏序列的稳定性,即归并排序也是稳定的排序算法。

C 语言代码实现

冒泡排序

#include <stdio.h>
#include <stdlib.h>
int main() {
    int a[]= {12,4,132,55,46,232,789,1,0,98,523,666};//随机数组
    int n = sizeof(a)/sizeof(a[0]);//获取数组大小
    int i,j,k;
//比较相邻的两个数据,如果第二个数小,就交换位置。从后向前两两比较,一直到比较最前两个数据。
        for(i = 1; i < n; i ++) {
            for(j = 0; j < n-1; j ++) {
                if(a[j] > a[j+1]) {//从小到大排序
                    k = a[j];
                    a[j] = a[j+1];
                    a[j+1] = k;
                }
            }
        }  
    for(i = 0; i < n; i ++)//输出排序后的结果
        printf("%d ",a[i]);
    return 0;
}
//运行结果如下:
//0 1 4 12 46 55 98 132 232 523 666 789


*选择排序*
#include <stdio.h>
#include <stdlib.h>
int main() {
    int a[]= {12,4,132,55,46,232,789,1,0,98,523,666};//随机数组
    int n = sizeof(a)/sizeof(a[0]);//获取数组大小
    int i,j,k;
        //第一次遍历n-1个数,找到最小的数值与第一个元素交换
        //第二次遍历n-2个数,找到最小的数值与第二个元素交换
        // 以此类推
        //第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成。
    for(i = 0; i < n-1; i ++) {
        for(j = i+1; j < n; j ++) {
             if(a[i] > a[j]) {//从小到大排序
                    k = a[i];
                    a[i] = a[j];
                    a[j] = k;
             }
         }
    }
    for(i = 0; i < n; i ++)//输出排序后的结果
        printf("%d ",a[i]);
    return 0;
}
//运行结果如下:
//0 1 4 12 46 55 98 132 232 523 666 789




*插入排序*
#include <stdio.h>
#include <stdlib.h>
int main() {
    int a[]= {12,4,132,55,46,232,789,1,0,98,523,666};//随机数组
    int n = sizeof(a)/sizeof(a[0]);//获取数组大小
    int i,j,k;
 //在要排序的一组数中,假定前n-1个数已经排好序,现将第n个数插到前面的有序数列中,
 //使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。
    for(i = 0; i < n-1; i ++) {
        for(j = i+1; j > 0; j --)
            if(a[j] < a[j-1]) {
                k = a[j-1];
                a[j-1] = a[j];
                a[j] = k;
            } else
                break;
    }
    for(i = 0; i < n; i ++)//输出排序后的结果
        printf("%d ",a[i]);
    return 0;
}
//运行结果如下:
//0 1 4 12 46 55 98 132 232 523 666 789



*快速排序*
#include <stdio.h>
#include <stdlib.h>
//先从数列中取出一个数作为key值
//将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边
//对左右两个小数列重复第二步,直至各区间只有1个数。
void quickSort(int a[],int l,int r) {
    if(l>=r)
        return;
    int i = l;
    int j = r;
    int key = a[l];//选择第一个数为key
    while(i<j) {
        while(i<j && a[j]>=key)//从右向左找第一个小于key的值
            j--;
        if(i<j) {
            a[i] = a[j];
            i++;
        }
        while(i<j && a[i]<key)//从左向右找第一个大于key的值
            i++;
        if(i<j) {
            a[j] = a[i];
            j--;
        }
    }
    a[i] = key;
    quickSort(a, l, i-1);//继续排左部分,递归调用
    quickSort(a, i+1, r);//继续排右部分,递归调用
}
int main() {
    int a[]= {12,4,132,55,46,232,789,1,0,98,523,666};//随机数组
    int i,n = sizeof(a)/sizeof(a[0]);//获取数组大小
    quickSort(a,0,n-1);//快速排序函数入口
    for(i = 0; i < n; i ++)//输出排序后的结果
        printf("%d ",a[i]);
    return 0;
}
//运行结果如下:
//0 1 4 12 46 55 98 132 232 523 666 789


*堆排序*
#include <stdio.h>
#include <stdlib.h>
int arr[]= {12,4,132,55,46,232,789,1,0,98,523,666};//随机数组
int n = sizeof(arr)/sizeof(arr[0]);//获取数组大小
void adjustHeap(int i, int lef) {
    int temp=arr[i];
    for(int k=i*2+1; k<lef; k=k*2+1) { //从i结点的左子结点开始,也就是2i+1处开始
        if(k+1<lef&&arr[k]<arr[k+1]) {
            k++;
        }
        if(arr[k]>temp) { //如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
            arr[i]=arr[k];
            i=k;
        } else {
            break;
        }
    }
    arr[i]=temp;//将temp值放到最终的位置
}
void swap(int a, int b) {
    int temp=arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}
void heapsort() {
    // 1、构建大顶堆
    for(int i = n/2-1; i>=0; i--) {
        //从第一个非叶子节点从下至上,从右至左调整结构
        adjustHeap(i,n);
    }
    //2、调整堆结构+交换堆顶元素与末尾元素
    for(int j=n-1; j>0; j--) {
        swap(0,j);//将堆顶元素与末尾元素进行交换
        adjustHeap(0, j);//重新对堆进行调整
    }
}
int main() {
    int i;
    heapsort();
    for(i = 0; i < n; i ++)
        printf("%d ",arr[i]);
    return 0;
}
//运行结果如下:
//0 1 4 12 46 55 98 132 232 523 666 789
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值