前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >数据结构(二)

数据结构(二)

作者头像
可爱见见
发布2019-09-09 16:55:07
3730
发布2019-09-09 16:55:07
举报
文章被收录于专栏:卡尼慕卡尼慕

这是卡尼慕的第n篇文章

上篇复习了一下数据结构的基本概念和常识,那么今天来复习一下几种常见的排序算法。

首先了解一下排序的一个特性:稳定性。

稳定性是指相同元素在排序后相对位置保持不变。

稳定性的意义

如果只是简单的进行数字的排序,那么稳定性将毫无意义。

如果排序的内容仅仅是一个复杂对象的某一个数字属性,那么稳定性依旧将毫无意义。

如果要排序的内容是一个复杂对象的多个数字属性,但是其原本的初始顺序毫无意义,那么稳定性依旧将毫无意义。

冒泡bubububu~

冒泡算法是最最最最基础的排序算法。小的往左移动,大的往右,整个过程就像是水中气泡上浮。在相邻两个元素的比较中,如果相等,则没有必要交换。

这一点,保证了冒泡排序的稳定性。它的思路也非常简单,使用了两层for循环。第一层循环是遍历整个数组,第二层是比较两两相邻的数。

代码语言:javascript
复制
void BubbleSorting(int a[] , int n)
{
    for(int i = 0; i < n-1; i++)
    {
        for(int j = 0; j < n-1-i; j++)
        {
            if(a[j] > a[j+1])
            {
                int tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
        }
    }   
}

快速排序

这里引荐一篇大牛的文章,看了简直是醍醐灌顶,哈哈,坐在马桶上看算法:快速排序。

方法其实很简单:分别从初始序列“6 1 2 7 9 3 4 5 10 8”两端开始“探测”。先从右往左找一个小于6的数,再从左往右找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即=10),指向数字。

首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j--),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。

现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:

6 1 2 5 9 3 4 7 10 8

到此,第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下:

6 1 2 5 4 3 9 7 10 8

第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:

3 1 2 5 4 6 9 7 10 8

代码语言:javascript
复制
void QuickSort(int a[] , int left ,int right)
{
    if(left > right)
    {
        return;
    }
    int i , j ,tmp;
    i = left;
    j = right;  
    tmp = a[left]; //哨兵
    6
    while(i != j) //哨兵不能碰面 
    {
        while(a[j] <= tmp && i < j) //先移动右边的哨兵 
        {
            j--;
        }
        while(a[i] >= tmp && i < j)
        {
            i++;
        }
        if(i < j)
        {
            int k = a[i];
            a[i] = a[j];
            a[j] = k;
        }
    }

    a[left] = a[j];
    a[j] = tmp;

    QuickSort(a ,left,i-1);
    QuickSort(a ,i+1,right);
}

选择排序

选择排序的思路也特别容易好想,就是先遍历整个数组,找到最小值,把最小值丢到最前面,然后选取第二个数作为首项,再找最小值,逐步排号顺序。

代码语言:javascript
复制
void ChoiceSort(int a[] , int N)
{
    for (int i = 0; i < N; i++)
    {
        int k = i;
        for (int j = k + 1; j < N; j++)
        {
            if (a[j] < a[k])
            {
                k = j;
            }
        }
        //找到了最小的值再交换
        int temp = a[i];
        a[i] = a[k];
        a[k] = temp;
    }
}

插入排序

插入排序的思路也不难,就可以简单的类比于我们整理扑克牌一样,现在左手上那一张牌,然后从牌堆中随便选取一张牌,按照大小插入左手已经有序的牌中,逐一类推。

从代码方面考虑,为了使代码更方便可行,一般直接按照顺序选取下一个未排序的元素作为待插入的牌。

代码语言:javascript
复制
void InsertSort(int a[] ,int N)
{
    //从第二个元素开始,加入第一个元素是已排序数组
    for (int i = 1; i < N; i++){
        //待插入元素 array[i]
        if (a[i] < a[i - 1]){
            int wait = a[i];
            int j = i;
            while (j > 0 && a[j - 1] > wait){
                //从后往前遍历已排序数组,若待插入元素小于遍历的元素,则遍历元素向后挪位置
                a[j] = a[j - 1];
                j--;
            }
            a[j] = wait;
        }
    }
}

归并排序

归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。

代码语言:javascript
复制
void Merge(int *arr, int n)
{
    int temp[n];    // 辅助数组
    int b = 0;  // 辅助数组的起始位置
    int mid = n / 2;    // mid将数组从中间划分,前一半有序,后一半有序
    int first = 0, second = mid;    // 两个有序序列的起始位置

    while (first < mid && second < n)
    {
        if (arr[first] <= arr[second])   // 比较两个序列
            temp[b++] = arr[first++];
        else
            temp[b++] = arr[second++];
    }

    while(first < mid)               // 将剩余子序列复制到辅助序列中
            temp[b++] = arr[first++];
    while(second < n)
            temp[b++] = arr[second++];
    for (int i = 0; i < n; ++i)    // 辅助序列复制到原序列
        arr[i] = temp[i];
}

void MergeSort(int* s, int Num) 
{
    if(n <= 1) //递归法的出口 
        return;
    if(n > 1)
    {
        MergeSort(a ,n/2);
        MergeSort(a + n/2 ,n - n/2);
        Merge(a , n);  //归并两部分 
    }
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 卡尼慕 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档