南邮数据结构

news2024/12/1 0:23:10

md文档网址:https://gitee.com/infiniteStars/wang-dao-408-notes/blob/master/408/数据结构.md

1 绪论

1.1算法的基本概念

  • 程序与算法的区别和联系

联系:程序是计算机指令的有序集合,是算法用某种程序设计语言的表述,是算法在计算机上的具体实现。
区别:在语言描述上不同,程序必须是用规定的程序设计语言来写,而算法的描述形式包括自然语言、伪代码、流程图和程序语言等;算法所描述的步骤—定是有限的,而程序可以无限地执行下去,比如一个死循环可以称为程序,但不能称为算法。

1.2数据结构的基本概念

数据结构是由某一数据对象及该对象中所有数据元素之间的关系组成的。数据结构包括数据的逻辑结构,存储结构及数据的运算三方面的内容。

1.3数据抽象和抽象数据类型

抽象数据类型(ADT)是一个数据模型以及在其上定义的运算集合。最主要的特征是数据封装和信息隐蔽。

1.4描述数据结构和算法

  • 算法的五个特征
  1. 输入:0个及以上
  2. 输出:1个及以上
  3. 可行性
  4. 确定性
  5. 有穷性

1.5算法分析的基本方法

算法的时间复杂度一般是指程序运行从开始到结束所需的时间。

算法执行时间需通过依据该算法编制的程序在计算机上运行所消耗的时间来度量。

度量算法执行时间的方法:事后统计法,事前估计法

影响算法时间效率最主要因素是问题规模。

2 线性表

2.1线性表的定义及基本操作

2.2线性表的顺序存储

1. 数据结构定义

#define MaxSize 100   //定义顺序表的最大长度
typedef struct {
   ElemType data[MaxSize];
   int length;       //顺序表的当前长度
}SqlList;            //顺序表的类型定义

2. 基本操作

插入操作

  1. 判断索引是否越界
  2. 判断元素个数是否大于最大长度
  3. 索引 i 之后的元素往后移一位
  4. 将元素e放入位置 i 处

删除操作

  1. 判断索引是否越界
  2. 删除索引 i 处的元素 e
  3. 将 i 后边的元素向前移一位

2.3线性表的链接存储

1.单链表

1.数据结构定义
typedef struct LNode{
    ElemType data;         //数据域
    struct LNode *next;    //指针域
}LNode, *LinkList;
2. 基本操作

插入——头插法

image-20220919094648714

  • 将结点s插入到a1前面
s.next = L.next
L.next = s

插入——尾插法

image-20220919094717108

  • 将结点 s 插到最后
r.next = s    //r为指向尾结点的指针
r = s    

删除操作

image-20220919094741430

  • 删除结点p后边的结点q
p.next = p.next.next
free(q)

2. 双链表

1. 数据结构定义

image-20220919094135965

typedef struct DNode{
    ElemType data;
    struct Dnode *prior,*next;  //前驱和后继指针
}Dnode,*DLinkList;
2. 基本操作

插入操作

image-20220919094904951

s.next = p.next      //第一步
p.next.prior = s     //第二步
s.prior = p          //第三步
p.next  = s          //第四步

删除操作

image-20220919100233234

p.next = q.next
q.next.prior = p
free(q)

3. 循环链表

循环单链表

image-20220919100512832

循环双链表

image-20220919100540976

4.静态链表

1. 概念
  • 静态链表是借助数组来描述线性表的链式存储结构

image-20220919100729273

2. 数据结构
#define MaxSize 50 //静态链表的最大长度
typedef struct{
    ElemType data;
    int next;      //下一个元素的数组下标
}SLinkList[MaxSize];

3 栈和队列

3.1栈和队列的基本概念

3.2栈和队列的顺序存储结构

1. 栈的顺序存储

#define MaxSize 50;
typedef struct{
    ElemType data[MaxSize];  //存放栈中元素
    int top;                 //栈顶指针
} SqStack;

2. 栈的基本操作

image-20220919101728348

  • 初始化时栈顶指针 top 指向 -1

入栈

S.data[++S.top] = x; //指针先加1,再入栈

出栈

x = S.data[S.top--]; //先出栈,指针再减1 

3.共享栈

image-20220919102322840

两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空;仅当两个栈顶指针相邻(top1-top0=1)时,判断为栈满。当0号栈进栈时top0先加1再赋值,1号栈进栈时top1先减1再赋值:出栈时则刚好相反。

4. 队列的顺序存储

#define MaxSize 50
typedef struct{
    ElemType data[MaxSize];
    int front,rear;     //队头指针和队尾指针
}SqQueue;
  • 初始状态(队空条件):Q.front== Q.rear ==0。
  • 进队操作:队不满时,先送值到队尾元素,再将队尾指针加1。
  • 出队操作:队不空时,先取队头元素值,再将队头指针加1。

image-20220919103608190

5.循环队列

区分队满和队空的三种方法

三种方法进栈,入栈操作都一样

  • 进栈:队尾指针进1——Q.rear = (Q.rear+1)%MaxSize
  • 出栈:队首指针进1——Q.front = (Q.front+1)%MaxSize
  1. 牺牲一个存储单元

image-20220919104528598

  • 队满条件: (Q.rear+1)%MaxSize == Q.front。
  • 队空条件:Q.front==Q.rear
  • 队列中元素的个数: (Q.rear-Q.front+MaxSize) % Maxsize。
  1. 类型中增设表示元素个数的数据成员。这样,队空的条件为Q.size=0;队满的条件为Q.size=MaxSize。这两种情况都有Q.front=Q.rear
  2. 类型中增设tag 数据成员,以区分是队满还是队空。tag等于0时,若因删除导致Q.front=Q.rear,则为队空;tag等于1时,若因插入导致Q.front==Q.rear,则为队满。

3.3栈和队列的链式存储结构

1. 栈的链式存储

image-20220919102447029

typedef struct Linknode{
    ElemType data;
    struct Linknode *next;
} LiStack;
  • 出栈和进栈都在链表的表头进行

2. 队列的链式存储

typedef struct LinkNode{         //链式队列结点
    ElemType data;
    struct LinkNode *next;
}LinkNode; 

typedef struct{                 //链式队列
     LinkNode *front,*rear;     //队头和队尾指针
}LinkQueue;
  • 队列判空
Q.front = Q.rear
  • 入队
//创建一个新结点s
Q.rear->next = s
Q.rear = s
  • 出队
LinkNode *p = Q.front->next;       //p为队列的第一个结点,Q.front指向头结点
Q.front->next = p->next;
if (Q.rear == p){                 //队列中只有一个元素
    Q.rear = Q.front;             //队尾指针指向头结点
}
free(p);

3. 双端队列

双端队列是指允许两端都可以进行入队和出队操作的队列。

image-20220919110919339

  • 输出受限的双端队列

image-20220919111010794

  • 输入受限的双端队列

image-20220919111110381

3.4表达式计算(栈)

中缀表达式,后缀表达式,前缀表达式

3.5递归(栈)

将递归算法变为非递归算法

4 数组

4.1数组的基本概念

4.2特殊矩阵

1. 对称矩阵的压缩存储

  • 按行优先,下标从 0 开始

image-20220920092159201

2. 三角矩阵的压缩存储

  • 下三角矩阵(按行优先,下标从 0 开始)

image-20220920092627208

  • 下三角矩阵(按行优先,下标从 0 开始)

image-20220920093712556

image-20220920093728064

3.三对角矩阵

  • 按行优先,下标从 0 开始

image-20220920094054711

4.3稀疏矩阵

  • 将非零元素及相应的行和列构成一个三元组(行标,列表,值)

image-20220920094332301

  • 可以按行优先(行三元组表)或者列优先(列三元组表)存在在一个三元组中。
  • 稀疏矩阵快速转置算法所需的num数组与k数组
    1. num[j]统计稀疏矩阵A中列号为 j 的非零元素个数
    2. k[j] 统计稀疏矩阵A中列号从 0 到 j-1 的非零元素个数之和。

5 树和二叉树

5.1树的基本概念

  • 度为 m 的树中第 i 层上至多有 m^(i-1) 个节点

    image-20220920094849460

5.2二叉树

5.2.1二叉树的定义及主要特征

  • 非空二叉树上的叶子结点数等于度为2的结点数加1,即n0 =n2 + 1。
  • 非空二叉树上第k层上至多有 2^(k-1) 个结点(k≥1)。
  • 高度为h的二叉树至多有2^h - 1个结点(h≥1)。
  • image-20220920095444503

5.2.2二叉树的顺序存储和链式存储

1. 顺序存储

image-20220920100110230

2.链式存储

image-20220920100214079

  • 链式数据结构定义
typedef struct BiTNode{
    ElemType data;
    struct BiTNode *lchild,*rchild;  //左,右孩子指针
}BiTNode,*BiTree;

在含有n个结点的二叉链表中,含有 n+1 个空链域

5.2.3二叉树的遍历

1.先序遍历
void PreOrder(BiTree T){
    if (T!=null){
        visit(I);
        PreOrder(T->lchild);
        PreOrder(T->rchild);
    }
}
2.中序遍历
void InOrder(BiTree T){
    if (T!=null){
        visit(I);
        InOrder(T->lchild);
        visit(I);
        InOrder(T->rchild);
    }
}
3.后序遍历
void PostOrder(BiTree T){
    if (T!=null){
        visit(I);
        PostOrder(T->lchild);
        PostOrder(T->rchild);
        visit(I);
    }
}
4. 层次遍历
void LevelOrder(BiTree T){
    InitQueue(Q);        //初始化辅助队列
    BiTree p;
    EnQueue(Q,T);        //将根结点入队
    while (!IsEmpty(Q)){
        DeQueue(Q,p);    //删除队头元素,并用p返回(赋值给p)
        visit(p);
        if (p->lchild!=null)
            EnQueue(Q,p->lchild);
         if (p->rchild!=null)
            EnQueue(Q,p->rchild);
    }
}

5.2.4 线索二叉树的基本概念和构造

概念

线索二叉树是以一定的规则将二叉树中的结点排列成一个线性序列,从而得到几种遍历序列,使得该序列中的每个结点(第一个和最后一个结点除外)都有一个直接前驱和直接后继。

引入线索二叉树是为了加快查找结点前驱和后继的速度

构造

image-20220920104922000

image-20220920104939458

数据结构

typedef struct ThreadNode{
    ElemType data;
    struct ThreadNode *lchild,*rchild;
    int ltag,rtag;
}ThreadNode,*ThreadTree;

通过中序遍历对二叉树线索化

image-20220920105715332

void Inthread(ThreadTree &p,ThreadTree &pre){
    if (p!=null){
        Inthread(p->lchild,pre);    //递归,线索化左子树
        if (p->lchild == NULL){
            p->lchild = pre;
            p->ltag = 1;
        }
        if (pre!=NULL && pre->rchild==NULL){
            pre->rchild = p;
            pre->rtag = 1;
        }
        pre = p;  //标记当前结点为刚刚访问过的结点
        Inthread(p->rchild,pre);     //递归,线索化右子树
    }
}

通过中序遍历建立中序搜索二叉树

void CreateInThread(ThreadTree T){
    ThreadTree pre = null;
    if (T!=null){
        Inthread(T,pre);           //线索化二叉树
        pre->rchild = NULL;
        pre->rtag = 1;
    }
}

5.3树和森林

5.3.1树的存储结构

1.双亲表示法

image-20220920110932973

#define MAX_TREE_SIZE 100     //树中最多结点数
typedef struct{
    ElemType data;
    int parent;              //双亲位置域
}PTNode;
typedef struct{
    PTNode nodes[MAX_TREE_SIZE];
    int n;                  //结点数
}PTree;

该存储结构利用了每个结点(根结点除外)只有唯一双亲的性质,可以很快得到每个结点的双亲结点,但求结点的孩子时需要遍历整个结构。

2.孩子表示法

image-20220920112245285

这种存储方式寻找子女的操作非常直接,而寻找双亲的操作需要遍历n个结点中孩子链表指针域所指向的n个孩子链表。

3.孩子兄弟表示法(左孩子,右兄弟)

image-20220920112615626

typedef struct CSNode{
     Elemtype data;
     struct CSNOde *nextchild,*nextsibling; 
}CSNode,*CSTree;

这种存储表示法比较灵活,其最大的优点是可以方便地实现树转换为二叉树的操作,易于查找结点的孩子等,但缺点是从当前结点查找其双亲结点比较麻烦。

5.3.2森林和二叉树的转换

  1. 将森林中每棵树转换成二叉树(左孩子右兄弟)
  2. 将第二个二叉树插到 第一个二叉树的右孩子处,以此类推

image-20220920113245580

5.3.3树和森林的遍历

树的后根遍历也叫做中根遍历

image-20220920113534100

5.4树和二叉树的应用

5.4.1二叉排序树

1.查找
BSTNOde *BST_Search(BiTree T,ElemType key){
    while (T!=NULL && T->data!=key){
        if (key<T->data)
            T = T->lchild;
        else
            T = T->rchild; 
    }
    return T;
}
2.插入
  • 插入的结点一定是一个新添加的叶结点
int BST_Insert(BiTree &T,KeyType k){
    if (T==null){
        T = (BiTree)malloc(sizeof(BSTNode));
        T->data = k;
        T->lchild = T->rchild = null;
        return 1;
    }
    else if (k==T->data){  //树中存在相同关键字的结点,插入失败
        return 0;
    }
    else if (k<T->data){
        return BST_Insert(T->lchild,k);
    }
    else
        return BST_Insert(T->rchild,k);
}
3.删除
  • 叶结点,直接删除
  • 只有一颗左(右)子树,直接删除,左(右)子树接替
  • 左右子树都有,让结点z的直接后继(或直接前驱)代替z,然后在子树中删除直接后继,变为第一或第二种情况

image-20220921085318354

4. 查找性能
  • 二叉排序树的查找效率主要取决于树的高度。
  • 平均查找长度为O(log2n),最坏情况下平均查找长度为O(n)

5.4.2 平衡二叉树

  • 平均查找长度为O(log2n)
1. 插入

image-20220921090129561

image-20220921090242165

例:以关键字序列{16,3,7,11,9,26,18,14,15}构造一颗AVL树(二叉平衡树)

image-20220921091004360

5.4.3哈夫曼(Huffman)树和哈夫曼编码

哈夫曼树的构造

给定n个权值分别为W1, W2,……,Wn的结点,构造哈夫曼树的算法描述如下:

  1. 将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F。
  2. 构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和。
  3. 从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
  4. 重复步骤2和3,直至F中只剩下一棵树为止。

6 图

6.1图的基本概念

6.2图的存储及基本操作

6.2.1邻接矩阵法

image-20220921091647501

#define MaxVertexNum 100      //顶点数目的最大值
typedef char VertexType;      //顶点的数据类型
typedef int EdgeType          //带权图边上权值的数据类型
typedef struct{
    VertexType Vex[MaxVertexNum];    //顶点表
    EdgeType Edge[MaxVertexNum][MaxVertexNum]; //边表
    int vexnum,arcnum;        //图的当前顶点数和弧数
}MGraph;

image-20220921092245382

6.2.2邻接表表示法

#define MaxVertexNum 100      //图中顶点数目的最大值
typedef struct ArcNode{       //边表结点
    int adjvex;              
    struct ArcNode *next;    
}ArcNode;
typedef struct VNode{        //顶点表结点
    VertexType data;         //顶点信息
    ArcNode *first;          //指向第一条以赴该节点的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
    AdjList vertices;    //邻接表
    int vexnum,arcnum;   //图中顶点数和弧数
}ALGraph;

image-20220921093723288

6.3图的遍历

6.3.1深度优先搜索

bool visited [MAX VERTEX NUM];//访问标记数组
void BFSTraverse(Graph G){   //对图G进行广度优先遍历
    for(i=0;i<G.vexnum;++i)
        visited[i]=FALSE;   //访问标记数组初始化
    InitQueue (Q);          //初始化辅助队列Q
    for(i=0;i<G.vexnum; ++i)//从0号顶点开始遍历
        if(!visited[i])     //对每个连通分量调用一次BFS
            BFS(G,i);       //vi未访问过,从vi开始BFS
    
void BFS(Graph G, int v){ //从顶点v出发,广度优先遍历图G
    visit(v);             //访问初始顶点V
    visited[v]=TRUE;      //对v做已访问标记
    Enqueue(Q, v);        //顶点v入队列Q
    while(!isEmpty (Q)){
        DeQueue(Q, v);    //顶点v出队列
        for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){      //检测v所有邻接点
            if(!visited[w]){ //w为v的尚未访问的邻接顶点
            visit(w);        //访问顶点w
            visited[w]=TRUE; //对w做已访问标记
            EnQueue(Q,w);    //顶点w入队列
            }
        }
    }
}

性能分析

  • 最坏情况下,空间复杂度O(|V|)
  • 采用领接表存储,时间复杂度为 O(|V|+|E|)。采用邻接矩阵存储,时间复杂度为 O(|V|^2)

6.3.2广度优先搜索

bool visited[MAX_VERTEX_NUM];      //访问标记数组
void  DFSTraverse(Graph G){        //对图G进行深度优先遍历
    for(v=0;v<G.vexnum;++v)
       visited[v] =FALSE;          //初始化已访问标记数据
    for (v=0;v<G.vexnum;++v)       //本代码中是从v=0开始遍历
       if (!visited[v])
          DFS(G,v);
}
void DFS (Graph G,int v){         //从顶点v出发,深度优先遍历图G
    visit(v) ;                    //访问顶点v
    visited[v]=TRUE;              //设已访问标记
    for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
    if (!visited[w])              //w为v的尚未访问的邻接顶点
        DFS(G,w);
}

性能分析

  • 空间复杂度O(|V|)
  • 采用领接表存储,时间复杂度为 O(|V|+|E|)。采用邻接矩阵存储,时间复杂度为 O(|V|^2)

6.4图的基本应用

6.4.1拓扑排序

步骤

  1. 从AOV网中选择一个没有前驱的顶点并输出。
  2. 从网中删除该顶点和所有以它为起点的有向边。
  3. 重复①和②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。

算法实现

bool Topologicalsort (Graph G){
    InitStack(S);    //初始化栈,存储入度为0的顶点
    for (int i=0;i<G.vexnum;i++)
      if(indegree[i]==0)
        Push(s,i);   //将所有入度为0的顶点进栈
    int count=0;     //计数,记录当前已经输出的顶点数
    while(!IsEmpty(S)){     //栈不空,则存在入度为0的顶点
        Pop (s,i);          //栈顶元素出栈
        print[count++]=i;   //输出顶点i
        for(p=G.vertices[i].firstarc;p;p=p->nextarc){
        //将所有i指向的顶点的入度减1,并且将入度减为0的顶点压入栈S
            v=p->adjvex;
            if(!(--indegree[v]))
               Push (s,v);    //入度为0.入栈
            )
        }
        if (count<G.vexnum)
            return false;     //排序失败,有向图中有回路
        else
            return true;      //拓扑排序成功
    }
}

image-20220921102730530

image-20220921102749943

6.4.2关键路径

  • 正向找最大,反向找最小
  • 最早时间=最晚时间 :关键路径

image-20220921104759547

6.4.3 最小代价生成树

1.Prim算法

初始时从图中任取一顶点(如顶点1)加入树T,此时树中只含有一个顶点,之后选择一个与当前T中顶点集合距离最近的顶点,并将该顶点和相应的边加入T,每次操作后T中的顶点数和边数都增1。以此类推,直至图中所有的顶点都并入T,得到的T就是最小生成树。此时T中必然有n-1条边。

image-20220921100546087

2.Kruskal算法

初始时为只有n个顶点而无边的非连通图T= {V,{}},每个顶点自成一个连通分量,然后按照边的权值由小到大的顺序,不断选取当前未被选取过且权值最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入T,否则舍弃此边而选择下一条权值最小的边。以此类推,直至T中所有顶点都在一个连通分量上。

image-20220921100821070

6.4.4最短路径

image-20220921101736790

image-20220921102425462

7 搜索(Search)

7.1搜索的基本概念

7.2顺序搜索法

王海艳数据结构版

1.无序表的顺序搜索

int Search(listSet L,ElemType x){
    int i;
    for (i=0;i<n;i++){
        if (L.element[i]==x)
            return i;
    }
    return -1;
}
  • 搜索成功的情况下平均查找长度 ASL = (n+1)/2
  • 搜索失败的情况下平均查找长度 ASL = n

2. 有序表的顺序搜索

int Search(listSet L,ElemType x){
    int i;
    for (i=0;L.element[i]<x;i++){
        if (L.element[i]==x)
            return i;
    }
    return -1;
}
  • 搜索成功的情况下平均查找长度 ASL = (n+1)/2
  • 搜索失败的情况下平均查找长度 ASL = n/2+2

7.3二分搜索法

int Binary Search(SeqList L,ElemType key)
    int low=0,high=L.TableLen-1,mid;
    while.(low<=high){
        mid=(low+high)/2;   //取中间位置
        if(L.elem[mid] ==key)
           return mid;      //查找成功则返回所在位置
        else if(L.elem[mid]>key)
           high=mid-1;      //从前半部分继续查找
        else
           low=mid+1;       //从后半部分继续查找
    }
    return-1;               //查找失败,返回-1
}
  • 时间复杂度为O(log2n)

  • 判定树

image-20220921111436093

在等概率情况下,查找成功(圆形结点)的ASL=(1×1+2×2+3x4+4×4)/11 =3,查找不成功(方形结点)的ASL=(3x4+4x8)/12= 11/3。

7.4 B-树及其基本操作

B树适用于外搜索的理由

B树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小、充分利用了磁盘预读的功能。每次读取磁盘页时就会读取整个节点。也正因每个节点存储着非常多个关键字、树的深度就会非常的小。进而要执行的磁盘读取操作次数就会非常少,更多的是在内存中对读取进来的数据进行查找。

1.插入

image-20220921113634166

2. 删除

image-20220921113943110

image-20220921113957628

3. B-树的特性

image-20220921114124493

7.5散列(Hash)表

1.概念

散列函数:一个把查找表中的关键字映射成该关键字对应的地址的函数,记为Hash(key)=Addr

散列(Hash)表:根据关键字而直接进行访问的数据结构。

常用的散列函数:

  1. 直接定址法:H(key)=a x key + b
  2. 除留余数法:H(key)=key % p
  3. 平方取中法
  4. 折叠法:将关键字值从左到右划分成位数相等的若干个部分值(位数与散列地址位数相等),部分值叠加,获得散列地址。如果计算结果超过地址位数,则将最高位去掉。

处理冲突的方法:

  1. 开放定址法
    取定某一增量序列后,对应的处理方法就是确定的。通常有以下4种取法:

    ①线性探测法。
    ②平方探测法(二次探测法)
    ③再散列法
    ④伪随机序列法

  2. 拉链法

2. 线性探测法

image-20220921114829925

3.平方散列法

image-20220921115324253

4. 再散列法(双散列法)

image-20220921115942319

  • 易错:第二个散列函数是下一个探测地址的增量

5. 伪随机序列法

当di=伪随机数序列时,称为伪随机序列法。

6.拉链法

image-20221031111156221

  • ASL失败 = (2+3+1+0+0+2+0+0+0+0+0)/11 = 8/11 (王道书上的方法,以这个为准)

7.6搜索算法的分析及应用

8 内排序

8.1排序的基本概念

8.2简单选择排序

  • 选择最小元素,放在队列前端

image-20220921163727171

void SelectSort(ElemType A[],int n){
    for (i=0;i<n-1;i++){
         min=i;
        for (j=i+1;j<n;j++){
            if (A[i]<A[min])
                min=j;
        }
        if (min!=i)
            swap(A[i],A[min]);
    }
}

8.3直接插入排序

  • 一个一个往前移

image-20220921163638212

void InsertSort(ElemType A[],int n){
    int i,j;
    for (i=2;i<=n;i++){
        if (A[i]<A[i-1]){
           A[0]=A[i];               //复制为哨兵,A[0]不存放元素
           for (j=i-1;A[0]<A[j];--j)//找到最后一个比A[0]的元素   
               A[j+1] = A[j];       //依次往后移
            A[j+1] = A[0];          //将A[0]插入
        }
    }
}

8.4冒泡排序(bubble sort)

  • 一个一个比较

image-20220921165307850

  • 下边代码是每趟排序选出一个最小位置,与上图过程不一致
void BubbleSort(ElemType A[],int n){
    for (i=0;i<n-1;i++){
        flag = false;
        for (j=n-1;j>i;j--)
            if (A[j-1]>A[j]){
                swap(A[j-1],A[j]);
                flag = true;
            }
        if (flag = false)
            return;           //本趟遍历没有发生交换,说明已经有序
    }
}

8.5希尔排序(shell sort)

  • 增量之间的插入排序(常用增量)

image-20220921170125199

public void sort(Comparabel[] a){
    int len=a.length;
    int h=1;
    while (h<len/2)
        h=2*h+1;
    while(h>=1){
        for (int i=h;i<len;i++){
            for (int j=i;j>=h && less(a[j],a[j-h]);j-=h)
                exch(a,j,j-h);
        }
        h=h/2;
    }
}

8.6快速排序

王道

image-20220921171940038

image-20220921172005033

void QuickSort (ElemType A[],int low,int high){
    if(low<high){   //递归跳出的条件
        //Partition ()就是划分操作,将表A[low…high]划分为满足上述条件的两个子表
        int pivotpos=Partition(A,low, high); //划分
        QuickSort(A,low,pivotpos-1); //依次对两个子表进行递归排序
        QuickSort(A,pivotpos+1,high);
    }
}
int Partition(ElemType A[],int low,int high){ //一趟划分
    ElemType 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;   //枢轴元素存放到最终位置
        return low;     //返回存放枢轴的最终位置
    }
}

南邮

image-20221205113630535

image-20221205113721395

  • 示例
待排序序列 48  36  68  72  12  48  02
第一趟    (12  36  02  48)  48  (72  68) 
第二趟    (02)  12  (36  48)  48  (72  68)       对左边进行排序
第三趟    02  12  36  48  48  72  68             对(36,48)进行排序 
第四趟    02  12  36  48  48  68  72             对右边进行排序

8.7堆排序

  • ①建堆 ②m/2 ③建大根堆 ④与最后一个换

image-20220921172714370

8.8两路合并排序(merge sort)

  • 分组再结合

image-20220922090326830

//将前后两个相邻的顺序表合成一个
ElemType *B = (ElemType *)malloc((n+1)*sizeof(ElemType));   //辅助数组B
//A[low,mid],A[mid+1,high]有序,将他们两个合并到一起
void merge(ElemType A[],int low,int mid,int high){
    for (int k=low;k<=high;k++){
        B[k]=A[k];
    }
    for (i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
        if (B[i]<B[j])
            A[k]=B[i++];
        else
            A[k]=B[j++];
    }
    //若一个表未检测完,复制
    while (i<=mid)
        A[k++]=B[i++];
    while (j<=high)
        A[k++]=B[j++];
}

//归并排序
void MergeSort(ElemType 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);
    }
}

8.9基数排序

image-20220922091544472

8.10各种内部排序算法的比较

image-20220922093415193

不稳定性:快些(希)选好友来聊天

时间快:快些归队

8.11内部排序算法的应用

  1. n较小,可以采用选择排序或插入排序。当记录本身信息量较大时,使用选择排序较好。

  2. 初始状态已基本有序,可以选用插入排序或冒泡排序

  3. 所需的辅助空间:堆排序<快速排序<归并排序

  4. 交换次数最小的排序是简单选择排序

  5. 排序速度与数据的初始排列状态没有关系的是简单选择排序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年如何搭建最小可行性的产品文档/产品手册?

在推出并击败竞争对手进入市场的竞赛中&#xff0c;很容易将“不必要的”任务&#xff08;如文档&#xff09;放在次要位置。但根据 Write the Docs 纪录片社区的说法&#xff0c;文档应该既是先导性的&#xff0c;也是参与性的。这意味着您应该在开始开发之前开始记录&#xf…

吊打高斯模糊的stackBlur加入OpenCV

stackBlur介绍 stackBlur 最近才加入到OpenCV中&#xff0c;将在下一个Relase版本&#xff08;4.7&#xff09;中出现。C用户可以尝试从源码编译OpenCV体验一下。Python 用户可以尝试用pip安装rolling版本的OpenCV&#xff1a; pip install opencv-python-rolling4.6.0.202210…

新手教程 | 常见的爬虫类型有哪些?

程序猿圈流传着一个神话级别的事&#xff1a;全公司仅靠1个人&#xff0c;每年就能转上1400多万美元。听起来天方夜谭一样&#xff0c;那他是如何做到的呢&#xff1f;看报道就会发现&#xff0c;他利用的是爬虫技术。 随着互联网的发展&#xff0c;从海量的互联网数据中&…

不可错过,Java程序员必备珍藏书单

不要因为迷茫&#xff0c;而停止了脚下的路。给大家推荐一份Java程序员必看的书单&#xff0c;豆瓣评分都挺不错的&#xff0c;往下看&#xff01; 一、Java 基础篇书单 《Java编程思想》&#xff1a;从Java的基础语法到最高级特性&#xff08;深入的面向对象概念、多线程、自…

煤矿智能化相关50项团体标准征求意见

智能化煤矿总体架构 原文地址&#xff1a;https://chinacs.scimall.org.cn/a3651.html 由煤矿智能化创新联盟等单位提出&#xff0c;中国煤炭学会归口&#xff0c;中煤科工集团常州研究院有限公司等单位起草的《煤矿通信接口与协议通用技术要求》50项团体标准已完成征求意见稿的…

用 Python 脚本实现电脑唤醒后自动拍照 截屏并发邮件通知

背景 背景是这样的, 我的家里台式机常年 休眠, 并配置了 Wake On Lan (WOL) 方便远程唤醒并使用. 但是我发现, 偶尔台式机会被其他情况唤醒, 这时候我并不知道, 结果白白运行了好几天, 浪费了很多电. 所以我的需求是这样的: &#x1f914; 电脑唤醒后(可能是开机, 有可能是…

3款电脑必装软件,功能强大且免费,打死也舍不得卸载

闲话不多说&#xff0c;直接上狠货。 1、FlowUs息流 FlowUs息流是一款知识管理与协作平台&#xff0c;以云端笔记为载体&#xff0c;配合在线文档、知识库、文件夹等多形态功能&#xff0c;支持免费使用&#xff0c;极大提高个人与团队工作效率。支持多端同步使用&#xff0c;无…

STL空间配置器框架分析

目录 一、空间配置器概念 二、空间配置器的作用 三、内存池技术 四、空间配置器的实现原理 3.1 流程概述 3.2 一级空间配置器 3.3 二级空间配置器 3.3.1 二级空间配置器设计 3.3.2 内存碎片问题 一、空间配置器概念 即为各个容器高效的管理空间(空间的申请与回收)的。…

聊一聊双亲委派模式

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 说起双亲委派模型&#xff0c;不得不说一下类加载器。 类加载器是什么&#xff1f; 当我们编译Java类时&#xff0c;JVM会创建与平台和…

Allegro174版本新功能介绍之移动画布不闪屏设置

Allegro174版本新功能介绍之移动画布不闪屏设置 Allegro在升级到174版本后,在移动画布的时候,视图数据量比较大的情况,会出现闪屏现象 Allegro在切换到Open GL模式下,这个现象会有所缓解,具体操作如下 选择Setup选择User-preferences

【Ansible】ansible 基础知识

ansible 文章目录ansible一、ansible Ad-Hoc 命令1.命令格式2.模块类型3.联机帮助4.常用模块4.1 command & shell 模块4.2 script 模块4.3 copy 模块4.4 yum_repository4.5 yum 模块4.6 systemd 模块4.7 group 模块4.8 user 模块4.9 file 模块4.10 cron 模块4.11 template …

SIE高级副总裁:关于PS VR2定价、设计、内容的思考

2023年2月22日&#xff0c;索尼将正式推出次世代头显PS VR2&#xff0c;首发VR游戏将超过30款&#xff0c;其中包括热门游戏《GT赛车7 VR》。此外&#xff0c;PS5全球销量也已突破3000万。实际上距离索尼推出上一代PS VR&#xff0c;已经过去了6年时间&#xff0c;相比于Quest等…

M12269 支持PD3.1等快充协议、140W升降压3-8节多串锂电充放电移动电源管理IC

引言 在快充技术持续迭代升级的过程中&#xff0c;充电从小功率向中大功率的转变是最为明显的。支持的快充功率从最初的7.5W&#xff0c;已经向最高240W迈进。PD3.1协议的推出&#xff0c;进一步助力快充加速走向中大功率。新增三种固定电压档&#xff1a;28V&#xff08;100-1…

VS1053 MP3模块介绍

VS1053MP3模块简介ATK-VS1053 MP3 MODULE是ALIENTEK推出的一款高性能音频编解码模块&#xff0c;该模块采用VS1053B作为主芯片&#xff0c;支持&#xff1a;MP3/WMA/OGG/WAV/FLAC/MIDI/AAC等音频格式的解码&#xff0c;并支持&#xff1a;OGG/WAV音频格式的录音&#xff0c;支持…

Mask RCNN网络源码解读(Ⅱ) --- ResNet、ResNeXt网络结构、BN及迁移学习

目录 1.ResNet简介 2.residual结构和ResNet-34详解 2.1 residual结构 2.2 Batch Normalization 3.迁移学习 4.ResNeXt网络结构 1.ResNet简介 ResNet在2015年由微软实验室提出&#xff0c;斩获当年lmageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得coco数据…

信道模型:Rayleigh、Rician、卫星→地面

这里写目录标题比较C. Loo模型&#xff1a;直射阴影&#xff0c;多径不阴影Corazza模型&#xff1a;直射和多径都阴影Lutz模型&#xff1a;好坏2个状态Rayleigh and Rician 信道生成Shadowed-Rician 直射径 散射径[Secure Transmission in Cognitive Satellite Terrestrial Net…

Redis的String类型,原来这么占内存

Redis的String类型&#xff0c;原来这么占内存 存一个 Long 类型这么占内存&#xff0c;Redis 的内存开销都花在哪儿了&#xff1f; 1、场景介绍 假设现在我们要开发一个图片存储系统&#xff0c;要求这个系统能够根据图片 ID 快速查找到图片存储对象 ID。图片 ID 和图片存储对…

【Flink基础】-- 源码中的注解

1.Flink自定义注解级别在升级 Flink版本至 1.15.3时&#xff0c;偶然遇到了一个异常&#xff0c;然后就准备详细了解下源码中的注解。设计注解的初衷&#xff1a;为了更好地进行代码和版本管理&#xff0c;Flink使用了Java的注解特性自定义了注解&#xff0c;对代码进行增强说明…

Kubernetes安全扫描之kubescape

一 背景 Kubescape 是第一个用于测试 Kubernetes 是否按照 NSA 和 CISA 的 Kubernetes 强化指南中定义的安全部署的工具 使用 Kubescape 测试集群或扫描单个 YAML 文件并将其集成到您的流程中。 二 特性 功能&#xff1a;提供多云 K8s 集群检测&#xff0c;包括风险分析、安…

stack 中缀表达式求值

【解法一】双栈思路梳理 【解法二】利用逆波兰表达式求解&#xff08;中缀转后缀&#xff09; 这个有俩种方法&#xff0c;一是直接根据条件进行各种情况的推导直接由中缀表达式求解&#xff0c; 二就是将中缀表达式转化为后缀表达式&#xff0c;利用更容易的逆波兰表达式求解…