最基本的排序方法--C语言

前言

经典的排序和查找方法是每个人都要掌握的,为了更加深刻的记忆它们,我把排序方法记录在这条菜鸟走向大神的罗马大道上。希望这篇文章可以帮到更多的人。
这篇文章的初步分为稳定排序不稳定排序两个部分,稳定又代表了什么呢,下面有一个数组:
arr[ 5, 5, 2, 1, 3],里面有两个数值相等的5,我们加一个符号标记不同位置的5。
经过稳定排序后:arr[1, 2, 3, 5, 5] 发现他们的相对位置不改变;
而经过不稳定排序后(如快速排序)数组就变成了arr[1, 2, 3, 5, 5], 相同数字位置发生改变了。

稳定排序

插入排序

插入排序的核心思想就是保证数组的头部有序,然后向后推进,直到遍历完整个数组。(下面所有代码都是默认从小到大排序)
不失一般性,我们假设数组的前 j - 1个元素已经有序,到了第 j 个元素,我们需要将它放到前 j 个位置适合它的位置, 每次跟它前面的元素比较,小于前面的元素则交换位置,否则就是目前位置
对于每一个元素 j,最好的情况交换0次,最坏的话交换 j 次。

void insert(int *num, int n) {
    for (int i = 1; i < n; i++) {
        for (int j = i; j > 0 && num[j] < num[j - 1]; j--) {
            swap(num[j], num[j - 1]);
        }
    }
    return ;
}

冒泡排序

冒泡排序和插入排序相反,他是保证数组后面有序。第一步是找到最大的数放在最后一为,循环n遍,每次找到未排序区间里面的最大数,放在已排序区间的前面。实现即大于右边的进行交换
对于冒泡排序,这里有一个加速的方法,防止有序的数组再遍历一遍。即使用一个变量 times 记录每次遍历交换的次数,如果某一次没有进行交换,则代表数组已经完全有序,可以直接跳出循环。

void bubble_sort(int *num, int n) {
    int times = 1;
    for (int i = 1; i < n && times; i++) {
        times = 0; // 如果有一次循环没有进行交换,证明已经有序了
        for (int j = 0; j < n - i; j++) {
            if (num[j] > num[j + 1]) {
                swap(num[j], num[j + 1]);
                times++;
            }
        }
    }
    return ;
}

归并排序

归并排序的思想也很简单,就是分治与合并,将一个数组一分为二,先把左边的进行排序,然后把右边的进行排序(分治),最后将两个有序的数组合并到一个数组中再拷贝到原数组里面。分别对两个小数组进行排序并不需要真正意义上的排序,我们利用递归将它分治成极小的数组(只有一个或两个元素)再将有序的数组合并即可。

void merge_sort(int *num, int l, int r) {
    if (r - l <= 1) {
        if (r - l == 1 && num[l] > num[r]) {
            swap(num[l], num[r]); // 只剩两个元素的时候
        }
        return;
    }
    int mid = (l + r) >> 1;
    merge_sort(num, l, mid); // 递归对左侧数组进行归并排序
    merge_sort(num, mid + 1, r); // 递归对右侧数组进行归并排序
    int *temp = (int *)malloc(sizeof(int) * (r - l + 1)); // 有序的数组进行合并
    int p1 = l, p2 = mid + 1, k = 0;
    while(p1 <= mid || p2 <= r) {
        if (p2 > r || (p1 <= mid && num[p1] < num[p2])) { //从左侧数组取值
            temp[k++] = num[p1++];
        } else {
            temp[k++] = num[p2++];
        }
    }
    memcpy(num + l, temp, sizeof(int) * (r - l + 1));
    free(temp);
    return ;
}

不稳定排序

选择排序

每次选择未排序区间最小值(最大值),然后和下一个应该排序的位置的元素交换位置。

void select_sort(int *num, int n) {
    for (int i = 0; i < n - 1; i++) {
        int ind = i;
        for (int j = i + 1; j < n; j++) {
            if (num[ind] > num[j]) ind = j;
        }
        swap(num[i], num[ind]); // 和应该排序的位置i的元素交换位置
    }
    return ;
}

快速排序

快速排序的思想就是找一个基准值,把小于基准值的所有数放在基准值左侧,大于基准值的所有数放在右侧,递归到两个小数组直到所有都有序

void quick_sort(int *num, int l, int r) {
    while (l < r) { // 终止条件,对整个大的分组全部完成快排
        int x = l, y = r, temp = num[(l + r) >> 1]; //基准值
        do {
            while (x <= y && num[x] < temp) x++; // 找到左侧第一个大于基准值的数字
            while (x <= y && num[y] > temp) y--;// 找到右侧第一个小于基准值的数字
            if (x <= y) {
                swap(num[x], num[y]); // 交换位置
                x++, y--;
            }
        } while (x <= y); // 基准值右侧全是大于它的数,第一次快排结束
        quick_sort(num, x, r); // 对右侧的区间递归块排
        r = y; // 对左区间进行块排(此时 l != r)
    }
    return ;
}

一更到此结束,下次更新各个排序算法之间的复杂度问题
昨天晚上看到一个很精彩的博客,是关于优先队列的,虽然之前会优先队列,但是还是受益匪浅。文章最后的一段话也很有感触,在此写下来了,另外附上博客的链接,需要的兄弟们可以进取看一看。
C++中优先队列priority_queue的基础用法

“人在比较中奋进,同在比较中消亡,
起初面临差距时会奋起直追,但是当努力过后发现距离反而越来越远时,
便会麻木懈怠,曾经的努力没有用吗?
我觉得不是,努力过不一定会成功,但是努力的过程已经印在了骨子里,
这本身就是生活的一部分。
你可以选择这条艰苦的路,同样也可以选择跳过,至于跳过时错失了什么,谁又知道呢?
毕竟人生无法再来过,重新读档只发生在游戏世界中~”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值