C语言,排序

前言

        排序,可以说是数据结构中必不可缺的一环。我们创造数据存储它,要想知道数据之间的联系,比较是必不可少的。不然,费劲心思得来的数据若是不能有更多的意义,那么拿到了又有什么用?

        排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分为内部排序和外部排序,若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。

一. 排序的种类

        快速排序、希尔排序、堆排序、直接选择排序不是稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。

        稳定排序:假设在待排序的文件中,存在两个或两个以上的记录具有相同的关键字,在用某种排序法排序后,若这些相同关键字的元素的相对次序仍然不变,则这种排序方法是稳定的。其中冒泡,插入,基数,归并属于稳定排序,选择,快速,希尔,归属于不稳定排序。

        具体为什么这些排序是稳定排序,或者是不稳定排序,会在之后进行图片演示。

        根据上述介绍可以知道,总共的排序分为8种,接下来我会挑出重要的和大家讲解。

二. 排序的实现

        一下排序都是按照排升序的方式进行的,请读者注意。如果想要让排序的功能更加丰富,推荐读者像qsort这样的标准函数一样,传入比较的函数指针,由于只是讲解比较原理,故简化了。

1. 插入排序

1.1. 原理

图1-1 插入排序原理图

        如图1-1所示,开始的时候数据的储存入第A行所示,我们需要将从第二个数据的位置开始,向前一次插入数据,将小的放在前面。从目标位置向前遍历的时候,如果目标数据小于比较数据,就将比较的数据向后移动一格。因此我们需要提前记录目标位置的值。

        从A行的第二列开始,2小于5,就将5向后移动一位,到顶了,就将2赋给第一列。同理,到了第三列的4,4小于5,就将5向后挪动一列,再继续比较,4大于2,就不需要挪动2,将4赋给第二列。按照上述规律,依次插入,直到到第E行,将第六列的3插入到第三列为止。

1.2. 代码

// 插入排序
void InsertSort(int* a, int n)
{
    // a, 不能为空
    assert(a);

    for(int i = 0; i < n - 1; ++i)
    {
        // 一次插入排序
        int end = i;
        int tmp = a[end + 1];
        while(end >= 0)
        {
            if(tmp < a[end]) // 升序中, 小于插入的值就挪动数据
            {
                a[end + 1] = a[end];
                end--;
            }
            else    // 反之跳出循环赋值
            {
                break;
            }
        }
        a[end + 1] = tmp;
    }
}

2. 希尔排序

2.1. 介绍

        希尔希尔(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。

        希尔排序是一种比较厉害的排序,虽然没有稳定性,但是排序的时间复杂度是小于O(N^2)的。所以,希尔排序是一种很厉害的排序。快的表现和快速排序、堆排序、归并排序是一个等级的。

2.2. 原理

图2-1 希尔排序原理图

        如果插入排序是希尔排序的一种特殊情况,插入排序相当于希尔排序增量为1的时候的排序。那么为什么诞生的希尔排序有它的意义呢?从第一趟增量为5的时候,相当于给数据分成了5个部分,大大减小了排序的范围。在第二趟的排序中,会造成的效果就是,交换的时候每次增量不超过上一次排序的增量也就是5。因此提高了效率。

        在性能上,希尔排序的时间复杂度区间在O(N^(3/2))到O(N*logN)之间,不需要大量的辅助空间,因此数据排序在中等规模中表现良好。但是对于规模非常大的数据时不是最佳选择,数据量大的时候仍然推荐快速排序。至于时间复杂度和降低时间复杂度的方法是需要非常复杂的数学模型的,专家们正在研究,如今仍然是数学难题。

2.3. 代码

// 希尔排序
void ShellSort(int* a, int n)
{
    // a, 不能为空
    assert(a);

    // 希尔排序相当于插入排序中有多次预排序
    int gap = n;

    while(gap > 1) //  控制预排序间隔,通常分为三块
    {
        gap = gap / 3 + 1;
        for(int i = 0; i < n - gap; ++i)
        {
            // 一次插入排序
            int end = i;
            int tmp = a[end + gap];
            while(end >= 0)
            {
                if(tmp < a[end]) // 升序中, 小于插入的值就挪动数据
                {
                    a[end + gap] = a[end];
                    end -= gap;
                }
                else    // 反之跳出循环赋值
                {
                    break;
                }
            }
            a[end + gap] = tmp;
        }
    }  
}

3. 选择排序

3.1. 原理

        直接选择排序(Straight Select Sorting) 也是一种简单的排序方法,它的基本思想是:第一次从R[0]~R[n-1]中选取最小值和最大值,与R[0]、R[n-1]交换,第二次从R[1]~R[n-2]中选取最小值和最大值,与R[1]、R[n-2]交换,....,第i次从R[i-1]~R[n-1-i]中选取最小值和最大值,与R[i-1]、R[n-1-i]交换,.....,重复n/2次,得到一个按排序码从小到大排列的有序序列。

        因为非常的简单,就是反复遍历数组将最小值最大值分别放到数组首和数组尾,所以时间复杂度很稳定,但也很大,一直都是O(N^2)。

3.2. 代码

void swap(int* a, int p1, int p2)
{
    int tmp = a[p1];
    a[p1] = a[p2];
    a[p2] = tmp;
}

// 选择排序
void SelectSort(int* a, int n)
{
    assert(a);

    int begin = 0, end = n - 1;
    while(begin < end)  // 一次循环找到最大值和最小值,
    {
        int mini = begin, maxi = end;
        for(int i = begin; i <= end; ++i) // 一次循环找到最大值和最小值
        {
            if(a[i] < a[mini]) // 找最小
            {
                mini = i;
            }
            if(a[i] > a[maxi]) // 找最大
            {
                maxi = i;
            }
        }

        if(maxi == begin) // 如果最大值在第一位,就需要改变交换的位置
        {
            maxi = mini;
        }

        swap(a, begin, mini); // 交换数据
        swap(a, end, maxi);

        ++begin; // 控制结束循环的条件
        --end;
    }
}

4. 堆排序

4.1. 原理

        原理相关的可以参考之前在二叉树部分推出的堆排序,是一样的。利用二叉树的原理建堆,将最大的数放在堆顶,然后将堆顶的数据放到末尾之后重新从堆头调整数据,这样堆的数据每次都会减少一个,直到全部完成排序。

        建堆的方式选择向下建堆,完成排序的时间复杂度是O(N*logN)。

4.2. 代码

// 堆排序
void AdjustDown(int* a, int n, int parent)
{
    assert(a);

    // 假设左孩子更大, 公式:child = parent * 2 + 1;
    int child = parent * 2 + 1;

    // 孩子节点没越界就继续比较
    while(child < n)
    {
        // 调整左右孩子,取更小的
        if(child + 1 < n && a[child + 1] > a[child])
        {
            ++child;
        }

        // 如果满足条件就交换父子位置并继续向下遍历
        if(a[parent] < a[child])
        {
            swap(a, parent, child);
            parent = child;
            child = parent * 2 + 1;
        }
        else// 反之,跳出循环(无需继续调整)
        {
            break;
        }
    }
}

void HeapSort(int* a, int n)
{
    assert(a);

    //向下调整建堆(大堆)
    int i = n / 2 - 1;
    while(i >= 0)
    {
        AdjustDown(a, n, i);
        --i;
    }

    // 得到降序数组
    while(--n)
    {
        swap(a, 0, n);
        AdjustDown(a, n, 0);
    }
}

5. 冒泡排序

5.1. 原理

        冒泡排序(Bubble Sort)是最简单和最通用的排序方法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直至最终完成排序。由此可得,在排序过程中,大的数据往下沉,小的数据往上浮,就像气泡一样,于是将这种排序算法形象地称为冒泡排序。

图5-1 冒泡排序原理图

        算法的原理如图5-1所示,在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换。比如第一趟排序,比较26和28,26小于28故不做改变,到了28和24,28大于24,所以就交换数据位置,再向下比较28和11,仍然需要交换,这样就完成了一轮,28不在需要比较。然后依次排序。

5.2. 代码

// 冒泡排序
void BubbleSort(int* a, int n)
{
    for(int i = 0; i < n - 1; ++i)  // 循环趟数
    {
        for(int j = 1; j < n - i; ++j)  // 循环一趟
        {
            if(a[j] < a[j - 1]) // 把大的元素往后放,使数据提增
            {
                swap(a, j, j - 1);
            }
        }
    }
}

6. 快速排序

6.1. 原理

        快速排序算法通过多次比较和交换来实现排序,其排序流程如下:

        (1)首先设定一个分界值,通过该分界值将数组分成左右两部分。

        (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。

        (3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。

        (4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

图6-1 快速排序举例图

        快速排序在分部中分为三种方法:最标准的霍尔法、挖坑法以及前后指针法。如图6-1所示,快速排序的方法是挖坑法。首先在数组中记录分界值(这里是49),设置头指针和尾指针。如果要将数据排列为升序,那么尾指针从后向前遍历找小于分界值的数(找到了27)停下来,将这个放到之前记录分界值位置的部分,也可以说是头指针部分,然后将27填充到49处,表示填坑。这样27的位置相当于挖了一个坑。然后就然头指针找大于分界值的数(这里找到的是65),然后将65填充到尾指针的位置处(27)。之后移动尾指针,重复这个循环,直到头指针和尾指针相遇,将记录的分界值填入。结果如图6-1中的一次划分后所示。每次划分都能够确定一个值的具体位置,这里确定的是49。之后将49前后的数据分为新的空间继续进行划分即可完成排序。

        霍尔法,是使用头指针从左找大,尾指针从后找小,然后交换位置。相遇之后交换分界值的位置,和挖坑法最大的不同之处在于49的位置是最后移动的。

        前后指针法,前指针找到小于分界值的数就和后指针位置的数交换,然后后指针也向前推进。与前两者最大的不同是,指针的移动是按照同方向进行的。

6.2. 代码

int Midi(int* a, int left, int right)
{
    int midi = (left + right) / 2;
    if(a[left] > a[midi])
    {
        if(a[midi] > a[right])
        {
            return midi;
        }
        else if(a[left] > a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
    else // a[left] < a[midi]
    {
        if(a[midi] < a[right])
        {
            return midi;
        }
        else if(a[left] < a[right])
        {
            return right;
        }
        else
        {
            return left;
        }
    }
}

// 快速排序递归实现
// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
    int key = left;
    int begin = left + 1, end = right;
    while(begin < end)
    {
        while(begin < end && a[end] >= a[key])
        {
            --end;
        }
        while(begin < end && a[begin] <= a[key])
        {
            ++begin;
        }
        swap(a, begin, end);
    }
    swap(a, key, end);
    return end;
}

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
    int key = a[left];
    int begin = left, end = right;
    while(begin < end)
    {
        while(begin < end && a[end] >= key)
        {
            --end;
        }
        a[begin] = a[end];
        while(begin < end && a[begin] <= key)
        {
            ++begin;
        }
        a[end] = a[begin];
    }
    a[end] = key;
    return end;
}

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
    int key = a[left];
    int prev = left, cur = left + 1;
    while(cur <= right)
    {
        if(a[cur] < key)
        {
            ++prev;
            swap(a, prev, cur);
        }
        ++cur;
    }
    swap(a, left, prev);
    return prev;
}


void QuickSort(int* a, int left, int right)
{
    assert(a);

    //区间过小直接返回
    if(left >= right)
    {
        return;
    }
    //如果区间过小使用插入排序
    // if(right - left  < 10)
    // {
    //     SelectSort(a, right - left + 1);
    // }

    // 三数取中确定key
    int key = Midi(a, left, right);
    swap(a, left, key);

    key = PartSort3(a, left, right);
    QuickSort(a, left, key - 1);
    QuickSort(a, key + 1, right);
}

6.3. 非递归的实现

        非递归的实现就需要解决区间的问题,所以我们需要向之前数据结构中,队列和栈中借过来存储区间位置即可。代码如下:


// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{
    assert(a);
    Stack st;
    StackInit(&st);
    StackPush(&st, right);
    StackPush(&st, left);
    
    while(!StackEmpty(&st))
    {
        int begin = StackTop(&st);
        StackPop(&st);
        int end = StackTop(&st);
        StackPop(&st);

        // 三数取中确定key
        int keyi = Midi(a, begin, end);
        swap(a, begin, keyi);

        keyi = PartSort2(a, begin, end);
        //区间过小就不入栈
        if(begin < keyi - 1)
        {
            StackPush(&st, keyi - 1);
            StackPush(&st, begin);
        }

        if(keyi < end)
        {
            StackPush(&st, end);
            StackPush(&st, keyi + 1);
        }
    }

    StackDestroy(&st);
}

7. 归并排序

7.1. 原理

图7-1 归并排序原理图

        归并排序,就是将两组都是有序的数据合成一组的排序。所以对于原来的数组如图7-1所示,先拆分为不可继续分割的区间,然后分别将其合并,排列成有序数组,为下一次归并做准备。例如这里的10和4,组合之后成为[4,10],3和6组合之后成为[3,6]。然后再将这两个区间组合成[3,4,6,10]。直到组合完成所有数据。

        ps:实际在代码中比较需要建立额外的位置存放排序的数据,另外还需要每次排列完成数据后将数据赋值给原来的数组。相互比较的时候也需要注意,会有一个区间的数据没完全存入,需要分出一步完成该过程。

7.2. 代码

// 归并排序,子函数
void _MergeSort(int* a, int* tmp, int left, int right)
{
    // 区间过小,直接返回
    if(left >= right)
    {
        return;
    }

    // 分区间
    int midi = (left + right) / 2;
    // [left, midi] [mide + 1, right]

    _MergeSort(a, tmp, left, midi);
    _MergeSort(a, tmp, midi + 1, right);

    // 归并
    int begin1 = left, end1 = midi;
    int begin2 = midi + 1, end2 = right;
    int i = begin1;
    // 控制比较次数
    while(begin1 <= end1 && begin2 <= end2)
    {
        //小的先存
        if(a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1++];
        }
        else
        {
            tmp[i++] = a[begin2++];
        }
    }

    // 存下剩余部分
    while(begin1 <= end1)
    {
        tmp[i++] = a[begin1++];
    }
    while(begin2 <= end2)
    {
        tmp[i++] = a[begin2++];
    }

    // 将暂存数据返回
    memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

// 归并排序
void MergeSort(int* a, int n)
{
    // 检查数组存在
    assert(a);

    // 开辟额外的空间暂存排序后数据
    int* tmp = (int*)malloc(sizeof(int) * n);
    if(tmp == NULL)
    {
        perror("malloc fail");
        return;
    }

    // 分装子函数,实现归并功能
    _MergeSort(a, tmp, 0, n - 1);
    free(tmp);
}

7.3. 非递归实现

        和快速排序一样,需要解决的是区域划分的问题。那么如何划分区间呢?

        方法是设置区间的大小值,每次排序都按照这个大小划分区间,没完成一次归并就将这个区间的值翻倍,如图7-1所示,分解区间,开始区间的大小为1,之后是2/4/8。当然划分区间需要调整,因为数组的大小可能并不是分区间的倍数。如果数组剩下的元素不满于一个区间,就不需要继续排序,如果有一个区间但是不足第二个,就需要修剪第二个区间的范围。

        代码如下:

// 归并排序, 非递归
void MergeSortNonR(int* a, int n)
{
    assert(a);

    // 开辟额外的空间暂存排序后数据
    int* tmp = (int*)malloc(sizeof(int) * n);
    if(tmp == NULL)
    {
        perror("malloc fail");
        return;
    }

    int gap = 1; // 表示区间间隔
    while(gap < n)
    {
        // 将所有区间分为n / gap + 1 份, 每次比较两个区间
        for(int i = 0; i < n; i += 2 * gap)
        {
            int begin1 = i, end1 = begin1 + gap - 1;
            int begin2 = end1 + 1, end2 = begin2 + gap - 1;
            int j = begin1;

            if(n - 1 < begin2)// 剩余区间过小,不比较
            {
                break;
            }
            else if(n - 1 < end2) // 如果区间2在数组结束之前,end2就是数组尾
            {
                end2 = n - 1;
            }

            //归并
            // 控制比较次数
            while(begin1 <= end1 && begin2 <= end2)
            {
                //小的先存
                if(a[begin1] < a[begin2])
                {
                    tmp[j++] = a[begin1++];
                }
                else
                {
                    tmp[j++] = a[begin2++];
                }
            }

            // 存下剩余部分
            while(begin1 <= end1)
            {
                tmp[j++] = a[begin1++];
            }
            while(begin2 <= end2)
            {
                tmp[j++] = a[begin2++];
            }

            // 将暂存数据返回
            memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));
        }

        gap *= 2; // 循环的递增
    }
    free(tmp);
}

作者结语

        说到底,排序并不是需要用论文的方式记录的东西,这些排序的存在都已经有很长时间了,可以说计算机行业家喻户晓,所以只能算是整理了自己的学习过程。

        写的也比较简单,原理然后接代码,从文本的角度来讲还是较难理解的,但是接触过的都能回到意思。这也是这篇博客的不足之处,本来是给小白学习的,但是小白却可能看不懂。

        无论如何,博客已经出炉了,希望各大高手指点指点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/676043.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

老挝语翻译通App中国人出门在外都在用的老挝语翻译工具,支持老挝文OCR识别、文字转语音、老挝语背单词学习等等功能!

老挝语翻译通App&#xff0c;一款更加符合中国人用语习惯的翻译工具&#xff0c;在国内外都能正常使用的翻译器。当大家选择去东南亚国家旅游、GAP的时候&#xff0c;老挝这个国家是值得一去的&#xff0c;可以让大家感受到另一番风情。 但是&#xff0c;在去之前&#xff0c;需…

关于序列化与反序列化解题

1、[安洵杯 2019]easy_serialize_php <?php$function $_GET[f];function filter($img){$filter_arr array(php,flag,php5,php4,fl1g);$filter /.implode(|,$filter_arr)./i;return preg_replace($filter,,$img); }if($_SESSION){unset($_SESSION); }$_SESSION["use…

Linux学习笔记:日志文件的编写

日志文件Log.hpp 日志文件的作用简单的日志文件编写 日志文件的作用 日志文件可以很好的帮我们显示出程序运行的信息,例如,进程pid,运行时间,运行状况等,通过日志记录程序的执行路径、变量值、函数调用等&#xff0c;可以帮助我们快速定位和修复代码中的错误。 简单的日志文件…

UMG绝对坐标与局部空间

在 Unreal Engine 的 UMG&#xff08;Unreal Motion Graphics&#xff09;中&#xff0c;“绝对坐标”和“局部空间”是两个常见的概念&#xff0c;主要用于描述 UI 元素的位置和大小。 概念与区别 绝对坐标&#xff08;Absolute Coordinates&#xff09;&#xff1a;这是指相…

LeetCode1137第N个泰波那契数

题目描述 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 解析 递归应该会超时&#xff0c;可以用循环&#xff0c;或者官方解法的矩阵的幂。 public int tr…

索尼CEO宣布全力推进AI电影制作,《蜘蛛侠》制片人坚称不用AI

原标题&#xff1a;索尼互娱制片人与CEO唱反调 易采游戏网6月3日消息&#xff1a;在最近的一次行业会议上&#xff0c;索尼影业高层首席执行官托尼文西奎拉向媒体透露&#xff0c;索尼正在全力推进人工智能(AI)技术的研发与应用&#xff0c;特别是在电影制作流程中。这一策略旨…

彩光大放异彩!《智慧园区以太全光网络建设技术规程》应用案例征集活动结果公布

近日,中国建筑业协会绿色建造与智能建筑分会正式公布了《智慧园区以太全光网络建设技术规程》应用案例征集活动的结果。本次活动旨在推广和应用该规程,进一步推动智慧园区的数字化、智慧化、绿色化建设。众多优秀项目在征集活动中脱颖而出,展示了规程在实际应用中的显著成效。评…

数据结构--关键路径

事件v1-表示整个工程开始&#xff08;源点&#xff1a;入度为0的顶点&#xff09; 事件v9-表示整个工程结束&#xff08;汇点&#xff1a;出度为0的顶点&#xff09; 关键路径&#xff1a;路径长度最长的路径 求解关键路径问题&#xff08;AOE网&#xff09; 定义四个描述量 …

数青蛙 ---- 模拟

题目链接 题目: 分析: 题目的意思是: 一次蛙鸣是一个完整的字符串"croak", 给你一个字符串, 让你求出最少的青蛙数目 示例一: 两次完整的"croak", 可以由一只青蛙完成, 所以答案为1 示例二: 第一次蛙鸣还没有结束, 又出现了"c", 说明有第二只青…

迪丽热巴与大姐的璀璨友情

迪丽热巴与“大姐”的璀璨友情&#xff1a;星光熠熠&#xff0c;友谊长存在娱乐圈的繁华舞台上&#xff0c;有两位耀眼的女星&#xff0c;她们如同夜空中亮的两颗星&#xff0c;交相辉映&#xff0c;共同谱写着一段段动人的佳话。她们&#xff0c;一个是被亲切称为“迪迪”的迪…

03_初识Spring Cloud Gateway

文章目录 一、网关简介1.1 网关提出的背景1.2 网关在微服务中的位置1.3 网关的技术选型1.4 补充 二、Spring Cloud Gateway的简介2.1 核心概念&#xff1a;路由&#xff08;Route&#xff09;2.2 核心概念&#xff1a;断言&#xff08;Predicate&#xff09;2.3 核心概念&#…

【算法训练记录——Day24】

Day24——回溯算法Ⅰ 77.组合 今日内容&#xff1a; ● 理论基础 ● 77. 组合 理论&#xff1a;代码随想录 77.组合 思路&#xff1a;k层for循环&#xff0c;不会 回溯&#xff0c;将组合问题抽象成n叉树&#xff0c;for循环控制宽度&#xff0c;递归的深度控制二叉树的深度 …

云原生架构案例分析_3.某快递公司核心业务系统云原生改造

名称解释&#xff1a; 阿里云ACK&#xff1a;阿里云容器服务 Kubernetes 版 ACK&#xff08;Container Service for Kubernetes&#xff09;集成Kubernetes网络、阿里云VPC、阿里云SLB&#xff0c;提供稳定高性能的容器网络。本文介绍ACK集群网络及阿里云网络底层基础设施的重要…

抄袭瓜!斯坦福作者已删库跑路!面壁和刘知远老师的最新回应

好家伙&#xff0c;AI大模型圈爆料不断&#xff0c;没想到今天最猛的料还是抄袭的瓜啊。 5月29日&#xff0c;一个斯坦福团队高调宣称&#xff0c;只需要500美元&#xff0c;就可以通过他们的新产品&#xff1a;Llama3-v &#xff0c;就可以通过Llama3&#xff0c;训练出一个全…

第二十五章CSS中的技巧(导航栏、下拉列表)

1.CSS精灵 1.什么是CSS精灵 英文叫法 CSS sprites&#xff0c;通常被解释为“CSS图像拼合”或“CSS贴图定位”;其实就是把网页中一些背景图片整合到一张图片文件中&#xff0c;再利用css“background-image”&#xff0c; “background-repeat”,“background-position”的组…

C++命名空间(详解)

C基础语法 C基于C语言的改进&#xff1a;c在C语言的基础上引入并扩充了面向对象的概念 C基础概念&#xff1a;C是基于C语言而产生的,它即可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计 在1998年 出现C98…

文件同步软件,PanguFlow局域网横着走

说到文件同步&#xff0c;它对企业来说及其重要&#xff0c;因为有了文件同步&#xff0c;这个文件数据它才能备份起来&#xff0c;才能用来抵抗自然灾害&#xff0c;比如服务器硬盘损坏了&#xff0c;你要是不备份&#xff0c;这损失可就大了&#xff0c;一款合适的文件同步软…

外星人Alienware X15R1 原厂Windows10系统 oem系统

装后恢复到您开箱的体验界面&#xff0c;包括所有原机所有驱动AWCC、Mydell、office、mcafee等所有预装软件。 最适合您电脑的系统&#xff0c;经厂家手调试最佳状态&#xff0c;性能与功耗直接拉满&#xff0c;体验最原汁原味的系统。 原厂系统下载网址&#xff1a;http://w…

第三方模块的下载与安装

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在进行Python程序开发时&#xff0c;除了可以使用Python内置的标准模块外&#xff0c;还有很多第三方模块可以被我们所使用。对于这些第三方模块&…

欧佩克+同意集体性减产延长,油价能否稳住?

KlipC报道&#xff1a;欧佩克组织同意将延长目前部分减产协议至2025年&#xff0c;以支撑油价。主要成员国把2023年11月宣布的日均220万桶的自愿减产措施延长至今年9月底&#xff0c;将在10月份根据市场情况开始缩减自愿减产规模。 高盛分析师表示&#xff0c;“我们认为这次欧…