细数那些我们熟悉的 排序!

Posted 默默无语敲代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了细数那些我们熟悉的 排序!相关的知识,希望对你有一定的参考价值。

第一个 ,不得不说的 是 <冒泡排序>

      作为 一个非常经典,又差劲的算法 。

      计算过程如下:

    ->每次遍历数组,通过对比,使最大的冒上去,

           ->这样通过N次的轮循 ,可以使 排序的数组有序。

      优点:简单,适用性强。

  缺点:慢。

  适用性:数组,链表.

  性质:稳定性排序【打乱元素的稳定性,也就是 你不知道 这个元素 一开始是 3,3,3,排序后的 这个三是哪个三】。

  复杂度:平均 n^2,在有序的情况下,可以 O(N) ,

       代码: 不写

 二:归并排序:

   前言:归并排序,是利用分治的思想,

也就是说 将一个大问题化为一些小问题,这样在解决小问题的情况下,

在通过,合并操作,使以解决的小问题称以解决的大问题。

      计算过程如下:

    ->将一个数组递归的分解成俩断->四段->八段->直到分解称只有一个元素

           ->在一个元素的情况下,这断可以认为是有序的,进而满足解决了这个小问题

    ->对于合并,对于俩段有序的数组,只要在每个数组上打一个index每次取最小的那个元素不,知道元素取完毕就好。

      优点:稳定,效率一般(不断的SWAP)

  缺点:需要额外的内存。

  适用性:数组,链表(需要预先处理出分段,麻烦,一般可以认为不支持).

  性质:稳定性排序

  复杂度:平均 n*log(n)。

  代码:

void mergeSort(int v[], int l, int r, int tmpArray[]) {
    if (l == r) {
        return;
    }
    int mid = l + (r - l) / 2;
    mergeSort(v , l, mid, tmpArray);
    mergeSort(v , mid + 1, r, tmpArray);
    for (int i = l, j = mid + 1, idx = l ; i <= mid || j <= r; ) {
        if (i <= mid) {
            if(j <= r) {
                tmpArray[idx++] = v[i] <= v[j] ? v[i++] : v[j++];
            } else {
                tmpArray[idx++] = v[i++];
            }
        }  else {
            tmpArray[idx++] = v[j++];
        }
    }
    for(int i = l; i <= r ; i ++) {
        v[i] = tmpArray[i];
    }
}

 

三:堆排序:

  堆排序,人如其名,利用堆来排序,原理有些类似于冒泡排序,都是每次找出最大值。

  盗图->

  简要的说一下堆,一般的堆都是指 二叉堆,也就是,上图是一个小顶堆,也就是说,父亲节点的元素值比他的儿子节点的元素的值都小。

  数组有一个很好的性质 

    对于  position = i 的元素 他的左儿子 可以表示为 i<<1(i*2) ,右儿子可以表示为i<<1|1 (i*2+1)

  也正是因此堆可以用老模拟数组的排序过程

  

void build(int * a, int pos, int n) {

    while(true) {
        int lSon = pos << 1;
        int rSon = pos << 1 | 1;
        if (lSon > n) break;
        int target = rSon > n ? lSon : a[lSon] > a[rSon] ? lSon : rSon;

        if (a[pos] < a[target]) {
            swap(a[pos], a[target]);
            pos = target;
        } else {
            break;
        }
    }

}
void heapSort(int * a, int n) {
    for(int i = n ; i >= 1 ;--i) {
        build(a, i, n);
    }
    for(int i = n ; i > 1  ;--i) {
        swap(a[1], a[i]);
        build(a, 1, i - 1);
    }
}

 

  过程:

    1.数组作为堆初始化成为大顶堆。这样堆顶元素是最大的

    2.把堆顶元素的值和最后一个位置元素的值做一个交换,同时堆的大小减一。 ->这样这个值,是所有元素中最大的且已经在最后面了。

    3.上边的操作就如同 删除堆顶元素的操作,只不过把堆顶元素放到了最后。

    4.删除堆顶元素后进行更新操作,递归向下,保持堆的性质

    5.重复2到4的操作,直到堆内没有元素。

      优点:稳定,效率一般(不断的SWAP)。

   缺点:稳定,同时中庸,比归并排序要快(个人见解)。

  适用性:数组,(链表不支持吧?)

  性质:非稳定性排序

  复杂度:平均 n*log(n)。

四:快速排序:

  快排,为什么叫快排?因为他快, 这个排序,也算是分治的思想,

  和归并的相反,先计算在递归分解,  而归并是先递归分解,在合并。

  过程,

    1.在这断内选取出一个关键的key值,同时指定俩个指针(就是位置表示符号)一个起始,一个结束(这里先认为取左侧第一个,不是的可以,用swap来达到这个状态)

    2.先从左开始比较,如果都比他小OK左侧的指针右移,否则把这个元素的值丢给右侧指针(不用担心左侧这个值怎么办,提示:提取出来的KEY),然后从右边开始扫描(向左侧)

    3.同上,知道扫描到一个比这个小的OK在此交换俩个指针指向的元素。在此从左侧的指针开始扫描

    4.重复2,3直到俩个指针指向同一个位置,OK,本次扫描结束

    5.这样连个指针只想的位置赋值为KEY那么左侧都比他小, 右侧都比她大,

    6.数据根据指针的位置,查分为俩段 利用1-4这个过程,求解即可。

代码:

  

void sort(int * v, int l ,int r){
    if (l >= r) return;
    int key = v[l];
    int low = l, high = r;
    while(low < high) {
        while(low < high && v[high] >= key) high --;
        v[low] = v[high];
        while(low < high && v[low]  <= key) low ++;
        v[high] = v[low];
    }
    v[low] = key;
    sort(v, l, low - 1);
    sort(v, low + 1, r);
}
View Code

 

  

 优点:内存无额外消耗,平均而言了认为是最好的排序算法。

性质,非稳定性排序。

复杂度n* logn,最坏情况下会退化到n^2

适用性,数组,链表。

,这个算法的关键在于寻找那个KEY,能让左右平衡一些(有三分写法),这个算法,扫描多余交换.

 

有些代码我没写(想要写给你,夜深了)

以上是关于细数那些我们熟悉的 排序!的主要内容,如果未能解决你的问题,请参考以下文章

细数macOS那些精美的桌面壁纸(值得收藏)

细数那些Java程序员最容易犯那些错

云小课|细数那些VMware虚拟机的恢复招式

单卡流量池统付池等,细数那些常见的物联网卡计费模式

细数Spark3.0的那些新特性

开发工具 浏览器细数那些优秀的谷歌浏览器插件