七大经典排序算法(C语言描述)

  排序算法是算法中非常重要的部分,我们要做的不仅仅写出各种算法代码,更重要的是在解决实际问题时根据每种算法的时间复杂度、空间复杂度以及稳定性选出合适的算法。

PS:排序算法的稳定性
  假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。

一、 插入排序

1. 直接插入排序

  直接插入排序(Straight Insertion Sort)是一种最简单的排序方法,其基本操作是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表。

  每一步将一个待排序的元素,按其排序码的大小,插入到前面已经排好序的一组元素的合适位置上去,直到元素全部插完为止。

数组arr[N],现对其进行插入排序。
1. 开始时数组中有序元素是a[0]。a[1]~a[N-1]均为无序的。
2. 把a[1]按升序或降序规则插入到有序序列中,此时a[0]~a[1]为有序区。
3. 循环第二步,直到将最后一个元素a[N-1]插入到数组当中。

时间复杂度:
最好的情况下为 O(n) O ( n ) ,最坏的情况下为 O(n2) O ( n 2 ) ,平均情况: O(n2) O ( n 2 )
空间复杂度: O(1) O ( 1 )
稳定性:稳定

< code >

void InsertSort(DataType* a, size_t size) // 1.直接插入排序
{
    assert(a != NULL);
    int end = 0;
    size_t i = 0; //记录end退后之前的坐标

    for (i = 0; i < size - 1; ++i)
    {
        end = i;
        while (end >= 0)
        {
            if (a[end] > a[end + 1])
            {
                Swap(&a[end], &a[end + 1]);
                end--; //往后找到这个元素的合适位置
            }
            else
            {
                break;
            }
        }
    }
}

2. 希尔排序

  希尔排序也叫缩小增量排序,算是直接插入排序的进阶版。我们知道直接插入排序的效率和元素的有序度有关,数组中的元素越有序那排序的速度越快。所以我们可以在排序前先进行预排序,提高元素的有序度。

预排序:
  直接插入排序,不管数组元素分布是怎么样的,它都逐步逐步地对元素进行比较,移动,插入。如果数组是[9, 8, 7, 6, 5, 4, 3, 2, 1]这种倒序序列,要把1插入到正确位置,比较和移动操作都需要进行8次。

  在希尔排序中,我们不再一步步操作元素,而是采用跳跃式分组的策略。例如在数组a[N]中,我们设置一个增量 gap=length/3+1 g a p = l e n g t h / 3 + 1 length l e n g t h 为数组长度),通过这个增量将数组元素划分为若干组。
第一组:a[0], a[0+gap], a[0+2*gap], a[0+3*gap]……
第二组:a[1], a[1+gap], a[1+2*gap], a[1+3*gap], a[1+4*gap]……
第三组:a[2], a[2+gap], a[2+2*gap], a[2+3*gap], a[2+4*gap]……
……
  然后分组进行插入排序,随后逐步缩小增量gap,继续按组进行插入排序操作。当增量为1,进行一次直接插入排序。

  希尔排序通过这种策略使得整个数组在初始阶段达到从宏观上看基本有序,小的基本在前,大的基本在后。然后缩小增量,到增量为1时,其实多数情况下只需微调即可,不会涉及过多的数据移动。

第一次排序 gap=10/3+1=4 g a p = 10 / 3 + 1 = 4
这里写图片描述

< code >

void ShellSort(int* a, size_t n)//希尔排序
{
    assert(a != NULL);

    //预排序
    int gap = n / 3 + 1;//希尔排序所需要的跨度
    while (gap > 1)
    {
        for (int i = 0; i < n - gap; ++i)
        {
            int end = i;
            while (end >= 0)
            {
                if (a[end] > a[end + gap])
                {
                    Swap(&a[end], &a[end + gap]);
                    end -= gap;
                }
                else
                {
                    break;
                }
            }
        }
        gap = gap / 3 + 1;
    }
    //直接插入排序
    InsertSort(a, n);
}

时间复杂度:最好情况 O(n) O ( n ) ,最坏情况 O(n2) O ( n 2 ) ,平均情况 O(n1.3) O ( n 1.3 )
空间复杂度: O(1) O ( 1 )
稳定性:稳定


二、选择排序

1. 选择排序

  选择排序的思想是很简单的,遍历一遍数组,选出其中最大或最小的数字,把它放到合适的位置上。但这里我们直接对选择排序进行优化,每遍历一次数组,同时找到一个最大值和最小值,然后将两者分别放在它们应该出现的位置,这样遍历的次数就比较少了,效率也略微提高了一点。

  选择排序的具体实现是利用两个指针left和right来标记无序元素范围,开始时这两个指针指向数组首尾元素,a[left]~a[right]都是无序的,然后遍历一遍选出最大数和最小数,让最小数和a[left]交换位置,最大数和a[right]交换位置,接着left++、right–,缩小无序元素范围再进行上面操作。

  但这里有个点需要注意,如果最大元素本来就是数组首元素即a[left],当最小数和a[left]交换位置后最大元素的位置已经改变了,这就会导致排序错误。

  虽然我们对选择排序进行了优化,但要知道的是选择排序几乎是排序算法中最low的一种,在最好的情况下即数组已经有序了,它任然要遍历所有元素。

时间复杂度:最好情况 O(n2) O ( n 2 ) ,最坏情况 O(n2) O ( n 2 ) ,平均情况 O(n2) O ( n 2 )
空间复杂度: O(1) O ( 1 )
稳定性:不稳定。

< code >

void SelectSort(DataType* a, size_t size) //3.选择排序
{
    assert(a != NULL);

    size_t left = 0;
    size_t right = size - 1;

    while (left < right)
    {
        size_t max = left; //开始时都以第一个元素作为基准
        size_t min = left;

        size_t i = left;
        for (i = left; i <= right; ++i)
        {
            /*找出这一趟中最大的元素和最小的元素*/
            if (a[i] > a[max])
            {
                max = i;
            }
            if (a[i] < a[min])
            {
                min = i;
            }
        }
        Swap(&a[left], &a[min]);

        /*如果最大的元素是数组的首元素,即a[left] == a[Max],那么Swap(&a[left], &a[min])执行完后,最大值的位置就已经被改变。*/
        if (left != max)
        {
            Swap(&a[right], &a[max]);
        }
        else
        {
            max = min;
            Swap(&a[right], &a[max]);
        }

        left++;
        right--;
    }
}

2. 堆排序

  1. 将数组建堆,排升序,建大堆;排降序,建小堆。
    1. 先找到第一个非叶子结点,下标为 (n2)>>1 ( n − 2 ) >> 1 ;
    2. 找出该结点中左右孩子中值较大的节点,交换父亲节点和较大孩子节点;
    3. 交换完后,继续找第二个非叶子结点,重复第二步 ;
    4. 直到找到根结点,建堆完毕 ;
  2. 用替换法进行排序
    1. 用一个end指针标识堆尾,先将建大堆之后的首尾交换,则最大的数在最末尾,最大元素已经处于正确位置,end–,接下来排序前面n-1个元素;
    2. 使用向下算法重新排序堆中前n-1个元素,排序完后堆顶元素就是此时堆中最大数(整个数组中的第二大数),再把此时堆中最后一个叶子节点和堆顶元素交换位置,再将末尾向前移动一个位置,即end–;
    3. 重复第二步,直到找到根结点,排序完毕。

关于堆排序的时间复杂度:
排序的复杂度是: O(nlogn) O ( n l o g n )
  循环 n -1 次,每次都是从根节点往下进行调整,一次调整的时间是 logn l o g n ,总共要调整(n-1)次总时间: logn(n1)=nlognlogn l o g n ( n − 1 ) = n l o g n − l o g n ,最后结果就是 O(nlogn) O ( n l o g n )

初始化建堆过程时间: O(n) O ( n )  推算过程

< code >

//向下调整法,大堆
void AdjustDown(DataType* a, size_t size, size_t parent)
{
    size_t child = parent * 2 + 1;

    while (child < size)
    {
        if (a[child] < a[child + 1] && (child + 1) < size)
        {
            child++;
        }

        if (a[child] > a[parent])
        {
            Swap(&a[child], &a[parent]);

            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

void HeapSort(DataType* a, size_t size)   //4.堆排序
{
    assert(a != NULL);

    //建大堆
    int i = (size - 2) >> 1;
    for (; i >= 0; --i)
    {
        AdjustDown(a, size, i);
    }

    int end = size - 1;
    while (end > 0)
    {
        Swap(&a[end], &a[0]);
        AdjustDown(a, end, 0);
        end--;
    }
}

三、交换排序

1. 冒泡排序

冒泡排序算是这几种排序中最简单的了,设有数组a[N](排升序):
第一趟:从a[0]~a[N-1]中选出一个最大数放到当前范围的最后一个位置a[N-1]处。
第二趟:从a[0]~a[N-2]中选出一个最大数放到当前范围的最后一个位置a[N-2]处。
第三趟:从a[0]~a[N-3]中选出一个最大数放到当前范围的最后一个位置a[N-3]处。
……

(1)冒泡优化

  同样冒泡排序也是可以进行优化的。设置一个标志符flag,flag初始值是0,如果在一趟遍历中有出现元素交换的情况,那就把flag置为1。一趟冒泡结束后,检查flag的值。如果flag依旧是0,那这一趟就没有元素交换位置,也就是说数组此时已经是有序的了,循环结束。这样可以减少外层循环的次数,提高了效率。


(2)冒泡进阶优化

  上面我们已经利用了flag标志优化了外层循环,此时如还想再优化只能从内层循环入手了。我们知道,内存循环每循环一趟,就能确定一个元素的正确位置,但对于 5 1 3 4 2 6 7 8 9 这样的序列,我们要冒泡四趟才能确定 6 7 8 9 这四个元素的顺序,但其实他们本就是有序的了,我们可以用一趟冒泡就确定这四个元素的的顺序。
  定义一个变量k,用来记录每趟循环最后发生交换的元素的下标(它后面的元素没有进行交换,就表示已经有序了),下一趟冒泡只需对k之前的元素进行冒泡,进一步提高了排序效率。


时间复杂度:最好情况 O(n) O ( n ) ,最坏情况 O(n2) O ( n 2 ) ,平均情况 O(n2) O ( n 2 )
空间复杂度: O(1) O ( 1 )
稳定性:稳定。
< code >

void BubbleSort(DataType* a, size_t size) //5.冒泡排序
{
    if (NULL == a || size < 2)
        return;

    size_t k = size - 1; //记录一趟冒泡最后进行交换的元素的下标
    size_t m = 0;
    size_t i = 0;
    size_t j = 0;
    size_t flag = 0;

    for (i = 0; i < size - 1; ++i)
    {
        flag = 0;
        for (j = 0; j < k; ++j)
        {
            if (a[j] > a[j + 1])
            {
                Swap(&a[j], &a[j + 1]);
                flag = 1;
                m = j;
            }
        }
        if (0 == flag)
            break;

        k = m;
    }
}

2. 快速排序

  快速排序可以说是这里面最难理解的算法之一了,它的总思想是——分治。主要步骤是:
1. 从数组中选取一个数key作为基准。
2. 分区,把比key小的数放到它左边,把比key大的数放到它右边。
3. 再对左右区间进行第二步操作,直到区间中只有一个元素。

  三个步骤看起来清楚明了,看似复杂的第三步其实只要用递归就能完成,那第二步分区又该如何实现呢?下面介绍分区的三种实现方法(三种方法复杂度一样)。

时间复杂度:最好情况: O(nlgn) O ( n l g n ) ,最坏情况: O(n2) O ( n 2 ) ,平均情况: O(nlgn) O ( n l g n )
空间复杂度: O(lgn) O ( l g n )
稳定性:不稳定


(1)三种实现方法

ps:默认数组尾元素为基准key

①左右指针法

设数组a[N],left和right是数组的首、尾元素下标。

  1. 设置两个指针start和end分别指向数组的首、尾元素。
    这里写图片描述
  2. start指针往后走,找到第一个大于key的数;end指针往前走,找到第一个小于key的数,交换两数位置。
    这里写图片描述
  3. 重复第二步,当start>=end时,循环结束,交换a[start]和a[right](a[right]就是基准key)。此时key左边的数都比它小,右边的数都比他大。
    这里写图片描述

< code >

DataType Part1Sort(DataType* a, size_t left, size_t right)//方法一:左右指针法
{
    assert(a != NULL);

    size_t start = left;
    size_t end = right;

    DataType key = a[right]; //选取数组尾元素为基准

    while (start < end)
    {
        while (start < end && a[start] <= key) //找到第一个大于key的数
        {
            start++;
        }

        while (start < end && a[end] >= key)   //找到第一个小于key的数
        {
            end--;
        }

        Swap(&a[start], &a[end]);
    }
    Swap(&a[start], &a[right]);

    return start;
}

②挖坑法

设数组a[N],left和right是数组的首、尾元素下标。

  1. 把数组尾元素赋值给基准key,此时数组最后一个位置a[right]就相当于一个‘坑’。

  2. 设置两个指针start和end分别指向数组的首、尾元素。
    这里写图片描述

  3. start指针往后走,找到第一个大于key的数,把他放到坑里面,即a[right]处。此时a[right]这个‘坑’就填上了,而a[start]处又成了一个新的坑。
    这里写图片描述
  4. end指针往前走,找到第一个小于key的数,把他放到坑里面,即a[start]处。此时a[start]这个‘坑’就填上了,而a[end]处又成了一个新的坑。
    这里写图片描述
  5. 重复第4、5步,直到start>=end,循环结束。因为start指针是最先走的,所以最后一个‘坑’的位置在start处,把最开始挖出去的基准key填到a[start]处,此时key左边的数都比它小,右边的数都比它大。
    这里写图片描述

< code >

DataType Part2Sort(DataType* a, size_t left, size_t right) //方法二:挖坑法
{
    assert(a != NULL);

    size_t start = left;
    size_t end = right;

    DataType key = a[right];

    while (start < end)
    {
        while (start < end && a[start] <= key)
        {
            start++;
        }
        a[end] = a[start];

        while (start < end && a[end] >= key)
        {
            end--;
        }
        a[start] = a[end];
    }
    a[start] = key;

    return start;
}
③前后指针法

  写不动了,这里就直接上代码。

< code >

DataType Part3Sort(DataType* a, int left, int right) //方法三:前后指针法
{
    assert(a != NULL);

    DataType cur = left;
    DataType prev = left - 1;
    DataType key = a[right];

    // 简洁写法
    //while (cur < right)
    //{
    //  if (a[cur] <= key && a[cur] != a[++prev])
    //  {
    //      Swap(&a[cur], &a[prev]);
    //  }
    //  cur++;
    //}
    //Swap(&a[++prev], &a[cur]);
    //return prev;

    // 基础写法
    while (cur < right)
    {
        if (a[cur] <= key)
        {
            ++prev;
            if (cur != prev)
            {
                Swap(&a[prev], &a[cur]);
            }
        }
        ++cur;
    }
    ++prev;
    Swap(&a[prev], &a[cur]);

    return prev;
}

(2)优化key的两种方法

  快速排序的效率和基准key的值相关,如果key选的好,(每次key恰好是中间数),那么算法的时间复杂度就是 O(nlogn) O ( n l o g n ) ,但如果选的不好(每次key恰好是最大值或者最小值),那么算法的时间复杂度就是 O(n2) O ( n 2 ) 。上面我们都是选数组尾元素作为基准,如果尾元素恰好是最大值或最小值,那就很坑了。所以这里必须优化key的选取方法。


①随机值法

  首先声明一下,本人觉得这种方法效果并不大。
  这里顾名思义就是从数组中随机选取一个值作为key(但这个随机值还可能是最大值或最小值啊)。

简单写一下代码:

sand(time(0));
size_t index = rand()%(right - left + 1);
size_t key = a[index];

②三数取中法

  重头戏来了,这里的三数取中法是如何实现的呢?

  选取数组的首元素、尾元素以及中间元素(注意这里是中间元素,不是中位数),选出三者的中位数作为key值。这时候选出来的key可以保证绝对不是最大数或最小数(如果三个数都是相同的,那key还是最大或最小值,不过出现这种情况概率及其低)。

注意:我们找出中位数作为key值后,把这个数和数组尾元素交换一下位置。

  下面放上左右指针法优化了选取key值后的代码。

< code >

int GetMidNumber(DataType*a, size_t left, size_t right) //返回三个数的中间数的下标
{
    DataType mid = left + ((right - left) >> 1);

    // 1<2<3 || 3<2<1
    if ((a[left] < a[mid] && a[mid] < a[right])
        || (a[mid] > a[right] && a[left] > a[mid]))
    {
        return mid;
    }

    //2<3<1 || 2<1<3
    else if ((a[left] > a[right] && a[left] < a[mid])
        || (a[left] > a[mid] && a[left] < a[right]))
    {
        return left;
    }

    //1<3>2 || 3>1<2
    else
    {
        return right;
    }
}

DataType Part1Sort(DataType* a, size_t left, size_t right)//方法一:左右指针法
{
    assert(a != NULL);

    size_t start = left;
    size_t end = right;

    DataType MidIndex = GetMidNumber(a, left, right); //获得中间数下标
    DataType key = a[MidIndex];
    Swap(&a[MidIndex], &a[right]); //把中间数和最后一个数交换位置,这样下面的代码就不需要更改

    while (start < end)
    {
        while (start < end && a[start] <= key) //找到第一个大于key的数
        {
            start++;
        }

        while (start < end && a[end] >= key)   //找到第一个小于key的数
        {
            end--;
        }

        Swap(&a[start], &a[end]);
    }
    Swap(&a[start], &a[right]);

    return start;
}

(3)小区间优化

  切割区间时,当区间内元素数量比较少时就不用切割区间了(递归太深影响效率),这时候就直接对这个区间采用直接插入法,可以进一步提高算法效率。


(4)栈实现非递归的快速排序

void QuickSortNR(int* a, int left, int right)//快排,非递归
{
    assert(a != NULL);
    Stack s;
    StackInit(&s);
    StackPush(&s, left);
    StackPush(&s, right);
    while (StackEmpty(&s) != 0)//栈不为空
    {
        int end = StackTop(&s);
        StackPop(&s);
        int start = StackTop(&s);
        StackPop(&s);

        int div = GetMidNumber(a, start, end);

        if (start< div-1)
        {
            StackPush(&s, start);
            StackPush(&s, div - 1);
        }
        if (end > div + 1)
        {
            StackPush(&s, div + 1);
            StackPush(&s, end);
        }
    }
}

四、归并排序

1. 归并排序

  1. 采取类似于快排的分治法,没有基准,直接把数组一分为二。
  2. 当二分为只剩下两个或一个元素的时候,比较大小排序。
  3. 递归回溯时,将数组两两进行合并,并且合并后保持有序 。
  4. 回溯完毕后,整个数组就有序了 。

时间复杂度:最好情况: O(nlgn) O ( n l g n ) ,最坏情况: O(nlgn) O ( n l g n ) ,平均情况: O(nlgn) O ( n l g n )
空间复杂度: O(N) O ( N )
稳定性: 稳定
这里写图片描述
< code >

void _MergeSort(DataType* a, size_t left, size_t mid, size_t right)
{
    DataType* tmp = (DataType*)malloc(sizeof(int)*(right - left + 1));
    assert(tmp != NULL);
    memset(tmp, 0, sizeof(int)*(right - left + 1));
    size_t index = 0; //tmp的初始下标

    size_t start1 = left; //第一个数组的范围
    size_t end1 = mid;

    size_t start2 = mid + 1; //第二个数组的范围
    size_t end2 = right;

    while (start1 <= end1 && start2 <= end2) //把两个数组的元素逐一比较,选出较小的放到tmp中
    {
        if (a[start1] <= a[start2])
        {
            tmp[index++] = a[start1++];
        }
        else
        {
            tmp[index++] = a[start2++];
        }
    }

    /*如果数组1或者数组2中还有元素,就直接复制到tmp中*/
    if (start1 <= end1) 
    {
        while (start1 <= end1)
        {
            tmp[index++] = a[start1++];
        }
    }
    else
    {
        while (start2 <= end2)
        {
            tmp[index++] = a[start2++];
        }
    }

    /*最后把数组tmp中元素全部拷贝到数组a中*/
    for (index = 0; index < right - left + 1; index++)
    {
        a[left + index] = tmp[index];
    }

    free(tmp);
}

void MergeSort(DataType* a, size_t left, size_t right)  //7.归并排序
{
    assert(a != NULL);
    if (left >= right)
    {
        return;
    }

    if (right - left + 1 > 5) //小区间优化法
    {
        size_t mid = left + ((right - left) >> 1);

        MergeSort(a, left, mid);
        MergeSort(a, mid + 1, right);
        _MergeSort(a, left, mid, right);
    }
    else
    {
        InsertSort(a + left, right - left + 1);
    }
}

五、计数排序

计数排序算法的原理跟哈希表的K-V模型比较相似
①遍历一遍数组,得出数组的范围range,创建一个大小为range的数组,即哈希表,初始化为全0。
②再从头开始遍历数组,数字重复出现一次,在其相应的位置对应的数值加1。
③从左到右开始遍历哈希表,将数值不为0的位置的下标存储到原数组中,且数值是多少就存储多少个 。
PS:计数排序会去除重复的元素。


六、各个算法效率、稳定性对比

类别直接插入排序希尔排序选择排序堆排序冒泡排序快速排序归并排序
平均时间复杂度 O(n2) O ( n 2 ) O(n1.3) O ( n 1.3 ) O(n2) O ( n 2 ) O(nlgn) O ( n l g n ) O(n2) O ( n 2 ) O(nlgn) O ( n l g n ) O(nlgn) O ( n l g n )
最好时间复杂度 O(n) O ( n ) O(n) O ( n ) O(n2) O ( n 2 ) O(nlgn) O ( n l g n ) O(n) O ( n ) O(nlgn) O ( n l g n ) O(nlgn) O ( n l g n )
最坏时间复杂度 O(n2) O ( n 2 ) O(n2) O ( n 2 ) O(n2) O ( n 2 ) O(nlgn) O ( n l g n ) O(n2) O ( n 2 ) O(n2) O ( n 2 ) O(nlgn) O ( n l g n )
空间复杂度 O(1) O ( 1 ) O(1) O ( 1 ) O(1) O ( 1 ) O(1) O ( 1 ) O(1) O ( 1 ) O(lgn) O ( l g n ) O(n) O ( n )
稳定性稳定不稳定不稳定不稳定稳定不稳定稳定
  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 代码如下: #include <stdio.h> void bubble_sort(int array[], int n) { int i, j, temp; for (i = 0; i < n-1; i++) // Last i elements are already in place for (j = 0; j < n-i-1; j++) if (array[j] > array[j+1]) { temp = array[j]; array[j] = array[j+1]; array[j+1] = temp; } } // Driver code int main() { int array[] = {64, 34, 25, 12, 22, 11, 90}; int n = sizeof(array)/sizeof(array[0]); bubble_sort(array, n); printf("排序后的数组: \n"); for (int i=0; i < n; i++) printf("%d ", array[i]); return 0; } ### 回答2: 冒泡排序是一种简单但效率较低的排序算法。下面是使用C语言编写冒泡排序的示例代码: ```c #include <stdio.h> void bubbleSort(int arr[], int n) { for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } int main() { int arr[] = {64, 34, 25, 12, 22, 11, 90}; int n = sizeof(arr)/sizeof(arr[0]); printf("排序前数组:\n"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } bubbleSort(arr, n); printf("\n排序后数组:\n"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; } ``` 以上代码中,我们定义一个冒泡排序函数`bubbleSort`,参数`arr`为待排序的数组,`n`为数组的长度。在每一轮排序中,我们比较相邻的两个元素,如果前面的元素大于后面的元素,则交换它们。这样每一轮结束后,最大的元素都会被放置在末尾。通过多次循环,直到全部元素都有序,排序完成。在`main`函数中,我们初始化一个整数数组并调用`bubbleSort`函数对其进行排序,最后输出排序后的数组。 ### 回答3: 冒泡排序是一种简单但效率较低的排序算法。它通过比较相邻的两个元素,如果顺序不对则交换位置,一次遍历可以将最大的元素放到末尾。重复遍历,每次都能确定一个最大元素的位置,直到全部元素都排序好。 下面是用C语言实现冒泡排序的代码: ```c #include<stdio.h> void bubbleSort(int arr[], int n) { for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (arr[j] > arr[j+1]) { // 交换arr[j]和arr[j+1]的位置 int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } int main() { int arr[] = {5, 3, 8, 2, 1, 4}; int n = sizeof(arr) / sizeof(arr[0]); printf("排序前的数组:"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } bubbleSort(arr, n); printf("\n排序后的数组:"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; } ``` 在上面的代码中,`bubbleSort`函数使用两层循环来实现冒泡排序。外层循环从0到`n-1`,表示需要进行`n-1`次遍历。内层循环从0到`n-i-1`,表示每次遍历的范围,因为每次遍历都会将最大的元素放到末尾,所以下一次遍历范围要减小。在内层循环中,比较相邻的两个元素,如果前一个元素大于后一个元素,则交换位置。 在`main`函数中,我们定义了一个整型数组`arr`并初始化。然后调用`bubbleSort`函数对数组进行排序,并打印排序前后的结果。 运行以上代码,输出结果为: ``` 排序前的数组:5 3 8 2 1 4 排序后的数组:1 2 3 4 5 8 ``` 这样就完成了用C语言写冒泡排序的实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值