首页 > 其他分享 >数据结构(第八章)

数据结构(第八章)

时间:2023-07-14 18:55:41浏览次数:58  
标签:排序 int 复杂度 元素 第八章 序列 数据结构 插入排序

数据结构(第八章)

排序

一、插入排序

1.1、直接插入排序

  • 直接插入排序的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。

代码展示:

#define MaxSize 100
//定义一个顺序表结构
typedef struct {
    int data[MaxSize+1]; //定义一个排序数组,data[0]位用作哨兵或临时变量
    int length; // 用来记录顺序表的长度
}SqList;
//直接插入排序
void InsertSort( SqList &L ){
         int i ,j;
    for (i = 2; i <=L.length ; ++i) { //从二号位开始,0号位为哨兵 , 1号位当初始的时候默认为有序
        if (L.data[i]<L.data[i-1]) //按照升序的排列方式排序
        {
            L.data[0]=L.data[i]; //将小的这个元素,放到哨兵位置临时存储
            for (j = i-1; L.data[j]>L.data[0] ; --j)  //从后往前查找待插入的位置
                    L.data[j+1]=L.data[j]; //将元素依次向后移动
            L.data[j+1]=L.data[0]; //将哨兵处的元素插入到正确的位置
        }
    }
}
  • 时间复杂度:O(n^2)
  • 稳定性:由于每次插入元素时,总是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个比较稳定的算法。
  • 适用性:顺序和链式存储的线性表。

1.2、折半插入排序

  • 从直接插入排序算法中,不难看出每趟插入的过程中都进行了两项工作:①从前面的有序子表中查找出待插入元素应该被插入的位置;②给插入位置腾出空间,将待插入元素复制到表中的插入位置。注意到在该算法中,总是边比较边移动元素。下面将比较和移动操作分离,即先折半查找出元素的待插入位置,然后统一地移动待插入位置之后的所有元素即折半插入排序。

代码展示:

//折半插入排序
void BinaryInsertSort(int A[] , int n){ //A表示待排数组,n表示数组中元素个数
    int i , j ,low ,high ,mid;
    for ( i = 2; i <=n ; ++i) { //依次将A[2]~A[n]的元素排序插入到数组中
        A[0]=A[i]; //将带排元素存入到哨兵中
        low=1;
        high=i-1; //设置折半查找的范围
        while(low<=high) //折半查找,默认递增序列
        {
            mid=(low+high)/2; //取中间值
            if (A[0]>mid)
                low=mid+1;
            else
                high=mid-1;
        }
        for ( j = i-1; j>=high+1 ; j) //统一后移元素
            A[j+1]=A[j];
        A[j+1]=A[0]; //将元素插入到正确位置
    }
}
  • 时间复杂度:O(n^2)
  • 稳定性:稳定的算法

1.3、希尔排序

  • 希尔排序又称缩小增量排序,其思想是将原本有大量记录数的记录进行分组。分割成若干子序列,此时每个子序列待排序的记录个数就比较少,然后在这些子序列中分别进行直接插入排序,当整个序列基本有序时,再对全体进行一次直接插入排序。
  • 基本有序:就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间。

代码展示:

//希尔排序
//对顺序表 L 进行一趟希尔排序, 增量为 d
void ShellSort(int L[],int n,int d)
{
    for(int j=d+1;j<=n;j++)
    {
        L[0]=L[j];//用L[0]来存储我们待排序的数据,此时的L[0]只用作存储临时元素,不用做哨兵
        int k=j-d; //前一位元素的下标
        while(k>=0&&L[0]<L[k])//从下标为0开始存储
        {
            L[k+d]=L[k];
            k=k-d;//依次移动d个位置
        }
        L[k+d]=L[0];
    }
}
  • 时间复杂度: O(n^3/2)

二、交换排序

2.1、冒泡排序

  • 冒泡排序:从后往前(从前往后)两两比较相邻元素的值,若为逆序(A[i]>A[i-1])则进行交换,直到没有逆序的记录为止。

代码展示:

//冒泡排序
void BubbleSort( int A[] , int n){
    bool flag; //用于记录本次冒泡是否发生过交换
    for (int i = 0; i < n - 1; ++i) {
        flag= false;
        for (int j = n-1; j >i; j--) { //一趟冒泡的过程 ,从后往前遍历
            if (A[j-1]>A[j]) //若为逆序则进行交换
            {
                swap(A[j-1],A[j]); //交换
                flag=true;
            }
            if (flag== false)
                return ; // 如果遍历后没有发生交换,说明已经有序
        }
    }
}
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定的算法

2.2、快速排序

  • 快速排序的基本思想:通过一趟排序将待记录分割成独立的两部分,其中一部分记录关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

代码展示:

//快速排序
//1. 对表中元素进行划分
int Partition(int A[] , int low , int high){
    int pivot=A[low]; //将当前表中的第一个元素设置为枢轴,对表中元素进行划分
    while(low<high){ //循环跳出条件
        while(low<high&&A[high]>=pivot)
            --high;
        A[low]=A[high]; //将比枢轴小的元素移动到左端
        while(low<high&&A[low]<=pivot)
            ++low;
        A[high]=A[low];//将比枢轴大的元素移动到左端
    }
    A[low]=pivot; //将枢轴元素插入到特定位置,即low==high的位置处
    return low; //返回枢轴的最终位置
}
void QuickSort(int A[] ,int low , int high){
    if (low<high){ //跳出递归的条件
        int pivotpos= Partition(A,low,high);//将数组划分成满足条件的两个子表
        QuickSort(A,low,pivotpos-1); //依次对两个子表进行递归调用
        QuickSort(A,pivotpos+1,high);
    }
}
  • 时间复杂度:O(nlogn)
  • 空间复杂度:Q(logn)(平均)
  • 稳定性:不稳定

三、选择排序

3.1、简单选择排序
  • 简单选择排序思想:每一趟从总选出关键字最小的元素,作为有序子序列的第i个元素(i=1,2,···),依次循环选择。

代码展示:

//简单选择排序
void SelectSort(int A[],int n){
    int i ,j;
    int min; //用于记录最小元素的位置
    for ( i = 0; i <n-1 ; ++i) { //一共进行n-1次
        min=i;
        for ( j = i+1; j <n ; ++j)//选择其中最小的元素
            if (A[j]<A[min])
                min=j; //交换最小元素的位置
        if (min!=i) //如果最小元素的位置不等,则交换位置
            swap(A[i],A[min]);
    }
}
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
3.2、堆排序
  • 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大根堆;每个结点的值都小于或等于其左右孩子结点的值称为小根堆。
  • 堆排序的基本思想:堆排序就是利用堆(假设利用大顶堆)进行排序的方法。将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值。如此反复执行,便能得到一个有序序列了。

代码展示:

//堆排序
//采用大根堆的方式进行堆排序
void HeapSort(int A[] , int len){
    //构建大根堆
    for (int i = len/2; i >0 ; i--) { //len/2表示的所有非终端结点中的最大值,即前len/2项都为非终端结点
         HeadAdjust(A,i,len);
    }
    for (int i = len; i >1 ; i--) {
        swap(A[1],A[i]);  //将当前栈顶元素与最后一位元素交换位置
        HeadAdjust(A,i,i-1); //将剩余的前i-1项元素再次创建成大根堆
    }
}
void HeadAdjust(int A[] ,int k, int len){ //len表示数组长度
     A[0]=A[k]; //A[0]暂存子树的根结点,防止被覆盖
     int i;
    for ( i =2*k; i <=len ; i*=2) { //2*k代表这k的左孩子结点,沿key值较大的子结点向下筛选
        if (i<len&&A[i]<A[i+1])
            i++; //选择key值较大的结点的下标
        if (A[0]>A[i])
            break;
        else{
            A[k]=A[i]; //将A[i]调整到双亲结点上
            k=i; //修改k值,方便继续进行筛选
        }
    }
        A[k]=A[0];  //被筛选结点的值放入最终位置
}
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
  • 稳定性:不稳定

四、归并排序和基数排序

4.1、归并排序
  • 定义:归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2](x表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序

代码展示(二路归并):

递归版

//归并排序(递归版)
#define MaxSize 100 //定义临时数组的最大范围
int B[MaxSzie+1];
void Merge(int A[], int low,int mid , int high){
    int i ,j,n;
    for (int k = low; k <=high ; k++) {
        B[k]=A[i]; //将A数组中的元素全部存入到临时数组中,对临时数组B进行归并操作
    }
    for ( i = low, j=mid+1, n=i ;i<=mid&&j<=high;n++ ) {
        if (B[i]<=B[j]) //比较左右两段中的元素,选择较小的插入到元素组中
            A[n]=B[i++];
        else
            A[n]=B[j++];
    }
    while(i<=mid)A[n++]=B[i++]; //检测两表中未检测完的元素
    while(j<=high)A[n++]=B[j++];
}
void MergeSort(int A[],int low , int high){
    if (low<high){ 
        int mid=(low+high)/2; //从中间划分两个子序列
        MergeSort(A,low,mid); //对左侧子序列进行递归排序
        MergeSort(A,mid+1,high); //对右侧子序列进行递归排序
        Merge(A,low,mid,high); //归并
    }
}
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
  • 稳定性:稳定
4.2、基数排序(了解)
  • 定义:基数排序是一种借助多关键字排序的思想对单逻辑关键字排序的方法。

考到的概率很小,这里就不再赘述。

标签:排序,int,复杂度,元素,第八章,序列,数据结构,插入排序
From: https://www.cnblogs.com/wfy-studying/p/17554769.html

相关文章

  • 数据结构之线性表
    线性表的定义和操作线性表的定义    线性表是具有相同数据类型的n(n>=0)个数据元素的有限序列,其中n为表长,当n=0时线性表是一个空表。若用L命名线性表,则其一般表示为L=(a1,a2,...ai,ai+1,...an)几个概念:   相同是指每个数据元素所占空间一样大。   ai是线......
  • 严蔚敏 数据结构 配套教材 PDF
    目录严蔚敏数据结构配套教材PDF下载地址:严蔚敏数据结构配套教材PDF配套教材包括:严蔚敏《数据结构题集》(C语言版).pdf严蔚敏《数据结构》(C语言版)配套题库【名校考研真题+章节题库+模拟试题】严蔚敏《数据结构》(C语言版)笔记和习题(含考研真题)详解严蔚敏《数据结构》(C语言版......
  • Redis底层数据结构
    Redis是什么?Redis是一个键值数据库,以“快”著称Redis是为什么这么快?我们都知道Redis很快,它在接收到一个键值对数据后,能以微妙级别的速度找到数据并快速完成操作。数据库这么多,为啥Redis能有这么突出的表现呢?一方面,这是因为它是内存数据库,所有操作都在内存上完成,内存的访问速......
  • 数据结构练习笔记——单链表的创建
    单链表的创建【问题描述】从键盘终端输入若干整数,为其创建带头节点的单链表存储结构【样例输入】51223323345【样例输出】1223323345【样例说明】第一行的数为单链表中元素的个数,后面为各元素的值#include<iostream>usingnamespacestd;structLNode{......
  • 数据结构之数据结构要学什么,基本概念,三要素
       我从大二上学期的时候学了数据结构,但是当时对数据结构的重要性并不太重视,直到在升大三的暑假,才意识到数据结构对以后学语言和找工作方面的重要性,所以亡羊补牢,为时未晚,尝试着结合b站上王道考研数据结构课,来记录自己对知识和代码的理解。  数据结构学习的内容可以理解......
  • 数据结构--查找
    数据结构--查找7.1查找的概念在哪里找?---查找表查找表是由同一类型的数据元素(或记录)构成的集合.由于"集合"中的数据元素之间存在着松散的关系,因此查找表是一种灵便的结构什么是查找?-----根据给定的某个值,在查找表中确定一个关键字等于给定值的数据元素或(记录).......
  • 数据结构学习5
    17、顺序查找①查找的基本概念基本概念查找表:由同一类型的数据元素(或记录)构成的集合查找:查询特定元素是否在表中查找成功:若表中存在特定元素,称查找成功,应输出该记录查找不成功:表中不存在给定值的元素,称查找不成功静态查找:只查找,不改变集合内的数据元素动态查找:......
  • 数据结构学习6
    21、哈希查找表①哈希表的基本概念哈希表的概念哈希表:即散列存储结构散列存储的基本思想:建立关键码与存储位置对应关系,或者说由关键码的值决定数据的存储的地址。优点:查找速度极快,查找效率与元素个数无关例1:若将学生信息按如下方式存入计算机,如:将2001011810201的......
  • 数据结构学习3
    9、栈的链式存储结构及实现定义栈的链式存储结构称为链栈,是运算受限的单链表。其插入和删除操作只能在表头位置上进行。因此,链栈没有必要像单链表那样附加头结点,栈顶指针top就是链表的头指针。对于链栈来说:1.不需要头结点2.不存在栈满的情况3.top=NULL,为空栈示意图:链......
  • redis数据结构编码优化(1)
    redis数据结构内部编码优化(1)Redis可以通过内部编码规则来节省空间。Redis为每种数据类型提供了两种内部编码方式。以散列类型为例,散列类型是通过散列表实现的,这样就可以实现o(1)时间复杂度的查找、赋值操作,然而当键中元素很少的时候,o(1)的操作并不会比o(n)有明显的性能提高,所以这......