数据结构(九)排序

第九章 排序

9.1 基本概念
  1. 排序是线性表的一种操作
  2. 每个元素叫做记录,输入是一个元素集合,输出是一个元素集合
  3. 内排序吧整个数据ui放在内存中进行排序,外排序由于记录个数太多,不能全部放在内存中
  4. 7种排序算法分为两大类:
    (1)简单算法:冒泡排序,简单选择排序,直接插入排序
    (2)改进算法:希尔排序,堆排序,归并排序,快速排序
  5. 排序用到的数据结构
    (1)线性表
    (2)数组元素交换操作
    ```c
    #include <stdio.h>
    #define MAXSIZE 10
    typedef struct{
    int r[MAXSIZE+1] ; // r[0]用于存储哨兵或临时变量
    int length;
    }SqList;

    void swap(SqList l,int i,int j){
    int temp = l->r[i];
    l -> r[i] = l->r[j];
    l->r[j] = temp;
    }
    (3)数组打印c
    void printSqlList(SqList
    l){
    for (int i = 0; i < l-> length+1; i++) {
    printf("%d , ",l-> r[i]);
    }
    }
    ```

9.2 冒泡排序
  1. 冒泡排序的基本思想:
    两两比较相邻记录的关键字,如果反序则交换记录,直到没有反序为止。
  2. 算法实现:
    (1)冒泡排序每次循环都把一个最小的元素放到无序表的最顶端
    (2)这个比较要从线性表的最后一位开始,逐个比较,把小的交换到前面,不断比较到线性表的开头,此时,最小的元素被放到了最开始的位置。
    (3)当发现某一次循环中,未发生元素交换,就是说每个相邻元素,都是左小右大的,此时,线性表已经全具有序,不需要再次比较。引入flag比较为

    /* 冒泡排序 */
    void bubbleSort(SqList *list){
      int flag = 1;
       for (int i = 1; i < list->length  && flag ; i++) {    // 数组0位置不是元素
          flag = 0;
           for (int j = list->length-1; j >= i; j--) {
               if(list->r[j+1] < list->r[j]){
                   flag = 1;    // 产生交换操作,下一次还需要进行比较交换
                   swap(list,j,j+1);
               }
           }
       }
    }

    (4)冒泡排序的时间复杂度
    最好情况:线性表已经有序,需要比较n-1次
    最坏情况:比较次数(n-1) + (n-2) + (n-3)+ ... +3 + 2 + 1 = \(\frac{n(n-1)}{2}\)
    所以总时间复杂度:O(n^2)

9.3 简单选择排序
  1. 冒泡排序不停的进行比较和交换,确保局部为左小右大。而简单选择排序,是一种不断进行比较,却很少次数交换的排序算法
  2. 简单选择排序:选择无序列表中最小的元素,用一个min标记记录该元素位置。然后把该元素和无序列表的第一个元素交换
  3. 简单选择排序的时间复杂度
    所有的比较次数:最后一趟比较1次,倒数第二趟比较2次,所以一共比较1+2+3+..+(n-1)=\(\frac{n(n-1)}{2}\)
    最好的时候,交换次数为0,最坏的情况下,交换次数为O(n),所以比较次数+交换次数形成算法的时间复杂度O(\(n^2\)),

    /* 简单选择排序 */
    void simpleSelest(SqList *list){
       for (int i = 1; i < list->length; i++) {
           for (int j = i+1; j <= list->length; j++) {
               int min = i;
               if(list->r[min]>list->r[j])
                   min = j;
               if(min != i)
                   swap(list,i,min);
           }
       }
    }
9.4 直接插入排序
  1. 直接插入排序的思想
    (1)直接插入排序是把无序列表中的每个元素,插入到已经排好序的有序列表中
    (2)角标为0的位置,存放每次待插入的元素值。
  2. 时间复杂度
    直接插入排序的时间复杂度要好于冒泡排序和简单选择排序,他的平均比较和移动次数约为\(\frac{n^2}{4}\)
  3. 算法

    /* 直接插入排序 */
    void insertSort(SqList *list){
        for (int i = 2; i <= list->length ; i++) {  // 初始情况下,认为第一个数据是有序列表
            if(list->r[i] < list->r[i-1]){
                list->r[0] = list->r[i];    // 哨兵
                int j;
                for (j = i-1;  list->r[j] > list->r[0]  ; j--) {  // 从插入元素向前查,找到需要插入的位置
                    list->r[j+1] = list->r[j];
                }
                list->r[j+1] = list->r[0];
            }
            printSqlList(list);
            printf("\n");
        }
    }
9.5 希尔排序
  1. 冒泡排序,简单选择排序,直接插入排序一个比一个节省时间。但是都没有突破O(\(n^2\))的限制。长期以来,算法界甚至觉得 一切排序算法都无法超越O(\(n^2\))。
    直到希尔排序出现,把时间复杂度提高到O(n*logn)。之后出现了一批超越O(\(n^2\))的算法
  2. 直接插入排序在某些情况下的效率很高:比如记录本身基本有序,只需少量插入操作就可以完成排序。还有就是纪录很少,插入的次数也就不多。但是这两个条件很苛刻。所以希尔排序出现了
    。他的思路就是把无序列表,向着记录数少,基本有序靠拢。
  3. 希尔排序不是稳定的排序
  4. 希尔排序的思想:
    希尔排序是分组内的直接插入排序。每趟保证分组内有序,最后increment为1,进行一次全部的直接插入排序

    /* 希尔排序 */
    void shellSort(SqList *list){
        int increament = list->length ;
        do{
            increament = increament/3 + 1;     // 增量序列
            printf("increament is %d :",increament);
            for (int i = increament+1; i <= list->length ; i++) {
                if(list->r[i] < list->r[i-increament]){   //  初始时r[1]与r[increment]比较
                    list->r[0] = list->r[i];            // 暂存
                    int j = 0;
                    // 下面的形式就是直接插入排序的代码,但是希尔时分组的,所以比较后的移位移动increment位
                    for (j = i-increament; j>0 && list->r[0] < list->r[j] ; j-=increament) {
                        list->r[j+increament] = list->r[j];
                    }
                    list->r[j+increament] = list->r[0];
                }
            }
            printSqlList(list);
        }while (increament > 1);
    }
9.6 堆排序
  1. 概念
    (1)堆排序是对简单选择排序的一种改进,简单选择排序在待排序的n个记录中选择一个最小记录需要比较n-1次。可是这种操作没有把每一趟的比较结果保存。而堆排序,改进了这种情况
    (2)节点大于等于左右孩子结点的完全二叉树称为大顶堆
             节点小于等于左右孩子节点的完全二叉树称为小顶堆

  2. 堆排序的思想:
    (1)将待排序的序列形成大顶堆,使得根节点为待排数中最大的数
    (2)将这个堆顶根节点和堆数组末尾的元素交换,使得堆数组的末尾元素为最大值,然后再将前面的n-1个结点序列重新构成一个堆,这样就能得到次小值。如此反复,得到一个有序序列。
    ```c
    #include <stdio.h>
    #define MAXSIZE 20
    typedef int ElementType;
    typedef struct { // 定义线性表
    ElementType Data[MAXSIZE];
    int length;
    }SqList;

    int getElem(SqList list,int i,ElementType e){
    if(list.length ==0 || i <1 || i>list.length)
    return 0;
    e = list.Data[i];
    return 1; // 1代表成功
    }

    int insertList(SqList *list,int i, ElementType e){
    if (list->length == MAXSIZE || i<1 || i>list->length+1 )
    return 0;
    // 插入数据时将被查位置后的元素向后移1位,如果是在最后一位操作,则不用这个操作
    if(i <= list->length){
    for(int j=list->length-1;j>i-1;j--){
    list->Data[j+1] = list->Data[j];
    }
    }
    list->Data[i-1] = e; // 线性表角标=数组角标+1
    list->length++;
    return 0;
    }

    void HeapAdjust(SqList list,int s,int m){
    int temp ;
    temp = list->Data[s];
    printf("tmp is : %d\n",temp);
    for (int i = 2
    s; i <= m ; i = 2) {
    if(i<m && list->Data[i]Data[i+1])
    ++i; // i是关键字中较大的记录下标
    if(temp >= list->Data[i])
    break;
    list->Data[s] = list->Data[i];
    s = i;
    }
    list->Data[s] = temp;
    }
    /
    堆排序 /
    void heapSort(SqList
    list){
    int i;
    for (int i = list->length/2; i > 0 ; i--) {
    HeapAdjust(list,i,list->length);
    }
    for (int i = list->length; i > 1 ; i--) {
    int temp = list->Data[i];
    list->Data[i] = list->Data1;
    list->Data1 = temp;
    HeapAdjust(list,1,i-1);
    }
    }

    int main(){
    printf("start\n");
    SqList list = {{999,13,25,3,99,41},5};
    SqList *l = &list;
    heapSort(l);
    for (int i = 0; i < 6; ++i) {
    printf( "%d, ",list.Data[i]);
    }
    }
    ```

9.7 快速排序
  1. 对冒泡排序的改进
  2. 它每次选取一个枢轴,让枢轴左边的数都比枢轴小,枢轴右边的数都比枢轴大。
  3. 排序过程
    image_1av0lj2kq42q1rjge21pnludo9.png-138.7kB
  4. 代码实现

    #define MAXSIZE 10
    typedef struct{
        int r[MAXSIZE+1] ;   // r[0]用于存储哨兵或临时变量
        int length;
    }SqList;
    
    void qsort(SqList *L,int low ,int high);
    int partition(SqList *L,int low,int high);
    void quickSort(SqList *L){
        qsort(L,1,L->length);
    }
    void qsort(SqList *L,int low ,int high){            /* 递归调用,对枢轴左边和右边分别递归 */
        int pivot;                                      /* 枢轴 */
        if(low < high){
            pivot = partition(L,low,high);              /* 调整枢轴到正确的位置 */
            qsort(L,low,pivot-1);
            qsort(L,pivot+1,high);
        }
    }
    int partition(SqList *L,int low,int high){
        printf("low:%d,high:%d\n",low,high);
        int pivotkey;
        pivotkey = L->r[low];
        while(low<high){
            while(low<high && L->r[high] >= pivotkey)    /* 找到右侧比pivot数小的 */
                high--;
            swap(L,low,high);
    
            while(low<high && L->r[low] <= pivotkey)      /* 找到左侧比pivot数大的 */
                low++;
            swap(L,low,high);
        }
        return low;                                       /* 跳出while时,low=high=枢轴位置 */
    }
    /*********
     测试代码
     *********/
    int main(){
        SqList list = {{-100,13,25,3,12,8,67,99,41,33,24},10};
        SqList *l = &list;
        quickSort(l);
        for (int i = 1; i < 10; ++i) {
            printf( "%d, ",list.r[i]);
        }
    }

转载于:https://www.cnblogs.com/moonlord/p/5938064.html

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值