常见的排序算法

来源:互联网 发布:mac windows 双系统 编辑:程序博客网 时间:2024/06/11 11:37

排序算法

排序算法是最基础,也是最简单的算法思想,因为应用场景多,书写简单,所以应用较为普遍,所以在面试和考试的时候,都会涉及到排序算法,虽然排序算法种类很多,但是只要理解了思想,然后灵活运用,那么就不难记忆.
排序算法两个需要记忆和理解的点就是:算法思想和时间复杂度.下面我们就介绍和分析一下常见的几种排序算法

冒泡排序

  • 思想
    冒泡排序是最基础的排序算法,很多基础语言中都会出现,顾名思义,冒泡排序就是将数像水泡一样,一个一个的排到上面,通过与相邻元素进行比较,然后将较小的数或者较大的数依次排到上面,例如:
    一个无序的序列A:3,17,13,9,46
    我们要将序列变为升序的序列:
    首先,我们从第一个数开始,即从3开始,3和17比较,3小,那么不变,后面同理,最后得出第一个数就是3,那么第一轮之后,序列变为:3,17,13,9,46
    然后从第二个数开始,即从17开始,17和13比较,13小,那么交换位置,此时第二个数为13,后面同理,最后得出第二个数为9,序列变为:3,9,17,13,46

    直到最后一个数也确定,那么序列就变为:3,9,13,17,46
  • 时间复杂度
    O(n^2)
  • 代码实现
    int num[10] = {9, 20, 178, 46, 8, 9, 17, 88, 11, 25};    int n = 10;    for(int i = 0; i < n; i++){        for(int j = i + 1; j < n; j++){            if(num[i] > num[j]){                int temp = num[i];                num[i] = num[j];                num[j] = temp;            }        }    }    for(int i = 0; i < n; i++){        printf("%d ",num[i]);    }

选择排序

  • 思想
    选择排序和冒泡排序相似,是基于冒泡排序的一个优化,将相邻元素依次比较改为每次找出最小值之后再进行交换,减少了元素交换的次数
  • 时间复杂度
    O(n^2)
  • 代码实现
    int num[10] = {9, 20, 178, 46, 8, 9, 17, 88, 11, 25};    int n = 10;    for(int i = 0; i < n; i++){        int minNumIndex = i;        for(int j = i + 1; j < n; j++){            if(num[j] < num[minNumIndex]){                minNumIndex = j;            }        }        int temp = num[minNumIndex];        num[minNumIndex] = num[i];        num[i] = temp;    }    for(int i = 0; i < n; i++){        printf("%d ",num[i]);    }

插入排序

  • 思想
    插入排序和选择排序不同的是,我们不是通过交换元素来实现排序,而是通过找到要排序的数的合适位置然后插入来实现的,例如:
    一个无序的序列A:3,17,13,9,46
    我们要将序列变为升序的序列:
    从第一个位置的数开始,因为3处于第一个位置,那么就认为暂时3是第一个数,然后是17,17和3比较,没有3小,那么17就暂时应该在3的后面,即位置不变,然后是13,和17比较,比17小,那么17的位置要发生改变,这时17应该在第三个数的位置,然后13继续向前走,和3比较,没有3小,那么停止,此时13应该在17原来的位置上,那么13暂时就是第二个数,然后是9,9和第三个位置的数17比较,比17小,那么17后移,9前移,和13比较,比13小,那么13后移,9继续前移,和3比较,没有3小,停止,此时,9暂时在第二个数的位置,13和17分别在第三个和第四个数的位置,最后是46,和17比较,没有17小,那么不变。最后序列变为:3,9,13,17,46
  • 时间复杂度
    O(n^2)
  • 实现代码
    int num[10] = {9, 20, 178, 46, 8, 9, 17, 88, 11, 25};    int n = 10;    for(int i = 0; i < n; i++){        int  j = i ;        int temp = num[i];        while(j > 0 && num[j - 1] > temp){            num[j] = num[j - 1];            j--;        }        num[j] = temp;    }    for(int i = 0; i < n; i++){        printf("%d ",num[i]);    }

快速排序

  • 思想
    快速排序,顾名思义,就是快速的排序,在实际应用中确实也是很广泛的,但是思想和冒泡排序还是有异曲同工之妙,冒泡排序是通过相邻元素的比较和交换把最小的冒泡到最顶端,而快速排序是比较和交换小数和大数,这样一来不仅把小数冒泡到上面同时也把大数沉到下面。例如:
    一个无序的序列A:9,17,13,3,46
    我们要将序列变为升序的序列:
    我们定义两个左右指针i和j,右指针找比相对数小的,左指针找比相对数大的,然后交换,但是因为我们在开始的时候可以保存相对数,所以说,在找到一个满足的数之后,立刻进行交换也行,9,17,13,3,46,用9作为比较的相对数,最终会把9小的移动到9的左边,比9大的移动到9的右边。 首先设置i,j两个指针分别指向两端,j指针先扫描3比9小停止。然后i扫描,17比9大停止。交换i,j位置。序列变为:9,3,13,17,46,然后j指针再扫描,这时j扫描3时两指针相遇。停止。然后交换3和相对数。序列变为:3,9,13,17,46 一次划分后达到了左边比5小,右边比5大的目的。之后对左右子序列递归排序,最终得到有序序列。但是例子中的序列比较特殊,一次性就排好了序,可以参考代码中的序列来进行理解
  • 时间复杂度
    O(nlog(n))
  • 实现代码
int swapArray(int left, int right){    int temp = num[left];    while(left < right){        while(right > left && num[right] >= temp)            right--;        num[left] = num[right];        while(left < right && num[left] <= temp)            left++;        num[right] = num[left];    }    num[left] = temp;    //返回我们所设置的相对点变换后位置    return left;}void quickSort(int left, int right){    if(left >= right)        return ;    int index = swapArray(left, right);    //递归分治    quickSort(left, index);    quickSort(index + 1, right);}

希尔排序

  • 思想
    希尔排序其实是对插入排序的一种优化,也可以成为缩小增量排序,当我们进行插入排序的时候,如果序列是有序的,即:3,9,13,17,46,那么时间复杂度就降为了O(n),希尔排序就是利用了这一点,首先设置了一个增量,然后将其分割为若干部分,针对若干部分依次进行排序,然后不断缩小增量,直至为0,例如:
    一个无序序列:9, 20, 178, 46, 8, 9, 17, 88, 11, 25
    第一次的时候:增量为 10 / 2 = 5,即分为5组
    A[1…2] = 9,9
    B[1…2] =20,17
    C[1…2] = 178,88
    D[1…2] = 46,11
    E[1…2] = 8,25
    然后将A,B,C,D,E中的数进行插入排序,
    序列变为9,17,88,11,8,9,20,178,46,25
    第二次的时候,增量为5 / 2 = 2,即分为2组
    A[1…5] = 9,88,8,20,46
    B[1…5] = 17,11,9,178,25
    然后将A,B中的数进行插入排序,序列变为
    8,9,9,11,20,17,46,25,88,178
    第三次的时候:增量为 2 / 2 = 1,即分为1组
    A[1…10] = 8,9,9,11,20,17,46,25,88,178
    然后对A中的数进行插入排序
    序列变为:8,9,9,11,17,20,25,46,88,178
    第四次的时候:增量为1 / 2 = 0,排序结束
  • 时间复杂度
    可以达到O(n^1.3)
  • 代码实现
    int len = n / 2;    while(len){        for(int i = 0; i < len; i++){            if(i + len >= n) break;            for(int j = i ;j < n ; j += len){                int k = j;                int temp = num[j];                while(k > 0 && num[k - len] > temp){                    num[k] = num[k - len];                    k -= len;                }                num[k] = temp;            }        }        len /= 2;    }    for(int i = 0 ;i < n;i++){        printf("%d ",num[i]);    }

归并排序

  • 思想
    归并排序和前面的排序算法思想没有什么继承和优化的关系,归并排序使用了递归分治的思想,先递归划分子问题,然后合并结果。把待排序列看成由两个有序的子序列,然后合并两个子序列,然后把子序列看成由两个有序序列。。。是一个从底向上的过程.在合并过程中,我们定义一个temp数组来临时存储变量,最后再将临时数组中的变量加入到我们要排序的数组中
  • 时间复杂度
    O(nlog(n))
  • 代码实现
void Merge(int left, int right){    int mid = (left + right) / 2;    int temp[10];    int len = 0;    int i = left;    int j = mid + 1;    while(i <= mid && j <= right){        if(num[i] < num[j]){            temp[len++] = num[i++];        } else{            temp[len++] = num[j++];        }    }    while(i <= mid) temp[len++] = num[i++];    while(j <= right) temp[len++] = num[j++];    for(int i = 0; i < len ;i++){        num[i + left] = temp[i];    }}void Sort(int left, int right){    if(left >= right) return;    int mid = (left + right) / 2;    Sort(left, mid);    Sort(mid + 1, right);    Merge(left, right);}

计数排序

计数排序应该是这些排序算法中思想最简单的一个算法了,我们定义一个计数数组来保存一个数出现的频率,例如:如果17出现了2次,那么cnt[17] = 2,然后我们根据这些数所在的大致范围来进行扫描,一个一个的塞到数组中即可

  • 时间复杂度
    O(n),虽然时间复杂度很低,但是空间复杂度非常的高,因为我们的cnt数组能保存的数的大小是有限的,当要排序的数很大的时候,就很麻烦了.所以说这只能用于小一点的数的排序,且对空间复杂度要求不高的.
  • 代码实现
    int cnt[maxn] = {0};    int minLeft = maxn + 10;    int maxRight = -1;    for(int i = 0; i < n;i++){        cnt[num[i]]++;        if(minLeft > num[i]) minLeft = num[i];        if(maxRight < num[i]) maxRight = num[i];    }    int k = 0;    for(int i = minLeft; i <= maxRight ;i++){        for(int j = 0; j < cnt[i]; j++){            num[k++] = i;        }    }    for(int i = 0 ;i < n;i++){        printf("%d ",num[i]);    }

基数排序

  • 思想
    基数排序又是一种和前面排序方式不同的排序方式,基数排序不需要进行记录关键字之间的比较。基数排序是一种借助多关键字排序思想对单逻辑关键字进行排序的方法。所谓的多关键字排序就是有多个优先级不同的关键字。对数字进行排序,那么个位、十位、百位就是不同优先级的关键字,如果要进行升序排序,那么个位、十位、百位优先级一次增加。基数排序是通过多次的收分配和收集来实现的,关键字优先级低的先进行分配和收集。
  • 代码实现
int getNum(int x){    if(x == 0) return 1;    int cnt = 0;    while(x){        x /= 10;        cnt++;    }    return cnt;}int Pow(int x){    int ans = 1;    for(int i = 0;i < x; i++){        ans *= 10;    }    return ans;}void Sort(){    int maxLoc = 1;    for(int i = 0; i < n ;i++){        int loc = getNum(num[i]);        if(maxLoc < loc){            maxLoc = loc;        }    }    int temp[10][20];//temp[i][j]表示当前位为i的第j个数    int cnt[10] = {0};//表示当前位为i的数的个数    for(int i = 1; i <= maxLoc; i++){        int powNum = Pow(i);        for(int j = 0; j < n; j++){            int numLoc = num[j] % powNum / (powNum / 10);            temp[numLoc][cnt[numLoc]] = num[j];            cnt[numLoc]++;        }        int  len = 0;        //取出数        for(int j = 0 ;j < 10; j++){            for(int k = 0; k < cnt[j] ;k++){                num[len++] = temp[j][k];            }            cnt[j] = 0;        }    }}

那么就先说到这里啦,有不足以后会补充

欢迎来到我的博客,我是AnICoo1,我喂自己袋盐

有错误请评论指出或联系我,不胜感激!
个人邮箱:helloiamclh@gmail.com

0 0