当前位置: 首页 > news >正文

数据结构基础内容(第十篇:排序)

# 排序

## 冒泡排序

稳定!  

最好情况:顺序 T = O(N)  

最坏情况:逆序 T = O(N^2)  

```c

void Swap(ElementType *a, ElementType *b){

    ElementType c;

    c = *a;

    *a = *b;

    *b = c;

}

void Bubble_Sort(ElementType A[], int N){

    int P, i, flag;

    for (P=N-1; P>0; P--){  // 最后一位下标是N-1

        flag = 0;

        for (i=0; i<P; i++){

            if(A[i] > A[i+1]){

                Swap(&A[i], &A[i+1]);

                flag = 1;  // 标识发生了交换

            }

        }

        if (flag==0)

            break; // 若全程无交换

    }

}

```

## 插入排序

稳定!

最好情况:顺序 T = O(N)  

最坏情况:逆序 T = O(N^2)  

时间复杂度和逆序对有关 T(N,I) = O(N+I)  

定理: 任意N个不同元素组成的序列平均具有 N ( N -1 ) / 4 个逆序对。    

简单排序(交换相邻两个数)的时间复杂度下界: Omega(N^2)  

```c

void Insertion_Sort(ElementType A[], int N){

    ElementType Tmp;

    int P, i;

    for(P=1; P<N; P++){

        Tmp = A[P]; // 摸下一张牌

        for (i = P; i>0; i--)

        {

            if(A[i-1] > Tmp) // 若前一张牌比新摸的牌大

                A[i] = A[i-1]; // 移出空位

            else break;  // 若新摸的牌比前一张牌大,则跳出循环

        }

        A[i] = Tmp; // 新牌落位

    }

}

```

## 希尔排序  

不稳定!

定义增量序列,如Sedgewick增量序列{1,5,19,41,109...}  

对每个D进行D间隔的排序  

```c

void Shell_Sort(ElementType A[], int N){

    int D, P,i;

    ElementType Tmp;

    for (D=N/2; D>0; D/=2){ // 希尔排序增量

        for ( P=D; P<N; P++){  // 插入排序

            Tmp = A[P];

            for (i = P; i-D>=0; i-=D)

            {

                if(A[i-D] > Tmp)

                    A[i] = A[i-D]; // 移出空位

                else break;

            }

            A[i] = Tmp; // 新牌落位

        }

    }

}

```

## 堆排序

不稳定!

选择排序:

```c

void Selection_Sort ( ElementType A[], int N )

{

    for(i=0;i<N;i++){

        MinPosition = ScanForMin( A, i, N–1 );

        Swap( A[i], A[MinPosition] );

    }

}

```

重要的是如何找到最小元.

堆排序处理N个不同元素的 随机排列的平均比较次数是2NlogN - O(NloglogN)

用最大堆,交换根节点和最后一个结点,让最大的数到最后,然后减小堆的规模

```c

void PercDown(ElementType A[] , int p, int N)

{// 下滤函数, 将Minheap中以H->Data[p]为根的子堆调整为最小堆

    int parent, child;

    ElementType X;

    X = A[p]; // 取出根节点的值

    for(parent =  p; parent*2+1 <= N-1 ; parent = child)

    {

        child = parent*2+1;

        if( (child != N-1 ) && (A[child] < A[child+1]))

        {

            child++;

        }

        if (X >= A[child])

            break;

        else // 将X下滤

            A[parent] = A[child];

    }

    A[parent] = X;

}

void Heap_Sort(ElementType A[], int N){

    int i;

    for (i=N/2; i>=0; i--)  // Build MaxHeap

        PercDown(A, i, N);

    for (i=N-1; i>0; i--){

        Swap(&A[0], &A[i]);  // DeleteMax

        PercDown(A, 0, i);  // 重新整理成最大堆,堆的size为i

    }

}

```

## 归并排序

稳定!

有序子列的归并

```c

void Merge(ElementType A[], ElementType TmpA[],

    int L, int R, int RightEnd)

{

    int LeftEnd, Num, Tmp;

    int i;

    LeftEnd = R - 1;

    Tmp = L;  // 存放结果的数组的初始位置

    Num = RightEnd - L + 1; // 一共有几个数据

    while (L <= LeftEnd && R <= RightEnd){  // 左和右都不为空

        if (A[L] <= A[R])

            TmpA[Tmp++] = A[L++];

        else

            TmpA[Tmp++] = A[R++];

    }

    // 跳出循环时左边还有剩下的

    while (L <= LeftEnd)

        TmpA[Tmp++] = A[L++];

    // 跳出循环时右边还有剩下的

    while (R <= RightEnd)

        TmpA[Tmp++] = A[R++];

    // 把TmpA倒回到A, 从RightEnd倒着复制回去

    for(i=0; i<Num; i++){  // 重复Num次

        A[RightEnd] = TmpA[RightEnd];

        RightEnd--;

    }

}

```

递归算法:  

```c

void MSort(ElementType A[], ElementType TmpA[], int L, int RightEnd)

{

    int Center;

    if(L < RightEnd){  // L和RightEnd相等时,就只有一个元素了,不能再分

        Center = (L+RightEnd)/2;

        // 分

        MSort(A, TmpA, L, Center);

        MSort(A, TmpA, Center+1, RightEnd);

        // 合

        Merge(A, TmpA, L, Center+1, RightEnd);

    }

}

// 同一接口

void Merge_Sort(ElementType A[], int N){

    ElementType *TmpA;  // 临时数组

    TmpA = malloc(N * sizeof(ElementType));

    if (TmpA != NULL){  // 分配空间成功

        MSort(A, TmpA, 0, N-1);

        free(TmpA);

    }

    else

        printf("空间不足\n");

}

```

T(N) = T(N/2) + T(N/2) + O(N)  

T(N) = O(NlogN)  

非递归算法

```c

void Merge_Pass(ElementType A[], ElementType TmpA[], int N, int length)  // lenght = 当前有序子列的长度, 一开始等于1

{

    int i,j;

    for (i=0; i <= N-2*length; i+=2*length) // 留两个length,分情况讨论

        Merge(A, TmpA, i, i+length, i+2*length-1); // A TmpA L R RightEnd

    if (i+length < N) // 归并最后两个子列

        Merge(A, TmpA, i, i+length, N-1);

    else  // 最后只剩一个子列, 则复制过来

        for (j=i; j<N; j++)

            TmpA[j] = A[j];

}

void Merge_sort(ElementType A[], int N){

    int length = 1;  // 初始化子序列的长度

    ElementType *TmpA;  // 临时数组

    TmpA = malloc(N * sizeof(ElementType));

    if (TmpA != NULL){  // 分配空间成功

        while (length < N){  // 有序子列长度等于N,则完成,跳出循环

            Merge_Pass(A, TmpA, N, length);

            length *= 2;

            Merge_Pass(TmpA, A, N, length);

            length *= 2;

        }

        free(TmpA);

    }

    else

        printf("空间不足\n");

}

```

归并排序是稳定的

## 快速排序

不稳定

主元一次性放到正确的位置上  

主元左边的都比主元小,右边的都比主元大  

```c

// 选主元 pivot

ElementType Median3(ElementType A[], int Left, int Right){

    int Center = (Left + Right) / 2;

    // 整理成 A[Left] <= A[Center] <= A[Right]

    if (A[Left] > A[Center])

        Swap(&A[Left], &A[Right]);

    if (A[Left] > A[Right])

        Swap(&A[Left], &A[Right]);

    if (A[Center] > A[Right])

        Swap(&A[Center], &A[Right]);

    // 将基准Pivot藏到右边Right-1处, 则之后只需考虑 A[Left+1] -> A[Right-2]

    Swap(&A[Center], &A[Right-1]);

    return A[Right-1];

}

void Qsort(ElementType A[], int Left, int Right){

    int Pivot, Cutoff, Low, High;

    // 如果序列元素足够多,则进入快排

    Cutoff = 4;

    if (Cutoff <= Right - Left){

        Pivot = Median3(A, Left, Right);

        Low = Left;

        High = Right-1;

        while(1){

            while(A[++Low] < Pivot);  // 一直循环,直到A[Low] >= Pivot, 停住

            while(A[--High] > Pivot);  // 一直循环,直到A[Hight] <= Pivot, 停住

            if (Low<High)

                Swap(&A[Low], &A[High]);

            else // Low > High low已经越过了high,子序列排序结束

                break;

        }

        Swap(&A[Low], &A[Right-1]);  // 将Pivot归位,此时 Low>High, 所以Pivot和Low交换

        Qsort(A, Left, Low-1);   // 递归Pivot左边

        Qsort(A, Low+1, Right);    // 递归Pivot右边

    }

    else // 元素太少,用简单排序

        Insertion_Sort(A+Left, Right-Left+1);  // 待排序列表A从Left开始,即A+Left, 长度是R-L+1

}

void Quick_Sort(ElementType A[], int N){

    Qsort(A, 0 ,N-1);

}

```

## 选择排序

不稳定

```c

#define MaxDigit 3  // 假设最大为三位数

#define Radix 10  // 十进制

/* 桶元素结点 */

typedef struct Node *PtrNode;

struct Node

{

    int key;

    PtrNode next;

};

/* 桶头结点 */

struct HeadNode

{

    PtrNode head;

    PtrNode tail;

};

typedef struct HeadNode Bucket[Radix];


/* 返回整型关键字X的第D位数字 */

int GetDigit(int X, int D){

    // 默认次位D=1, 主位 D<= MaxDigit

    int d, i;

    for(i=1; i<=D; i++){

        d = X % Radix;

        X /= Radix;

    }

    return d;

}


// 次位优先(Least Significant Digit)排序

void LSDRadix_Sort(ElementType A[], int N){

    int D, Di, i;

    Bucket B;

    PtrNode tmp, p, List=NULL;

    // 初始化每个桶为空链表

    for(i=0; i<Radix; i++)

        B[i].head = B[i].tail = NULL;

    // 将原始序列A[], 逆序(插在链表头)存入链表List

    for(i=0; i<N; i++){

        tmp = (PtrNode)malloc(sizeof(struct Node));

        tmp->key = A[i];

        tmp->next = List;

        List = tmp;

    }

    // 开始排序

    for (D=1; D<= MaxDigit; D++){  // 对数据的每一位循环处理

        p=List;

        while(p)

        {

            Di = GetDigit(p->key, D);  // 获得当前元素的第D位数字

            // 从链表P中删除结点

            tmp = p;

            p = p->next;

            tmp->next = NULL;

            // 把结点插入到对应的B[Di]桶里

            if (B[Di].head == NULL)

                B[Di].head = B[Di].tail = tmp;

            else{

                B[Di].tail->next = tmp; // 把tmp加在尾部

                B[Di].tail = tmp;  // 尾部指向最后一个结点,即tmp

            }

        }  // while循环结束, 以第D位的分配完毕

        // D位数排完以后开始收集

        List = NULL;

        for(Di=Radix-1; Di >= 0; Di--)  // 将诶个桶的元素顺序收入List, 因为每次插入到List的头部,所以要从9开始收

        {

            if (B[Di].head){

                B[Di].tail->next = List;

                List = B[Di].head;

                B[Di].head = B[Di].tail = NULL;  // 清空桶

            }

        }

    }// 大for循环结束

    // 将List[]导入A[], 并释放空间

    for(i=0; i<N; i++){

        tmp = List;

        List = List->next;

        A[i] = tmp->key;

        free(tmp);

    }

}

```

时间复杂度 T = O(P(N+B))

当B较小时,基本是线性的.

http://www.lryc.cn/news/601324.html

相关文章:

  • 力扣129. 求根节点到叶节点数字之和
  • 力扣热题100----------53最大子数组和
  • 【多模态】天池AFAC赛道四-智能体赋能的金融多模态报告自动化生成part2-报告输出
  • logstash采集springboot微服务日志
  • Spring经典“送命题”:BeanFactory vs FactoryBean
  • 力扣131:分割回文串
  • JavaScript单线程实现异步
  • 探秘CommonJS:Node.js模块化核心解析
  • GPT-4o实战应用指南:从入门到精通的技术心得
  • 物联网安装调试-物联网网关
  • 【图像处理基石】Segment Anything Model (SAM) 调研
  • MGRE综合实验
  • 望言OCR视频字幕提取2025终极评测:免费版VS专业版提全方位对比(含免费下载)
  • 20250707-2-Kubernetes 网络-Ingress暴露应用(http与https)_笔记
  • Flutter中实现页面跳转功能
  • iOS安全和逆向系列教程 第21篇:iOS应用加密与混淆技术深度剖析
  • macOS配置 GO语言环境
  • mac电脑安装docker图文教程
  • 智慧施工:施工流程可视化管理系统
  • 【秋招笔试】7月26日科大讯飞秋招第二题
  • 算法竞赛阶段二-数据结构(37)数据结构动态链表list
  • DDPM:重新定义图像生成的革命性技术
  • Ubuntu Linux 如何配置虚拟内存 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录8
  • RabbiteMQ安装-ubuntu
  • Android CameraX 使用指南:简化相机开发
  • Keepalived + LVS-DR 高可用与负载均衡实验
  • ubuntu 部署 coze-loop
  • [10月考试] F
  • Java 后端 Cookie Session Token会话跟踪技术
  • LabelMe数据标注软件介绍和下载