数据结构期末复习

一、各种数据结构的创建(初始化)

1.1 线性表——顺序表

// 顺序表的存储结构
#define MAXSIZE 100          // 顺序表可能达到的最大长度
typedef struct{
    ElemType *elem;          // 存储空间的基地址
    int length;              // 当前长度
}SqList;                     // 顺序表的结构类型为SqList


// 初始化
bool InitList(SqList &L){
    L.elem = (ElemType*)malloc(sizeof(ElemType));
    if(!L.elem) return false;
    L.length = 0;
    return true;
}

1.2 线性表——单链表

// 单链表的存储结构
typedef struct LNode{
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;


// 初始化
bool InitList(LinkList &L){
    L = (LinkList)malloc(sizeof(LNode));
    L->next = NULL;
    return true;
}

1.3 线性表——双链表

// 双向链表的存储结构
typedef struct DuLNode{
    ElemType data;            // 数据域
    struct DuLNode *prior;    // 前指针
    struct DuLNode *next;     // 后指针
}DuLNode,*DuLinkList;


// 初始化
bool InitDuList(DuLinkList &D){
    D = (DuLinkList)malloc(sizeof(DuLNode));
    D->prior = NULL;
    D->next = NULL;
}

1.4 栈——顺序栈

// 顺序栈的存储结构
#define MAXSIZE 100
typedef struct{
    ElemType *base;    // 栈底指针
    ElemType *top;     // 栈顶指针
    int stacksize;     // 栈可用的最大容量
}SqStack;


// 初始化
void InitStack(SqStack &s){
    s.base = (ElemType*)malloc(sizeof(ElemType));
    if(!s.base) return false;
    s.top = s.base;
    s.stacksize = MAXSIZE;
    return true;
}

1.5 栈——链栈

// 链栈的存储结构
typedef struct StackNode{
    ElemType data;
    struct StackNode *next;
}StackNode,*LinkStack;



// 初始化
bool InitLinkStack(LinkStack &s){
    s = NULL;
    return true;
}

1.6 队列——顺序队列

// 顺序队列的存储结构
#define MAXSIZE 100;
typedef struct{
    ElemType *base;
    int front;
    int rear;
}SqQueue;


// 初始化
bool InitQueue(SqQueue &q){
    q.base = (ElemType*)malloc(sizeof(ElemType)*MAXSIZE);
    if(!q.base) return false;
    q.front = q.rear = 0;
    return true;
}

1.7 队列——链队列

// 链队列的存储结构
typedef struct QNode{    // 队列的节点
    ElemType data;
    struct QNode *next;
}QNode,*QueuePtr;

typedef struct{         // 链队列
    QueuePtr front;     // 队头指针
    QueuePtr rear;      // 队尾指针
}LinkQueue;



// 初始化
bool InitQueue(LinkQueue &q){
    q.front = q.rear = (LinkQueue*)malloc(sizeof(LinkQueue));
    q.front->next = NULL;        // 头节点的指针域置空
    return true;
}

1.8 串——顺序

// 串的 定长 顺序存储结构
#define MAXLEN 255         // 串的最大长度
typedef struct{
    char ch[MAXLEN+1];     // 存储串的一维数组
    int length;            // 串的当前长度
}SString;

// 串的 堆式 顺序存储结构
typedef struct{
    char *ch;
    int length;
}SString;

1.9 串——链式

// 串的链式存储结构
#define CHUNKSIZE 80
typedef struct Chunk{    // 节点
    char ch[CHUNKSIZE];
    struct Chunk *next;
}Chunk;

typedef struct{
    Chunk *head,*tail;    // 串的头和尾指针
    int length;           // 串的当前长度
}LString;

1.10 树——二叉树的二叉链表

// 二叉树的二叉链表存储表示
typedef struct BiTNode{
    ElemType data;                     // 节点的数据域
    struct BiTNode *lchild,*rchild;    // 左右孩子指针
}BiTNode,*BiTree;

1.11 树——二叉树的二叉线索树

// 二叉线索树存储表示
typedef struct BiThrNode{
    ElemType data;
    struct BiThrNode *lchild,*rchild;  // 左右孩子指针
    int LTag,RTag;                     // 左右标志
}BiThrNode,*BiThrTree;

注意: \color{green}{注意:} 注意:

LTag= { 0 lchid域指示节点的左孩子 1 lchid域指示节点的前驱 RTag= { 0 rchid域指示节点的右孩子 1 rchid域指示节点的后继 \text{LTag=}\left\{ \begin{array}{l} 0\quad\text{lchid域指示节点的左孩子}\\ 1\quad\text{lchid域指示节点的前驱} \end{array} \right.\\ \quad\\ \text{RTag=}\left\{ \begin{array}{l} 0\quad\text{rchid域指示节点的右孩子}\\ 1\quad\text{rchid域指示节点的后继} \end{array} \right.\\ LTag={0lchid域指示节点的左孩子1lchid域指示节点的前驱RTag={0rchid域指示节点的右孩子1rchid域指示节点的后继

1.12 图——邻接矩阵表示法

// 图的邻接矩阵存储表示
#define MaxInt 32767            // 表示极大值 即无穷大
#define MVNum 100               // 最大顶点数
typedef char VerTexType;        // 假设顶点的数据类型为字符型
typedef int ArcType;            // 假设边的权值类型为整型
typedef struct{
    VerTexType vexs[MVNum];     // 顶点表
    ArcType arcs[MVNum][MVNum]; // 邻接矩阵
    int vexnum,arcnum;          // 图的顶点数、边数
}AMGraph;

1.13 图——邻接表表示法

// 图的邻接表的存储表示
#define MVNum 100               // 最大顶点数
typedef struct ArcNode{         // 边节点
    int adjvex;                 // 该边指向的顶点的位置
    struct ArcNode *nextarc;    // 指向下一条边的指针
    OtherInfo info;             // 和边相关的信息
}ArcNode;
typedef struct VNode{
    VerTexType data;
    ArcNode *firstarc;          // 指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];          // AdjList 表示邻接表的类型
typedef struct{
    AdjList vertices;
    int vexnum,arcnum;          // 图的顶点数、边数
}ALGraph;

图示: \color{blue}{图示:} 图示:
在这里插入图片描述

二、算法大集合

2.1 串——BF算法

模式匹配算法,指定主串中查找的起始位置pos

算法步骤:

  • ij 表示主串S,模式串T中正待比较的字符位置,i 的初始值为posj 的初始值为1

  • 两串均未到末尾,循环执行:

    • S.ch[i]T.ch[j] 比较,相同就 ij 分别指向下一位置。

    • 若不相等,指针退后重新比较,从主串的下一字符 i=i-j+2 起重新和模式串的第一个字符 j=1 比较

  • 如果 j > T.length 说明匹配成功,返回 i - T.length;否则不成功就返回 0

int BF(SString S,SString T,int pos){
    i = pos;
    j = 1;
    while(i <= S.length && j <= T.length){
        if(S.ch[i] == T.ch[j]){
            i++;j++;
        }else{
            i = i - j + 2;    // 回溯
            j = 1;
        }
    }
    if(j > T.length){
        // 匹配成功
        return i - T.length;
    }else{
        return 0;
    }
}

BF算法的时间复杂度: \color{red}\text{BF算法的时间复杂度:} BF算法的时间复杂度:

O ( n × m ) O(n\times m) O(n×m)

2.2 串——KMP算法

KMP算法是BF算法的升级版,简化了算法的时间复杂度,即不对 i 进行回溯,使模式串自己把以前和主串一样的部分排开,从不一样的地方开始匹配。


int next[100]; // 因为后面要使用next数组,先在堆内存开辟出来,初始化为0

int KMP(SString S,SString T,int pos){
    int i = pos;
    int j = 1;

    get_next(T,next); // 调用get_next()函数,将数据存到next数组中

    while(i <= S.length && j <= T.length){
        if(S.ch[i] == T.ch[j]){
            i++;j++;
        }else{
            //i = i - j + 2;    // 回溯
            //j = 1;

            j = next[j];        // 模式串自己匹配,需要用next数组辅助,这样就知道j要去哪里匹配了
        }
    }
    if(j > T.length){
        // 匹配成功
        return i - T.length;
    }else{
        return 0;
    }
}


// 以下是求next数组
void get_next(SString T,int next[]){
    int i = 1,j = 0;
    next[1] = 0;     // 防止脏数据
    while(i < T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            next[i] = j;        // 由这里可以看出,next[2] 一定为 1 
        }else{
            j = next[j];        // 不相等了,就让 j 去回溯
        }
    }
}

例如:模式串为 a b a b a a b a b ,它的 n e x t 数组为: \color{green}{例如:模式串为ababaabab,它的next数组为:} 例如:模式串为ababaabab,它的next数组为:

j j j123456789
模式串ababaabab
next[ j j j] 0 \color{red}{0} 0 1 \color{red}{1} 1 1 \color{red}{1} 1 2 \color{red}{2} 2 3 \color{red}{3} 3 4 \color{red}{4} 4 2 \color{red}{2} 2 3 \color{red}{3} 3 4 \color{red}{4} 4

!注意: \color{red}{!注意:} !注意:

    当模式串为aaaab,在主串aaabaaaab中时,KMP的匹配效率很低,因为模式串的next数组为:01234。所以要改进next数组。

使用nextval数组来修正之前的next数组,提升KMP算法的整体效率!

算法核心思想: 最大前缀和、最大后缀和 算法核心思想:\color{red}{最大前缀和、最大后缀和} 算法核心思想:最大前缀和、最大后缀和

void get_nextval(SString T,int nextval[]){
    int i = 1,j = 0;
    nextval[1] = 0;     // 防止脏数据
    while(i < T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            if(T.ch[i] != T.ch[j])
                nextval[i] = j;
            else
                nextval[i] = nextval[j];
        }else{
            j = nextval[j];
        }
    }
}

例如:模式串为 a b a b a a b a b ,它的 n e x t v a l 数组为: \color{green}{例如:模式串为ababaabab,它的nextval数组为:} 例如:模式串为ababaabab,它的nextval数组为:

j123456789
模式串ababaabab
next[j] 0 \color{red}{0} 0 1 \color{red}{1} 1 1 \color{red}{1} 1 2 \color{red}{2} 2 3 \color{red}{3} 3 4 \color{red}{4} 4 2 \color{red}{2} 2 3 \color{red}{3} 3 4 \color{red}{4} 4
nextval[j]010104101

这样就可以替换KMP算法中的next数组了!附完整KMP代码: \color{gray}\text{这样就可以替换KMP算法中的next数组了!附完整KMP代码:} 这样就可以替换KMP算法中的next数组了!附完整KMP代码:

int nextval[100];
int next[100]; 

int KMP(SString S,SString T,int pos){
    i = pos;
    j = 1;

    get_next(T,next); 
    get_nextval(T,nextval);    

    while(i <= S.length && j <= T.length){
        if(S.ch[i] == T.ch[j]){
            i++;j++;
        }else{
            j = nextval[j];
        }
    }
    if(j > T.length){
        // 匹配成功
        return i - T.length;
    }else{
        return 0;
    }
}


void get_next(SString T,int next[]){
    int i = 1,j = 0;
    next[1] = 0;     // 防止脏数据
    while(i < T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            next[i] = j;        // 由这里可以看出,next[2] 一定为 1 
        }else{
            j = next[j];        // 不相等了,就让 j 去回溯
        }
    }
}
void get_nextval(SString T,int nextval[]){
    int i = 1,j = 0;
    nextval[1] = 0;     // 防止脏数据
    while(i < T.length){
        if(j == 0 || T.ch[i] == T.ch[j]){
            i++;
            j++;
            if(T.ch[i] != T.ch[j])
                nextval[i] = j;
            else
                nextval[i] = nextval[j];
        }else{
            j = nextval[j];
        }
    }
}

KMP算法的时间复杂度: \color{red}\text{KMP算法的时间复杂度:} KMP算法的时间复杂度:

O ( n + m ) O(n+m) O(n+m)

2.3 树——哈夫曼算法

哈夫曼树又称最优树,是一类带权路径长度最短的数

树的带权路径长度(WPL):

W P L = ∑ k = 1 n w k l k WPL=\sum\limits_{k=1}^nw_kl_k WPL=k=1nwklk

最优二叉树,哈夫曼树:WPL最小的二叉树

// 哈夫曼树的存储表示
typedef struct{
    int weight;                      // 节点的权值
    int parent,lchild,rchild;        // 节点的双亲、左孩子、右孩子
}HTNode,*HuffmanTree;

算法分析:

  • 初始化:置零,输入权值

  • 创建树:选择两个权值最小的,合并

// 选择Select
void Select(HuffmanTree &HT,int n,int &s1,int &s2){
    int min;
    for(int i = 1; i <= n; i++){
       if (HT[i].parent == 0){
             min = i;
             break;
       }
    }
    for(int i = min+1; i <= n; i++){
        if(HT[i].parent == 0 && HT[i].weight < HT[min].weight){
            min = i;
        }
    }
    s1 = min; // 第一个最小值找到
    for(int i = 1; i <= n; i++){
       if (HT[i].parent == 0 && i != s1){
             min = i;
             break;
       }
    }
    for(int i = min+1; i <= n; i++){
        if(HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1){
            min = i;
        }
    }
    s2 = min; // 第二个最小值也找到了
}


// 构建哈夫曼树
void CreatHuffmanTree(HuffmanTree &HT,int n){
    if(n <= 1) return;     // 简单的判断
    int m = 2*n -1;        // 因为有n个叶子节点,哈夫曼树就要有2n-1个节点
    HT = (HuffmanTree)malloc(sizeof(HTNode) * (m+1)) // 开辟空间,因为0号单元不用就多开一份
    for(int i = 1; i <= m; i++){     // 置零
        HT[i].parent = 0;
        HT[i].lchild = 0;
        HT[i].rchild = 0;
    }
    for(int i = 1; i <= n; i++){
        scanf("%d",&HT[i].weight);
    }
/*---------------------初始化哈夫曼树完成-----------------------*/
    for(int i = n+1; i <= m; i++){
        int s1,s2;
        Select(HT,i-1,s1,s2);    // 建立一个函数,选择两个最小的权值的索引放在s1,s2中
        HT[s1].parent = i;
        HT[s2].parent = i;
        HT[i].lchild = s1;
        HT[i].rchild = s2;
        HT[i].weight = HT[s1].weight + HT[s2].weight;
    }
}

2.4 图——深度优先搜索(DFS)

深度优先搜索(DFS)是图的遍历,类似于树的先序遍历,是树的先序遍历的推广

  • 采用 采用 采用 邻接矩阵 \color{red}{邻接矩阵} 邻接矩阵 表示的图的深度优先搜索 表示的图的深度优先搜索 表示的图的深度优先搜索
bool visited[MVNum];     // 访问标志数组,初始值为 false

void dfs(AMGraph G,int v){
    visited[v] = true;   // 先将访问的顶点设为 true
    for(int w = 0; w < G.vexnum; w++){    //依次检查邻接矩阵 v 所在的行
        if((G.arcs[v][w] != 0) && (!visited[w]))
            dfs(G,w);     // 如果 G.arcs[v][w] != 0 说明 w 是 v 的邻接点,若 w 未访问,递归调用 dfs
    }
}
  • 采用 采用 采用 邻接表 \color{red}{邻接表} 邻接表 表示的图的深度优先搜索 表示的图的深度优先搜索 表示的图的深度优先搜索
bool visited[MVNum];     // 访问标志数组,初始值为 false

void dfs(ALGraph G,int v){
    visited[v] = true;   // 先将访问的顶点设为 true
    ArcNode *p = G.vertices[v].firstarc;    // p 指向 v 的边链表的第一个边节点
    while(p!=NULL){
        int w = p->adjvex;        // 表示 w 是 v 的邻接点
        if(!visited[w]) dfs(G,w); // 如果 w 未访问,递归调用 dfs
        p = p->nextarc;           // p 指向下一个边节点
    }
}

DFS算法的时间复杂度:(n为图中顶点数,e为图中边数) \color{red}\text{DFS算法的时间复杂度:(n为图中顶点数,e为图中边数)} DFS算法的时间复杂度:(n为图中顶点数,e为图中边数)

邻接矩阵

O ( n 2 ) O(n^2) O(n2)

邻接表

O ( n + e ) O(n+e) O(n+e)

2.5 图——广度优先搜索(BFS)

广度优先算法是尽可能对横向进行搜索,需要引进队列这种数据结构

算法分析:

  • 设置一个visited数组,对访问过后的顶点进行记录。

  • 设置一个队列,使访问中的那个顶点入队列

  • 只要队列不空就一直执行以下操作:

    • 队头元素 u u u出队

    • 依次检查 u u u的所有邻接点 w w w,若visited[ w w w]的值为false,则访问。

算法实现: \color{green}{算法实现:} 算法实现:

bool visited[100];    // 对访问过后的节点进行记录。

// 入队
bool EnQueue(SqQueue &Q,int n){
    if((Q.rear+1) % MAXSIZE == Q.front){
        return false;
    }
    Q.base[Q.rear] = n;
    Q.rear = (Q.rear + 1) % MAXSIZE;
    return true;
}

// 出队
bool DeQueue(SqQueue &Q,int &n){
    if(Q.front == Q.rear){
        return false;
    }
    n = Q.base[Q.front];
    Q.front = (Q.front+1) % MAXSIZE;
    return true;
}

// 判断队列是否为空
bool QueueEmpty(SqQueue Q){
    if(Q.front == Q.rear) return false;
    else return true;
}

int FirstAdjVex(Graph G,int v){
    // 假设图使用 邻接矩阵 存储
    for(int i = 0; i < vexnum; i++){
        if(arcs[v-1][i] != 0){
            return i;
        }
    }
    return -1;
}

int NextAdjVex(Graph G,int u,int w){
    for(int i = w+1; i < vexnum; i++){
        if(arcs[u-1][i] != 0){
            return i;
        }     
    }
    return -1;
}

void bfs(Graph G,int v){
    SqQueue Q;        // 声明一个队列
    InitQueue(Q);     // 初始化队列
    visited[v] = true;// 访问的顶点被标记
    EnQueue(Q,v);     // 顶点 v 入队
    while(!QueueEmpty(Q)){
        // 队列非空
        int u;        // 记录要出队的顶点
        DeQueue(Q,u); // 队头出队,记为 u
        for(int w = FirstAdjVex(G,v); w >= 0; w = NextAdjVex(G,u,w)){
            // FirstAdjVex(G,v) 表示 u 的第一个邻接点
            // NextAdjVex(G,u,w) 表示 u 相对于 w 的下一个邻接点
            if(!visited[w]){
                printf("%d ",w);
                visited[w] = true;
                EnQueue(Q,w);
            }
        }
    }
}

BFS算法的时间复杂度:(n为图中顶点数,e为图中边数) \color{red}\text{BFS算法的时间复杂度:(n为图中顶点数,e为图中边数)} BFS算法的时间复杂度:(n为图中顶点数,e为图中边数)

邻接矩阵

O ( n 2 ) O(n^2) O(n2)

邻接表

O ( n + e ) O(n+e) O(n+e)

2.6 图——普利姆算法

    **普利姆算法是用来解决最小生成树的一种算法**

此算法不考代码实现,只要求手算就行了 \color{red}{此算法不考代码实现,只要求手算就行了} 此算法不考代码实现,只要求手算就行了

//返回顶点在邻接矩阵中对应的下标
int LocateVex(AMGraph& G, char *v1)
{
    for (int i = 0; i<G.vexnum; i++)
    {
        if (v1 == G.vexs[i])
            return i;
    }
}
void CreateUDN(AMGraph& G)
{
    int i, j, k,w;
    string v1,v2;
    cin >> G.vexnum >> G.arcnum;        //输入总顶点数和总边数
    for (i = 0; i < G.vexnum; ++i)
        cin >> G.vexs[i];        //依次输入点的信息***顶点数组
    for (i = 0; i < G.vexnum; ++i)
        for (j = 0; j < G.vexnum; ++j)
            G.arcs[i][j] = MaxInt;        //初始化邻接矩阵,使每个权值初始化为极大值
    for (k = 0; k < G.arcnum; ++k)        //构造邻接矩阵
    {
        cin >> v1 >> v2 >> w;            //输入一条边依附的顶点及其权值
        i = LocateVex(G, &v1[0]);            //确定v1和v2在G中的位置,即顶点数组的下标
        j = LocateVex(G, &v20]);
        G.arcs[i][j] = w;                //边<v1,v2>的权值置为w
        G.arcs[j][i] = G.arcs[i][j];    //置<v1,v2>的对称边<v2,v1>权值为w
    }
}
struct closedge
{
    VerTexType adjex;//顶点 
    ArcType lowcost;//权值 
 }closedge[MVNum];//该数组用来记录从顶点集U到U-V的权值的最小的边 
int Min(struct closedge *p,int G)
{
    int min=100,k;
    for(int i=0;i<G;i++)
    {
        if(p[i].lowcost!=0&&p[i].lowcost<min)
        {
            min=p[i].lowcost;
            k=i;
            }    
    }
    return k;
}
void show(AMGraph G)
{
    for (int i = 0; i < G.vexnum; i++)
    {    
        for (int j = 0; j < G.vexnum; j++)
        {
        //    if (G.arcs[i][j] == MaxInt)
                cout << G.arcs[i][j] << "\t";
        ///    else
        //        cout << G.arcs[i][j] << "\t";
        }
    cout << endl<<endl;
    }
}
//普利姆算法 
void Prim(AMGraph G,char *u)//u为起始顶点 
{
    int k,j,i;
    string u0,v0;
    k=LocateVex(G,&u[0]);//k存储u的下标  
    for(j=0;j<G.vexnum;++j)//对U-V的每一个顶点初始化 
        if(j!=k)
            closedge[j]={u,G.arcs[k][j]};//初始化 
        closedge[k].lowcost=0;
    for(i=1;i<G.vexnum;++i)
    {//其余的n-1个顶点生成n-1条边 
        k=Min(closedge,G.vexnum);//找出边上权最小的边对应的另一个顶点的下标 
        u0=closedge[k].adjex;//找出最小边在U中的点的下标 
        v0=G.vexs[k];//找出最小边在U-V中的点的下标 
        cout<<u0<<"——>"<<v0<<" "<<endl;//输出两点 
        closedge[k].lowcost=0;//初始化,相当于是标记已访问过了 
        for(j=0;j<G.vexnum;++j)
            if(G.arcs[k][j]<closedge[j].lowcost)//找已经存在U中的最小权 
                closedge[j]={G.vexs[k],G.arcs[k][j]};
    }
}

普利姆算法的时间复杂度: \color{red}\text{普利姆算法的时间复杂度:} 普利姆算法的时间复杂度:

O ( n 2 ) O(n^2) O(n2)

普利姆算法适用于求稠密图的最小生成树 \color{gray}{普利姆算法适用于求稠密图的最小生成树} 普利姆算法适用于求稠密图的最小生成树

2.7 图——克鲁斯卡尔算法

    **克鲁斯卡尔算法也是求最小生成树的一种算法**

此算法不考代码实现,只要求手算就行了 \color{red}{此算法不考代码实现,只要求手算就行了} 此算法不考代码实现,只要求手算就行了

#include<iostream>
#include<stdio.h>
using namespace std; 
typedef char VerTexType; 
typedef int ArcType; 
#define MaxInt 32767 
#define MVNum 100
#define ArcNum 100
#define OK 1
#define ERROR -1
int Vexset[MVNum];//辅助数组表示连通分量 
typedef int status;
typedef struct{
    VerTexType vexs[MVNum] {'A','B','C','D','E','F'};
    ArcType arcs[MVNum][MVNum];
    int vexnum = 6,arcnum = 10;
}AMGraph; 

typedef struct{
    VerTexType Head;//起点 
    VerTexType Tail;//终点
    ArcType lowcast; 
}Edge[ArcNum];

status CreateUDN(AMGraph &G){//创建无向图     
    for(int i=0;i<G.vexnum;i++){
        for(int j=0;j<G.vexnum;j++){
            if(i==j){
                G.arcs[i][j] = 0;
            }else
                G.arcs[i][j] = MaxInt;//初始状态全部节点之间相互不可达
        }
    }
    G.arcs[0][1]=6;G.arcs[0][2]=1;G.arcs[0][3]=5;
    G.arcs[1][2]=5;G.arcs[1][4]=3;
    G.arcs[2][3]=5;G.arcs[2][4]=6;G.arcs[2][5]=4;
    G.arcs[3][5]=2;
    G.arcs[4][5]=6;
    for(int i=0;i<G.vexnum;i++){
        for(int j=i+1;j<G.vexnum;j++){
            if(G.arcs[i][j]!=MaxInt){
                G.arcs[j][i] = G.arcs[i][j];
            } 
        }
    }//矩阵对称 
    return OK; 
}

void ShowGraph(AMGraph G){
    cout<<" ";
    for(int i=0;i<G.vexnum;i++){
        cout<<" "<<G.vexs[i];
    }
    cout<<endl;
    for(int i=0;i<G.vexnum;i++){
        cout<<G.vexs[i]<<" ";
        for(int j=0;j<G.vexnum;j++){
            if(G.arcs[i][j]==MaxInt){
                cout<<"* ";
            }else{
                cout<<G.arcs[i][j]<<" ";
            }
        }
        cout<<endl;
    }
}

int LocateVex(AMGraph G, VerTexType v){
    int i;
    for(i=0;i<G.vexnum;i++){
        if(G.vexs[i]==v){
            return i;
        }
    } 
    return ERROR;
}

VerTexType Transform(AMGraph G, int vn){
    return G.vexs[vn]; 
}

void InitailEdge(AMGraph G,Edge &edge){//初始化边表 
    int arcnum = 0;
    for(int i=0;i<G.vexnum;i++){//纵列为起点 
        for(int j=i+1;j<G.vexnum;j++){//横行为终点 
            if(G.arcs[i][j]!=MaxInt&&G.arcs[i][j]!=0){
                edge[arcnum].Head = Transform(G,i);
                edge[arcnum].Tail = Transform(G,j);
                edge[arcnum].lowcast = G.arcs[i][j];
                arcnum++;
            }
        }
    } 
}

void sort(AMGraph G,Edge &edge){
    VerTexType tv;
    ArcType tl;
    for(int i=0;i<G.arcnum;i++){
        for(int j=0;j<G.arcnum-i-1;j++){
            if(edge[j].lowcast>edge[j+1].lowcast){
                tv = edge[j].Head;
                edge[j].Head = edge[j+1].Head;
                edge[j+1].Head = tv;

                tv = edge[j].Tail;
                edge[j].Tail = edge[j+1].Tail;
                edge[j+1].Tail = tv;

                tl = edge[j].lowcast;
                edge[j].lowcast = edge[j+1].lowcast;
                edge[j+1].lowcast = tl;
            }
        }
    }
}

void ShowEdge(AMGraph G,Edge edge){
    for(int i=0;i<G.arcnum;i++){
        cout<<edge[i].Head<<"-"<<edge[i].lowcast<<"-"<<edge[i].Tail<<endl;
    }
}

void ShowVexset(AMGraph G){
    for(int i=0;i<G.vexnum;i++){
        cout<<Vexset[i]<<" ";
    } 
    cout<<endl;
}

void Kruskal(AMGraph &G){
    Edge edge;
    InitailEdge(G,edge); 
//    ShowEdge(G,edge);
    sort(G,edge);
//    ShowEdge(G,edge);
    for(int i=0;i<G.vexnum;i++){
        Vexset[i] = i;//每个节点自成一个分量 
    }
    int headi,taili;//边起点的下标、边终点的下标 
    int headt,tailt;//操作连通分量时的中间量 
    for(int i=0;i<G.arcnum;i++){
        headi = LocateVex(G,edge[i].Head); //起点下标 
        taili = LocateVex(G,edge[i].Tail); //终点下标 
        headt = Vexset[headi];//获取起点的连通分量 
        tailt = Vexset[taili];//获取终点的连通分量 
        if(headt!=tailt){//如果两个点不是同一个连通分量
            cout<<edge[i].Head<<"-"<<edge[i].lowcast<<"-"<<edge[i].Tail<<endl;
            for(int j=0;j<G.vexnum;j++){
                if(Vexset[j]==headt){//更新Vexset数组,把改起点的连通分量改成和终点连通分量一致(其实就是合并连通分量) 
                    Vexset[j] = tailt;
//                    ShowVexset(G);
                }
            }
        }
    }     
}

int main(){
    AMGraph G;
    CreateUDN(G);
    ShowGraph(G);
    Kruskal(G); 
    return 0;
} 

克鲁斯卡尔算法的时间复杂度:(e表示边的条数) \color{red}\text{克鲁斯卡尔算法的时间复杂度:(e表示边的条数)} 克鲁斯卡尔算法的时间复杂度:(e表示边的条数)

O ( e log e ) O(e\text{log}e) O(eloge)

克鲁斯卡尔算法适用于求稀疏图的最小生成树 \color{gray}{克鲁斯卡尔算法适用于求稀疏图的最小生成树} 克鲁斯卡尔算法适用于求稀疏图的最小生成树

2.8 图——迪杰斯特拉算法

    **迪杰斯特拉算法,典型的计算最短距离的算法,很有用的!但是只能求非负权值的图**

算法实现: \color{red}{算法实现:} 算法实现:

  • 需要引入的数据结构:

    • 一维数组visited:记录已经被确定的最短路径长度,true表示确定

    • 一维数组path:记录当前最短路径上 v i v_i vi的直接前驱编号,其初始值为-1

    • 一维数组val:记录从源点到终点 v i v_i vi的当前最短路径长度,其初始值为源点到各点 v i v_i vi的权值

算法步骤: \color{red}{算法步骤:} 算法步骤:

  • 初始化:

    • 将源点 v 0 v_0 v0加入visited数组中,即viisited[ v 0 v_0 v0] = true

    • v 0 v_0 v0到各个终点的最短路径初始化为权值,即val[ i i i] = G.arcs[ v 0 v_0 v0][ v i v_i vi]

    • v 0 v_0 v0和顶点 v i v_i vi之间有弧,则将 v i v_i vi的前驱置为 v 0 v_0 v0,即path[ i i i] = v 0 v_0 v0,否则path[ i i i] = -1

  • 循环n-1次,执行下列操作

    • 选择下一条最短路径的终点 v k v_k vk,使得:

      v a l [ k ] = m i n { D [ i ] ∣ v i ∈ V − S } val[k]=min\lbrace D[i]|v_i\in V-S\rbrace val[k]=min{D[i]viVS}

    • v k v_k vk加到visited中,即visited[ v k v_k vk] = true

    • 根据条件更新最短路径长度val,若条件val[ k k k] + G.arcs[ k k k][ i i i] < val[ i i i],则更新val[ i i i] = val[ k k k] + G.arcs[ k k k][ i i i],同时更改 v i v_i vi的前驱为 v k v_k vk,path[ i i i] = k k k

算法实现: \color{green}{算法实现:} 算法实现:

void Dijkstra(AMGraph G,int v0){
    int n = G.vexnun;             // n为G中的顶点数
    /*---------初始化--------*/
    bool visited[n];
    int val[n];
    int path[n];
    for(int i = 0; i < n; i++){
        visited[i] = false;
        val[i] = G.arcs[v0][i];
        if(val[i] < MaxInt) path[i] = v0;    // 初始化,让和v0连通的所有顶点的前驱都为v0
        else path[i] = -1;
    }
    /*-------初始化完成------*/
    for(int i = 1; i < n; i++){    // 循环 n-1次
        int min = MaxInt;        // 声明最小值
        for(int w = 0; w < n; w++){    
            if(!visited[w] && val[w] < min){    // 先找一个最短路径的顶点
                v = w;
                min = val[w];
            }
        }
        visited[v] = true;
        for(int w = 0; w < n; w++){
            if(!visited[w] && (val[v] + G.arcs[v][w] < val[w])){
                val[w] = val[v] + G.arcs[v][w];        // 更新最短距离
                path[w] = v;                           // 更新前驱
            }
        }
    }
}

例 \color{gray}{例}

给定邻接矩阵求 v 0 v_0 v0到各点的距离

[ ∞ ∞ 10 ∞ 30 100 ∞ ∞ 5 ∞ ∞ ∞ ∞ ∞ ∞ 50 ∞ ∞ ∞ ∞ ∞ ∞ ∞ 10 ∞ ∞ ∞ 20 ∞ 60 ∞ ∞ ∞ ∞ ∞ ∞ ] \begin{bmatrix}\infin&\infin&10&\infin&30&100\\\infin&\infin&5&\infin&\infin&\infin\\\infin&\infin&\infin&50&\infin&\infin\\\infin&\infin&\infin&\infin&\infin&10\\\infin&\infin&\infin&20&\infin&60\\\infin&\infin&\infin&\infin&\infin&\infin\end{bmatrix} 1055020301001060

  • 初始化结果 \color{red}{初始化结果} 初始化结果
v012345
visitedtruefalsefalsefalsefalsefalse
val0 ∞ \infin 10 ∞ \infin 30100
path-1-10-100
  • 求解过程中参数变化 \color{red}{求解过程中参数变化} 求解过程中参数变化
终点 i = 1 i=1 i=1 i = 2 i=2 i=2 i = 3 i=3 i=3 i = 4 i=4 i=4 i = 5 i=5 i=5
v 1 v_1 v1 ∞ \infin ∞ \infin ∞ \infin ∞ \infin ∞ \infin
v 2 v_2 v210( v 0 , v 2 v_0,v_2 v0,v2)
v 3 v_3 v3 ∞ \infin 60( v 0 , v 2 , v 3 v_0,v_2,v_3 v0,v2,v3)50( v 0 , v 4 , v 3 v_0,v_4,v_3 v0,v4,v3)
v 4 v_4 v430( v 0 , v 4 v_0,v_4 v0,v4)30( v 0 , v 4 v_0,v_4 v0,v4)
v 5 v_5 v5100( v 0 , v 5 v_0,v_5 v0,v5)100( v 0 , v 5 v_0,v_5 v0,v5)90( v 0 , v 4 , v 5 v_0,v_4,v_5 v0,v4,v5)60( v 0 , v 4 , v 3 , v 5 v_0,v_4,v_3,v_5 v0,v4,v3,v5)
v k v_k vk v 2 v_2 v2 v 4 v_4 v4 v 3 v_3 v3 v 5 v_5 v5
pathpath[3] = 2path[3] = 4,path[5] = 4path[5] = 3
visited{ v 0 , v 2 v_0,v_2 v0,v2}{ v 0 , v 2 , v 4 v_0,v_2,v_4 v0,v2,v4}{ v 0 , v 2 , v 4 , v 3 v_0,v_2,v_4,v_3 v0,v2,v4,v3}{ v 0 , v 2 , v 4 , v 3 , v 5 v_0,v_2,v_4,v_3,v_5 v0,v2,v4,v3,v5}
  • 最终结果 \color{red}{最终结果} 最终结果
v012345
visitedtruefalsetruetruetruetrue
val0 ∞ \infin 10503060
path-1-10403

迪杰斯特拉算法的时间复杂度: \color{red}\text{迪杰斯特拉算法的时间复杂度:} 迪杰斯特拉算法的时间复杂度:

O ( n 2 ) O(n^2) O(n2)

2.9 图——弗洛伊德算法

    **求最短路径的第二种方式——弗洛伊德算法,但是它可以求带负权值的图**

算法实现: \color{red}{算法实现:} 算法实现:

  • 引入的数据结构:

    • 二维数组path[ i i i][ j j j]:最短路径上顶点 v j v_j vj的前一项顶点的序号

    • 二维数组val[ i i i][ j j j]:记录顶点 v i v_i vi v j v_j vj之间的最短路径长度

void Floyd(AMGraph G){
    for(int i = 0; i < G.vexnum; i++){
        for(int j = 0; j < G.vexnum; j++){
            val[i][j] = G.arcs[i][j];    // 初始化
            if(val[i][j] < MaxInt && i != j) path[i][j] = i;    // 初始有边的顶点的前驱
            else path[i][j] = -1;    // 无边就直接为-1
        }
    }
    for(int k = 0; k < G.vexnum; k++){
        for(int i = 0; i < G.vexnum; i++){
            for(int j = 0; j < G.vexnum; j++){
                if(val[i][k] + val[k][j] < val[i][j]){
                    val[i][j] = val[i][k] + val[k][j];
                    path[i][j] = path[k][j];
                }
            }
        }
    }
}

弗洛伊德算法的时间复杂度: \color{red}\text{弗洛伊德算法的时间复杂度:} 弗洛伊德算法的时间复杂度:

O ( n 3 ) O(n^3) O(n3)

2.10 查找——顺序查找

数据结构定义 : \color{red}{数据结构定义:} 数据结构定义:

// 数据元素定义
typedef struct{
    KeyType key;
    InfoType otherinfo;
}ElemType;

// 顺序表
typedef struct{
    ElemType *R;
    int length;
}SSTable;
**普通顺序查找**
int Search(SSTable ST,KeyType key){
    for(int i = ST.length; i >= 1; i--){    // 闲置ST.R[0]不用
        if(ST.R[i] == key){
            return i;    // 返回位置
        }
    }
    return 0;    // 未找到返回0
}
**设置监视哨的顺序查找**
int Search(SSTable ST,KeyType key){
    ST.R[0].key = key;
    for(int i = ST.length; ST.R[i].key != key; i--);
    return i;
}

算法的平均查找长度 ( A S L ) : \color{gray}{算法的平均查找长度(ASL):} 算法的平均查找长度(ASL)

A S L = 1 n ∑ i = 1 n i = n + 1 2 ASL=\dfrac{1}{n}\sum\limits_{i=1}^ni=\dfrac{n+1}{2} ASL=n1i=1ni=2n+1

算法的时间复杂度: \color{gray}{算法的时间复杂度:} 算法的时间复杂度:

O ( n ) O(n) O(n)

2.11 查找——折半查找

折半查找也称二分查找,效率较高,但是要求表必须是线性表且已经排好序。

int Search_Bin(SSTable ST,KeyType key){
    int low = 1;
    int high = ST.length;    // 设置查找区间
    while(low <= high){
        int mid = (low + high) / 2;
        if(key == ST.R[mid].Key) return mid;
        else if(key < ST.R[mid].Key) high = mid - 1;
        else low = mid + 1;
    }
}

算法的平均查找长度 ( A S L ) : \color{gray}{算法的平均查找长度(ASL):} 算法的平均查找长度(ASL)

A S L = n + 1 n log 2 ( n + 1 ) − 1 ASL=\dfrac{n+1}{n}\text{log}_2(n+1)-1 ASL=nn+1log2(n+1)1

算法的时间复杂度: \color{gray}{算法的时间复杂度:} 算法的时间复杂度:

O ( log n ) O(\text{log}n) O(logn)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值