首页 > 其他分享 >排序 - 插入 & 交换 & 选择 & 堆 & 基数

排序 - 插入 & 交换 & 选择 & 堆 & 基数

时间:2024-05-11 17:52:07浏览次数:15  
标签:复杂度 high 插入 算法 SqList 排序 基数 void

插入排序

直接插入排序

算法描述

将一条记录插入到有序表中,得到新的有序表。
将需要调整位置的元素暂存在r[0]处,然后再进行插入。

算法实现

void InsertSort(SqList &L) {
    for(i = 2; i <= L.length; i++)
        if(L.r[i].key < L.r[i - 1].key) {
            L.r[0] = L.r[i]; L.r[i] = L.r[i - 1];
            for(j = i - 2; L.r[0].key < L.r[j].key; j--) 
                L.r[j + 1] = L.r[j]; // 逐个后移 找到插入位置
            L.r[j + 1] = L.r[0];
        }
}

算法分析

  1. 时间复杂度
    关键字比较次数KCN + 记录移动次数RMN = O(n²)
  2. 空间复杂度
    只需要一个辅助空间r[0],空间复杂度是O(1)

算法特点

  1. 稳定排序。
  2. 也适用于链式存储。
  3. 适用于初始记录基本有序的情况。

折半插入排序

算法描述

将直接插入排序的查找方式改为折半排序,提高平均性能。

算法实现

void BInsertSort(SqList &L) {
    for(i = 2; i <= L.length; i++) {
        L.r[0] = L.r[i];
        low = 1; high = i - 1;
        while(low <= high) {
            m = (low + high) / 2;
            if(L.r[0].key < L.r[m].key) high = m - 1;
            else low = m + 1;
        }
        for(j = i - 1; j >= high + 1; j--)
            L.r[j + 1] = L.r[j];
        L.r[high + 1] = L.r[0];
    }
}

算法分析

  1. 时间复杂度
    折半插入的查找比直接插入快,但无法减少移动次数。
    插入第i个记录时需要经过floor(log2(i) + 1)次比较。
    关键字比较次数KCN + 记录移动次数RMN = O(n²)
  2. 空间复杂度
    只需要一个辅助空间r[0],空间复杂度是O(1)

算法特点

  1. 稳定排序。
  2. 不适用于链式存储。
  3. 适用于初始记录无序、n较大的情况。

希尔排序

算法描述

本质上是一种分组插入
事先确定一个增量数组,每次取出一个增量d,把所有间隔为d的元素分成一组,全部元素共分成d组,在每个组中分别进行直接插入排序。
直接插入排序是一种增量为1的希尔排序。

算法实现

void ShellInsert(SqList &L, int dk) {
    for(i = dk + 1; i <= L.length; i++)
        if(L.r[i].key < L.r[i - dk].key) {
            L.r[0] = L.r[i];
            for(j = i - dk; j > 0 && L.r[0].key < L.r[j].key; j -= dk)
                L.r[j + dk] = L.r[j]; // 找到插入位置
            L.r[j + dk] = L.r[0];
        }
}

void ShellSort(SqList &L, int dt[], int t) {
    for(k = 0; k < t; k++)
        ShellInsert(L, dt[k]); // 增量为dt[t]
}

算法分析

  1. 时间复杂度
    低于直接插入排序,具体难以计算,但n取向无穷时,时间复杂度为n(log2(n))²
  2. 空间复杂度
    只需要一个辅助空间r[0],空间复杂度是O(1)

算法特点

  1. 不稳定排序。
  2. 不适用于链式存储。
  3. 增量值互质,必须递减,最后一个增量值必须为1.
  4. 适用于初始记录无序、n较大的情况。

交换排序

冒泡排序

算法描述

每一趟比较过程中,将第一个与第二个、第二个与第三个、……第n-1个与第n个分别比较,逆序就交换。如果某一趟过程中没有发生交换,则排序完毕。

算法实现

void BubbleSort(SqList &L) {
    m = L.length - 1; flag = 1;
    while((m > 0) && (flag == 1)) {
        flag = 0;
        for(j = 1; j <= m; j++) 
            if(L.r[j].key > L.r[j + 1].key) {
                flag = 1;
                t = L.r[j]; L.r[j] = L.r[j + 1]; L.r[j + 1] = t;
            }
        m--;
    }
}

算法分析

  1. 时间复杂度
    最好(初始正序):一趟 n-1次关键字比较 不移动
    最坏(初始逆序):n-1趟
    平均时间复杂度O(n²)
  2. 空间复杂度
    交换过程中需要一个暂存辅助,复杂度为O(1)

算法特点

  1. 稳定排序。
  2. 可以用于链式结构。
  3. 初始无序且元素较多时效率低。

快速排序

算法描述

任意(一般是第一个)取一个元素作为基准(数值大小意义上的基准),设置左右两个指针,初始时指向表的下界和上界。右指针从右往左搜索,找到第一个比基准小的元素,移到左指针指向的位置;然后左指针从左往右搜索,找到第一个比基准大的元素,移到右指针指向的位置;一直重复,直到左右指针指向同一位置。将基准放在该位置,将序列分为左右两个序列。
整体来看就是基准点把待排序序列分成左右两个子表,然后对于任何一个表都有一个基准点和左右子表,所有小于基准点的都放在他左边,大于基准点的都放在他右边。

算法实现

int Partition(SqList &L, int low, int high) {
    L.r[0] = L.r[low]; // 基准点移动
    pivotkey = L.r[low].key;
    while(low < high) {
        while(low < high && L.[high].key >= pivotkey) high--;
        L.r[low] = L.r[high];
        while(low < high && L.r[low].key <= pivotkey) low++;
        L.r[high] = L.r[low];
    } // 一次排序
    L.r[low] = L.r[0]; // 基准点复位
    return low;
}

void QSort(SqList &L, int low, int high) {
    if(low < high) {
        pivotloc = Partition(L, low, high);
        QSort(L, low, pivotloc - 1);
        QSort(L, pivtloc + 1, high); // 左右子表分别递归
    }
}

void QuickSort(SqList &L) {
    QSort(L, 1, L.length);
}

算法分析

  1. 时间复杂度
    最好(初始正序):一趟 n-1次关键字比较 不移动
    最坏(初始已经排号):递归树为单支树,需要n-1趟
    平均时间复杂度O(nlog2(n))
    *避免最坏情况出现的方法“三者取中”:比较表中第一个、中间一个、最后一个关键字,事先把处于中间位置的换到第一个,然后再开始排序。通过这种方式防止递归树成为单支树。
  2. 空间复杂度
    最大递归调用次数等于递归树的深度。
    复杂度最好为O(log2(n)),最坏为O(n)

算法特点

  1. 不稳定排序。
  2. 不适用于链式结构。
  3. 平均情况下是内部排序中最快的(以空间换时间),适用于初始记录无序、n较大。

如何判断某个序列(不知原序列情况下)可不可能是第i趟快速排序后的结果?

快排每一趟都会将那一趟的基准放到其最终位置,这个位置满足:前面所有都比基准小,后面所有都比基准大。如果是第i趟,则应该能找到(至少)i个元素已经到达对应位置。

选择排序

简单选择排序

算法描述

n-1次遍历,每次选出一个未排序区域中的最小元素放入已排序区域中的合适位置。

算法实现

void SelectSort(SqList &L) {
    for(i = 1; i < L.length; i++) {
        k = i;
        for(j = i + 1; j <= L.length; j++)
            if(L.r[j].key < L.r[k].key) k = j;
        if(k != i) {t = L.r[i]; L.r[i] = L.r[k]; L.r[k] = t;}
    }
}

算法分析

  1. 时间复杂度
    最好情况(初始正序):不移动
    最坏情况(初始逆序):移动3(n-1)次
    时间复杂度是O(n²)
  2. 空间复杂度
    交换元素时需要一个辅助空间,空间复杂度是O(1)

算法特点

  1. 稳定排序。
  2. 适用于链式存储结构。
  3. 移动次数较少。当每个记录占空间较大时,比直接插入排序快。

树形选择排序

算法描述

用有n个结点的完全二叉树表示,其中每个非终端结点的值是他的左右孩子的最小值,所以根的值是待排序列中的最小值。找到最小值之后,将最小值对应的叶子的值改为∞,对于该叶子到根的路径上的所有值进行过再次比较更新,找出次小值。

算法分析

  1. 时间复杂度
    除了最小值,选择其他关键字时只需要比较log2(n)次,时间复杂度为O(nlog2(n)).
  2. 所需存储空间多,存在与已被选出的叶子节点的多余比较。

堆排序

堆是一种满足所有非终端结点的值均不大于(小根堆)/小于(大根堆)其左右孩子结点的值的完全二叉树。其根必为最小/大值。

堆排序

算法描述

建立在完全二叉树的顺序存储基础上。先将待排序序列建成一个小根堆,交换根和最后一个值,即现在最后一个位置是最小值。然后从1到n-1个元素再次建立小根堆,交换第一个和最后一个值。也就是说每一次堆化再交换一次之后,能确定的就是排在序列后面部分的最大值。不断重复,直到交换了第一个和第二个值为止。

算法实现

  1. 筛选法调整堆
    从根到叶地进行调整,直到整个树再次符合堆的性质。
void HeapAdjust(SqList &L, int s, int m) {
    rc = L.r[s];
    for(j = 2 * s; j <= m; j *= 2) {
        if(j < m && L.r[i].key < L.r[j + 1].key) j++;
        if(rc.key >= L.r[j].key) break;
        L.r[s] = L.r[j]; s = j;
    }
    L.r[s] = rc;
}
  1. 建初堆
    只有一个结点的树一定是堆。完全二叉树中所有序号大于floor(n/2)的结点都是叶子,所以他们都是堆。所以只需要从最后一个非叶子节点floor(n/2)开始依次将以其及其前面序号的结点为根的子树调整为堆。
void CreateHeap(SqList &L) {
    n = L.length;
    for(i = n / 2; i > 0; i--) 
        HeapAdjust(L, i, n);
}
  1. 堆排序
    建初堆,然后反复进行堆顶堆底的交换和堆调整。
void HeapSort(SqList &L) {
    CreateHeap(L);
    for(i = L.length; i > 1; i--) {
        x = L.r[1]; L.r[1] = L.r[i]; L.r[i] = x;
        HeapAdjust(L, 1, i - 1);
    }
}

算法分析

  1. 时间复杂度
    平均时间复杂度接近于最坏性能O(nlog2(n))
  2. 空间复杂度
    需要一个交换辅助空间,复杂度为O(1)

算法特点

  1. 不稳定排序。
  2. 不适用于链式结构。
  3. 建初堆成本较大,不适用于元素较少的情况,元素较多时比较高效。

基数排序

链式基数排序

算法描述

采用最低位优先法LSD思想,借助分配和收集两种操作进行排序。
例如需要对一些三位正整数从小到大排序。首先比较个位,个位相同的放在同一队列中(类似桶排),此时队列内部无序,队列间按照个位从小到大排序,相对有序。然后按照十位、百位重复以上操作,比较完百位则完成排序。

算法分析

  1. 时间复杂度
    对于n个记录,每个记录有d个关键字,每个关键字取值为rd:每一趟分配的时间复杂度为O(n),每一趟收集为O(rd),共进行d次分配和收集操作,所以时间复杂度为O(d(n+rd))
  2. 空间复杂度
    队列和链表指针域,空间复杂度为O(n+rd)

算法特点

  1. 稳定排序
  2. 适用于链式结构
  3. 需要知道各关键字的主次关系和各关键字取值范围

标签:复杂度,high,插入,算法,SqList,排序,基数,void
From: https://www.cnblogs.com/ww0809/p/18186950

相关文章

  • 排序
    冒泡排序【题目描述】编程输入n(1≤n≤20)个小于1000非负整数,然后自动按从大到小的顺序输出。(冒泡排序)【输入】第一行,数的个数n;第二行,n个非负整数。【输出】由大到小的n个非负整数,每个数占一行。【输入样例】5258612【输出样例】128652n=int(inp......
  • 选择排序桌面检查
    码云代码:https://gitee.com/yibo886/codes/0ihau3t9pj6bkfqzxcmnl93博客园:一、实验题目:代码审查二、实验目的1、熟悉编码风格,利用开发环境所提供的平台工具对代码进行自动格式审查;2、根据代码规范制定代码走查表,并按所制定的审查规范互审代码。三、实验内容1、IDEA环境和P......
  • Mysql 查询后进行插入
    Mysql查询后进行插入,具体要求如下:1、有2张表,sys_role_user和sys_role_user_123,两张表结构相同,表字段有role_id、user_id2、role_id和user_id是唯一索引3、把sys_role_user中没有的数据从sys_role_user_123中复制到sys_role_user表中 INSERTINTOsys_role_user(role_i......
  • 04 总结三傻排序
    我的总结:插入排序:扑克牌,右侧往左侧挪,右侧无序变到左侧有序。冒泡排序:两两比较,大的往右侧挪。像水冒泡一样。选择排序:遍历一遍,选择最小的,每次挪最小的,放到左侧。形成有序。   再看一眼动图:插入排序:    冒泡排序:     选择(最小的)排序: 参考资料:......
  • 03 插入排序
    1.插入排序的含义类似扑克牌,假设认为0-0位置有序,再把0-1的位置变有序,循环直到所有的有序。每次拿取右侧的数字,一个一个对比放到左侧来。2.示例代码definsertion_sort(arr):ifarrisNoneorlen(arr)<2:returnforiinrange(1,len(arr)):#......
  • RR级别-多线程环境下-for update+插入操作包含的间隙锁+插入意向锁引发的死锁问题
    记录selectforupdatemysql死锁问题_执行select...where...forupdate是否会造成死锁(deadlock)-CSDN博客......
  • 33. 搜索旋转排序数组
    整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k],nums[k+1],...,nums[n-1],nums[0],nums[1],...,nums[k-1]](下标从0开始计数)。例如,[0,1,2,4,5,6,7]在下标3处经......
  • 153. 寻找旋转排序数组中的最小值
    已知一个长度为n的数组,预先按照升序排列,经由1到n次旋转后,得到输入数组。例如,原数组nums=[0,1,2,4,5,6,7]在变化后可能得到:若旋转4次,则可以得到[4,5,6,7,0,1,2]若旋转7次,则可以得到[0,1,2,4,5,6,7]注意,数组[a[0],a[1],a[2],...,a[n-1]]旋转一次的结果......
  • [笔记]拓扑排序
    对于一个有向无环图(DAG)的顶点按顺序排成一个序列的过程,就是拓扑排序(TopologicalSort)。具体来说,这个序列必须满足:每个顶点正好出现\(1\)次。如果图上存在一条\(A\toB\)的路径,那么\(A\)一定在\(B\)之前。注意:拓扑排序结果可能不唯一。具体做法就是每次在图中寻找\(1\)个入......
  • 冒泡排序、插入排序、选择排序
    冒泡排序思想:从左到右,元素交换。第一个元素和第二个元素比较,若第一个元素大于第二个,则交换元素,再第二个元素与第三个元素比较,依次比较,直到比较完。则最尾部的元素是最大值。voidmaopao(inta[5],intsi){for(inti=0;i<si-1;i++){for(intj=0;......