数据结构总结

来源:互联网 发布:淘宝上的止鼾器有用吗 编辑:程序博客网 时间:2024/06/05 15:08

数组:

#include<iostream>using namespace std;#define LIST_INIT_SIZE 100 #define LISTINCREMENT 10    //decrement 消耗 减量#define ElemType int#define OK 1#define ERROR 0typedef struct{    ElemType *elem;    int length;    int listsize;}SqList;//数组的结构,数组作为这个结构体的一项,定义了2个标志数组属性的项(*  ̄3)(ε ̄ *)int InitList_Sq(SqList &L){    L.elem = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));    if (!L.elem)exit(OVERFLOW);    L.length = 0;    L.listsize = LIST_INIT_SIZE;    return OK;}//创建数组int InitList_Sq(SqList &L){    L.elem = new ElemType[LIST_INIT_SIZE];    if (!L.elem)exit(OVERFLOW);    L.length = 0;    L.listsize = LIST_INIT_SIZE;    return OK;}//创建数组,先初始化L.elem然后L.length,L.listsizeint ListInsert_Sq(SqList &L, int i, ElemType e)//插入的数组,位置,数据(这里的位置是第i个元素之前也就是下标为i-1的元素){    ElemType *newbase;    ElemType * q;                                      //引入指针q的目的是标记第i个元素位置,后面引入指针p为了指向    if (i<1 || i>L.length+1)//i判断是否合法          return ERROR;    if (L.length >= L.listsize)//存储空间满,重新初始化elem,listsize,(length在插入之后更新)(相等也要申请)    {        newbase = (ElemType*)realloc(L.elem, (L.listsize + LISTINCREMENT)*sizeof(ElemType));//这里完全不需要定义newbase为了下面判断空间是否申请成功才定义了这个变量        if (!newbase)            exit(OVERFLOW);        L.elem = newbase;        L.listsize += LISTINCREMENT;    }    q = &(L.elem[i - 1]);//取下标i的元素地址(也就是第i-1个元素)(下面用>=来标志结束)(这里的原本值需要后移,把值插入到这里)    for (ElemType *p = &(L.elem[L.length - 1]); p >= q; --p)//length-1为最后一位的下标   最后一位到i,被指针指到的数据都移动        *(p + 1) = *p;    *q = e;//放入数据    ++L.length;    return OK;}//插入:检查有效,判断越界,更新listsize,更新elem(定义指针位置,循环移动),更新lengthint ListDelete_Sq(SqList &L, int i,ElemType &e){    if (i<1 || i>L.length)exit(ERROR);//判断有效,和刚才不同这里不是插入    ElemType *p = &(L.elem[i - 1]);//p指向第i个元素    e = *p;    ElemType *q = L.elem + L.length - 1;//q指向最后一个元素    for (++p; p <= q; ++p)//进入循环时指针指向了第i+1个元素,同上一个相同移动的是指针指到的内容(包括首和尾)for(p;p<=q;p++)2个指针指向的内容都会移动    {        *(p - 1) = *p;    }    --L.length;    return OK;}//和添加类似流程:检查有效,定义指针指向位置,循环移动,更新length,删除即数组整体移动覆盖空出的空间最后deleteint LocateElem_Sq(SqList L, ElemType e, int(*compare)(ElemType, ElemType))//用函数指针调用cmpare函数(cmpare可以在该函数外自己重载){    int i = 1;//用i来表示第i个元素,初始为1    ElemType *p = L.elem;//用p来指向每一个元素,初始p指向第一个元素,p和i是同步的    while (i <= L.length&&!(*compare)(*p++, e))++i;//i指向的每一位(包括头和尾)都会遍历,这里用p++先执行比较p之后p才会指向下一个,断掉的时候i为该数组第i个元素    if (i <= L.length)return i;    else return 0;}//用同步的2个值i和p分别表示第i个元素和指向第i个元素的指针pvoid MergeList_Sq(SqList &La, SqList &Lb, SqList &Lc)//合并,把2个数组合并为一个{    ElemType *pa = La.elem;  //定义指针a    ElemType *pb = Lb.elem;  //定义指针b    Lc.listsize = La.listsize + Lb.listsize;    ElemType *pc;            //定义指针c    Lc.elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));    if (!Lc.elem)exit(OVERFLOW);    pc = Lc.elem;//定义指针c    ElemType *pa_last = La.elem+Lb.length-1;    ElemType *pb_last = Lb.elem + Lb.length - 1;//定义指向a,b末尾的指针    while (pa <= pa_last&&pb <= pb_last)//相等遍历到结尾,需要操作结尾所以有等号    {        if (*pa <= *pb)*pc++ = *pa++;//pa++为先执行再+        else *pc++ = *pb++;    }    while (pa <= pa_last)*pc++ = *pa++;//这里是如果b先完了之后就直接把a直接给c    while (pb <= pb_last)*pc++ = *pb++;}//定义三个指针初始化为首地址,定义2个指针指向结尾作为结束的标志,循环比较/*总之这里的i表示的是第几个而不是下标,for(p;p<=q;p++)p和q指向的所有包括两头边界都会被操作,查找用i<=length||查到作为结束的标志用p和i同步的指向数组的各个位置。用p++来遍历数组,用++i来给数据+1*/

链表:

#include<iostream>using namespace std;#define ElemType int#define ERROR 0#define OK 1typedef struct LNode{    ElemType data;    struct LNode *next;}LNode,*LinkList;//把链表构造为一个结构体数据部分,指针部分,注意整个链表是由一个一个结构体作为节链接成的(这和数组不同)指针数据类型为LNode的指针是为能指向下一个链表int GetElem_L(LinkList L, int i, ElemType &e)//扫描链表,把返回值给e 注:查找函数用一地址作为缓冲区{    LinkList p = L->next;//令p指向链表第一项    int j = 1;//定义j作为计数器,二者同步    while (p&&j < i)//这里的p=p->next就相当于++p    {        p = p->next; ++j;    }//之前说从p指向的位置遍历到q指向的位置(把前后边界都操作)用for(p;p<=q;++q)这里不需要操作边界(这里的操作是p=p->next不能让它再走下去)所以这里没用=    if (!p || j>i)return ERROR;    e = p->data;    return OK;}//取出链表中第i个元素放到缓冲区e中int ListInsert_L(LinkList &L, int i, ElemType e)//在L的第i个元素之前插入e{    LinkList p = L; int  j = 0; //初始化指针和,数 同步为指向链表第0个    while (p&&j < i - 1)//查找第i-1个链节    {        p = p->next;        ++j;    }    if (!p || j>i - 1)return ERROR;//j>i-1是    LinkList s = (LinkList)malloc(sizeof(LNode));//这里的LinkList=LNode*    s->data = e; s->next = p->next; p->next = s;//先一走p->next后赋值给p->next    return OK;}//插入:先查找,查找的目标是第i-1个链节int ListDelete_L(LinkList &L, int i, ElemType &e)//删除L的第i个元素返回到缓冲区e中{    LinkList p = L; int j = 0;    while (p->next&&j < i - 1)//查找到的是第i-1个元素    {        p = p->next; ++j;    }    if (!p->next || j>i - 1)return ERROR;    LinkList q = p->next; p->next = q->next;//先取出第i个元素然后删除    e = q->data;    free(q);    return OK;}//删除:查找,取出对应链节,把链节从链表上删除,把链节从内存中删除void CreateList_L(LinkList &L, int n)//输入{    L = (LinkList)malloc(sizeof(LNode));//申请第一个链节空间    L->next = NULL;//让链表的尾部指针指向NULL    LinkList p;    for (int i = n; i > 0; --i)//i=0的时候已经赋值为空,不需要再操作,这里用i>0    {        p = (LinkList)malloc(sizeof(LNode));        scanf("%d", &(p->data));//这里需要把p->data格式化为int型(默认为char型)        p->next = L->next; L->next = p;//先把L->next移走,然后把p连接到L->next,插入到表头,不动第0个链接    }}//建立链表:建立第零个链节,申请空间建立后面的空间void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc)//已知单链线性链表La,Lb元素按值递减排列,归并两链表{    LinkList pa = La->next;    LinkList pb = La->next;    LinkList pc;//定义遍历链表的3个指针    Lc = pc = La;//先让Lc的开头指向La开头    while (pa&&pb)//用NULL作为判断链表结束的标志    {        if (pa->data <= pb->data)        {            pc->next = pa; pc = pa; pa = pa->next;//Lc后面接pa,pc后移,pa后移        }        else{ pc->next = pb; pc = pb; pb = pb->next; }    }    pc->next = pa ? pa : pb;//用pa->next=pa来判判断pa是否结束    free(Lb);//释放链表的头结点,只是头结点}//链表组合:创建3个指针指向三链表的头,遍历链表比较插入,填入剩余/*for(int i=1;i<=length;i++),for(int *p=List;p<=(List+length);++p)这是总的遍历数组的方式,总之都是一个指向头一个指向尾用<=作为判断条件里面的每一个元素都会执行循环的操作,所以对于插入和删除需要用等号而查找不能用再总结:从一个指针或者标志,到另一个指针或标志判断的条件若有=则表示2个边界都会操作*//*链表查找方式:while(p&&j<i){p=p->next;++j}这里的p和j需要同步(初始化为p=L;j=0;或者p=L->next;j=1)*/

一元多项式:

#include<iostream>#define EQUAL 0#define BELOW -1#define ABAVE 1using namespace std;typedef struct{    float coef;//系数    int expn;//指数}term,ElemType;typedef struct LNode{    ElemType data;    struct LNode *next;}*LinkList,LNode;typedef LinkList polynomial;bool InitList(polynomial &P){    P = new LNode;    if (P != NULL)return 1;    else return 0;}LinkList GetHead(polynomial P){    if (!P)exit(0);    else return P;}void SetCurElem(LinkList&h, ElemType e){    h->data.coef = e.coef;    h->data.expn = e.expn;}bool Cmp(polynomial P, ElemType e){    while (P)    {        if (P->data.expn == e.expn)return 1;//找到返回1        P = P->next;    }    return 0;//没有找到返回0}bool MakeNode(LinkList &s, ElemType e){    if (InitList(s) != NULL){        SetCurElem(s, e); return 1;    }    else return 0;}int Cmp(int a, int b){    if (a == b)return EQUAL;    if (a > b)return ABAVE;    if (a < b)return BELOW;}void InsertFirst(LinkList &q, LinkList &s){    s->next = q->next;    q->next = s;}void CreatPolyn(polynomial &P, int m){    InitList(P);    LinkList s,q;    LinkList h = GetHead(P);    ElemType e;    e.coef = 0.0;    e.expn = -1;    SetCurElem(h, e);    for (int i = 0; i <= m; ++i)    {        scanf("%d   %d", e.coef, e.expn);        if (Cmp(P,e)==0)        {            if (MakeNode(s, e)!=NULL) InsertFirst(h->next, s);        }    }}void AddProlyn(polynomial &Pa, polynomial &Pb){    polynomial ha = GetHead(Pa);//ha指向前驱    polynomial hb = GetHead(Pb);    LinkList qa = ha->next; LinkList qb = hb->next;//qa指向当前    while (qa&&qb)    {        ElemType a = qa->data; ElemType b = qb->data;        switch (Cmp(a.expn, b.expn))        {        case EQUAL :            float sum=a.coef+b.coef;            if (sum != 0.0){ qa->data.coef = sum;            ha = qa; }            else {}        case BELOW:        }    }}

栈:

#include<iostream>#define SElemType int#define STACK_INIT_SIZE 100#define STACKINCREMENT 10;using namespace std;typedef struct{    SElemType *base;    SElemType *top;    int stacksize;}SqStack;int InitStack(SqStack &S){    S.base = new SElemType[STACK_INIT_SIZE];    if (!S.base)exit(0);    S.top = S.base;    S.stacksize = STACK_INIT_SIZE;    return 1;}//创造空栈:申请空间,定栈底,定栈顶(栈顶和栈底指向同一位置),定sizeint GetTop(SqStack S, SElemType &e){    if (S.top == S.base)return 1;    e = *(S.top-1);//这里值得注意,top指向的是栈顶数据的下一位    return 0;}//用缓冲区e存放获取到的数据,用返回值判断操作是否成功。取元素:判栈空,返回int Push(SqStack &S, SElemType e){    if (S.top - S.base >= S.stacksize)//这里top-base==size时栈满    {        S.base = (SElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT)*sizeof(SElemType));        if (!S.base)exit(1);        S.top = S.base + S.stacksize;        S.stacksize += STACKINCREMENT;    }    *S.top++ = e;    return 0;}

串的匹配—KMP算法

#include<iostream>using namespace std;int next[20];void get_next(string T, int next[]){    int i = 1; next[1] = 0; int j = 0;    while (i < T.length())    {        if (j == 0 || T[i] == T[j]){ ++i; ++j; next[i] = j; }        else j = next[j];    }}//设置i和j偏移1第一位设置为0,(如果T[i]!=T[j]用2次循环实现:1、j+1,i+12. j返回0。)(如果相等:1.++i,++j。2.更新next的对应位置)\//因为后面是i和j是先加的所以循环的while不需要有=//i和j分别指向开头和结尾的比较的位置处,相等j就向下加,不相等就让j前移直到相等或者j==0int Inde_KMP(string S, string T,int pos,int next[]){    int i = pos;    int j = 1;    while (i <= S.length() && j <= T.length())    {        if (j == 0 || S[i] == T[j]){ ++i; ++j; }        else j = next[j];    }    if (j > T.length())return i - T.length;//这里要返回的值是i-T.length()   j=T.length()时也需要比较    else return 0;}//不等i不动,j返回到对应的next位置处,相等就继续向下比较

三元组存储的矩阵转置

#include<iostream>using namespace std;#define MAXSIZE 12500#define ElemType inttypedef struct{    int i, j;//该元素的行下标和列下标    ElemType e;}Triple;//数据结构体typedef struct{    Triple data[MAXSIZE + 1];    int mu, nu, tu;//矩阵行数,列数,和非零元个数}TSMatrix;//zhint TransposeSMatrix(TSMatrix M, TSMatrix &T){//求用三元组存储的稀疏矩阵M的转置矩阵T    T.mu = M.mu; T.nu = M.nu; T.tu = M.tu;    if (T.tu)    {        int q = 1;        for (int col = 1; col <= M.nu; ++col)//扫描列        {            for (int p = 1; p <= M.tu; ++p)//扫描非零元素,这里用tu而不是mu因为用mu会有元素访问不到            {                if (M.data[p].j == col)//数组的列号和循环到的列号对应的时候才会运算(其他时候不运算,浪费了时间)                {                    T.data[q].i = M.data[p].j;//行号给列号                    T.data[q].j = M.data[p].i;//列号给行号                    T.data[q].e = M.data[p].e;//元素复制                    ++q;//q是在这里加的能够确保矩阵能全部循环                }            }        }    }    return 1;}/*矩阵转置核心代码:T.data[q].i=M.data[p].j;T.data[q].i=M.data[p].j;T.data[q].e=M.data[p].e;   外层循环决定列,内层循环决定行ya压缩矩阵转置方式之一是先解压缩再转置,对应转置。(这里需要实现换mu,nu,tu.换i,j,e。调整更改后的数组为有序)*/int TransposeSMatrix(TSMatrix M, TSMatrix &T){    T.mu = M.mu; T.nu = M.nu; T.tu = M.tu;    int num[100];    if (T.tu)    {        for (int col = 1; col <= M.nu; ++col)num[col] = 0;        for (int t = 1; t <= M.tu; ++t)++num[M.data[t].j];//求每一列含非零元个数求非零元素的个数:for(int t=1;t<M.tu;++t)++num[M.data[t].j];        int cpot[100];        cpot[1] = 1;//把第一行的开头定为1而不是0        for (int col = 2; col < M.nu; ++col)cpot[col] = cpot[col - 1] + num[col - 1];//求第col列中第一个非零元素在b.data中的序号        for (int p = 1; p <= M.tu; ++p)//从M.data中顺序取存到T.data对应顺序位置中(这里的对应位置就是cpot所标注的位置)        {            int col;            col = M.data[p].j;            int q = cpot[col];            T.data[q].i = M.data[p].j;            T.data[q].j = M.data[p].i;            T.data[q].e = M.data[p].e;            ++cpot[col];//这个值是在数组中跳着取值,然后++。最终把所有cpot[col]都+到该列的尾        }    }    return 1;}/*主体思路:可以为M.data标注上一个每列开始元素的位置(引入了cpot)这样每次循环结束只需要把该列的cpot+1就能指向该列的元素而求出cpot就要知道每列多少个元素num(初始化为1(这里数组的0位置处都是没有值的))最终用有序的p依次循环赋值到根据cpot取值的T[q]里*/

二叉树

#include<iostream>#include<string>#define TElemType intusing namespace std;typedef struct BiTNode{    TElemType data;    struct BiTNode *lchild, *rchild;//左右孩子指针}BiTNode,*BiTree;int PreOrderTraverse(BiTree T,int (* Visit)(TElemType e))//二叉树遍历的递归算法{//这里的Visit为形参,具体值看调用的是什么函数,用指针来实现以形参的方式调用函数,涨姿势了O(∩_∩)O    if (T)//二叉树不为空    {        if (T==NULL)return 0;//这里是T而不是T->lchild必须介入节点后再判断该节点,如果判断该节点的下一个节点会导致该节点返回那么接下来对该节点的操作将不能实现        //我觉得递归调用不能出现T->next这种判断导致返回。        Visit(T->data);        if (PreOrderTraverse(T->lchild, Visit))//进入左孩子        if (PreOrderTraverse(T->rchild, Visit))return 1;//进入右孩子    }    else return 1;}//首先判断该链节是否存在执行操作,如果操作成功,进入左孩子(一直执行操作进入左孩子)直到执行操作失败返回0 开始执行操作,进入右孩子(每进入一个右孩子就运行1.执行操作,2.进入右孩子)//每进入一个最底层的右孩子程序执行操作返回一层//这里程序有2个出口是必要的一个是进入左孩子的出口,一个是进入右孩子的出口(也就是说一个函数里有几次递归调用就要有几个出口)//这里需要关注的不是程序的执行次数,而是思考函数调用到最后一次时的情况//这里最后一次有三种情况1.只有左孩子。2.只有右孩子。3.一个孩子都没有。三种情况都能返回才能实现递归//最后一次是1.操作。2.访问左孩子。3.访问右孩子.//从开头分析想实现遍历需要1.操作。2.访问左孩子不知道左孩子是什么就调用遍历二叉树这个函数。3.访问右孩子不知道左孩子是什么就调用遍历二叉树这个函数//调用函数:(status (*function)(status 参数))int CreatBiTree(BiTree &T)//二叉树的建立{    char ch;    cin >> ch;    if (ch == ' ')T = NULL;    else    {        if (!(T = new BiTNode))exit(0);        T->data = ch;        CreatBiTree(T->lchild);        CreatBiTree(T->rchild);    }    return 1;}//这里只有一个返回,担任了2个出口的责任//函数内部不能出现T->next这种语句如果出现就用CreateBiTree(T->next)代替

建立线索二叉树

#include<iostream>#define TElemType intusing namespace std;typedef enum PointerTag{Link,Thread};typedef struct BiThrNode{    TElemType data;    struct BiThrNode *lchild, *rchild;    PointerTag LTag, RTag;}BiThrNode,*BiThrTree;BiThrNode *pre;void InThreading(BiThrTree p){    if (p == NULL)return;//空返回,则进入空之后开始执行空的上一级调用    InThreading(p->lchild);    if (!p->lchild)//这里下一级为空之后执行    {        p->LTag = Thread;        p->lchild = pre;    }    if (pre->rchild == NULL)    {        pre->RTag = Thread;        pre->rchild = p;    }//这里是这个程序的完美的地方,用全局变量来保留pre,每次调用更新pre这样pre只会向前走,不会退回去。是中序遍历,每次实现左右2个节点    pre = p;//这一句实现了pre的推进,只要p不为空就要推进    InThreading(p->rchild);}//InThreading(p->rchild);这一句能实现进入右孩子仅仅是进入右孩子void InOrderThreading(BiThrTree &Thrt, BiThrTree T)//用Three来返回生成的树{    if (!(Thrt = new BiThrNode))exit;    Thrt->LTag = Link; Thrt->RTag = Thread;    Thrt->rchild = Thrt;//初始化为有右指针指向自己    if (!T)Thrt->lchild = Thrt;    else    {        Thrt->lchild = T; pre = Thrt;        pre->rchild = Thrt;        InThreading(T);        pre->rchild = Thrt;        pre->RTag = Thread;        Thrt->rchild = pre;    }}

哈夫曼树:

#include<iostream>using namespace std;typedef struct{    int weight;    int parent, lchild, rchild;}HTNode,*HuffmanTree;typedef char **HuffmanCode;void Select(HuffmanTree HT, int n, int &s1, int &s2){    s1 =1;    HuffmanTree p;    int i;    for (i = 1; i <=n; ++i)    {        if (HT[i].parent == 0)        {            if (HT[i].weight <= HT[s1].weight)                s1 = i;        }    }    if (s1 == 1)s2 = 2;    else s2 = 1;    for (i = 1; i <=n; ++i)    {        if (HT[i].parent == 0)        {            if (HT[i].weight <= HT[s2].weight&&i!=s1)                s2 = i;        }    }}void HuffmanCoding(HuffmanTree &HT, HuffmanCode &HC, int *w, int n)//w存放n个字符的权值,HT是哈夫曼树,HC是各权值组求出来的哈夫曼数组{    if (n <= 1)return;//判断输入的值是否合法    int m = 2 * n - 1;//总节点数    HT = new HTNode[m + 1];//0号单元未引用    HuffmanTree p; int i;    for ( p = HT+1, i = 1; i <= n; ++i, ++p, ++w)        *p = { *(w+1), 0, 0, 0 };//结构体初始化,明确给出的权值初始化    for (; i <= m; ++i, ++p) *p={ 0, 0, 0, 0 };//计算出来的权值初始化为全0    for (i = n + 1; i <= m; ++i)//这里从n+1到2*n-1两端都是=相当于n+1到2*n这里运算了n-1次(2*n-(n+1))实际就应该运行n-1次(要生成几个节点就运行几次)    {//下面是生成新节点(为新节点赋值和连接)        int s1, s2;        Select(HT, i - 1, s1, s2);//决定谁的(序号)双亲作为新节点        HT[s1].parent = i; HT[s2].parent = i;//为选出来的2个节点设置双亲        HT[i].lchild = s1; HT[i].rchild = s2;//生成的双亲节点序号作为老节点序号+1,设置新节点的2个孩子        HT[i].weight = HT[s1].weight + HT[s2].weight;//设置新节点权值    }    HC = new char*[n + 1];    char *cd = new char[n];//二维数组申请空间    cd[n - 1] = '\0';    for (i = 1; i <= n; ++i)    {        int start = n - 1;        int f;        int t = 0;        HC[i] = new char[n-1];        for ( int c = i,f = HT[i].parent; f != 0; c = f, f = HT[f].parent)//这里可以换成for(c=i;HT[c].parent!=0;c=HT[c].parent)        {//cd用做临时变量,用来做栈,从下往上遍历,HC[i]作为存储哈夫曼编码的空间   for(c=i;c!=0;c=HT[c].parent)            if (HT[f].lchild == c)cd[--start] = '0';//换成if(HT[HT[c].parent].lchild==c)cd[--start]='0';            else cd[--start] = '1';//这是一个倒扣的栈        }        for (int t = 0; ; ++t)        {            HC[i][t] = cd[start++];            if (cd[start] == '\0'){ HC[i][t+1] = '\0'; break; }        }    }    delete cd;}void Print(HuffmanCode HC,int n){    for (int i = 0; i < n; ++i)    {        for (int j = 0; HC[i][j] != '\0';++j)        {            cout << HC[i][j];        }        cout << endl;    }}int main(){    HuffmanTree HT;    HuffmanCode HC;    int w[4] = {0,3,2,1};//w的0位置不会被引用    int n=3;    HuffmanCoding(HT, HC, w, n);    Print(HC, n);}

图的数组表示:

#include<iostream>using namespace std;#define INFINTY INT_MAX#define MAX_VERTEX_NUM 20#define VRType int#define InfoType int#define VertextType inttypedef enum{DG,DN,UDG,UDN}GraphKind;//图的种类:有向图,有向网,无向图,无向网typedef struct ArcCell{    VRType adj;//顶点关系类型(即顶点是否连接)对于无权图用0或1表示是否相邻对带权图为权值类型    InfoType *info;//该弧相关的信息类型指针}ArcCell,AdjMastrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];typedef struct{    VertextType vexs[MAX_VERTEX_NUM];//顶点的值    AdjMastrix arcs;//表示顶点间关系的矩阵    int vexnum, arcnum;//图的当前顶点数和弧数    GraphKind kind;//图的种类}MGraph;int IncInfo;int LocateVex(MGraph G, int v){    for (int i = 0; i < G.vexnum;++i)    if (G.vexs[i] == v)return i;}void CreateUDN(MGraph &G)//构造无向网{    int v1, v2, w;    cin>>G.vexnum>>G.arcnum>>IncInfo;//IncInfo==0则弧不含其他信息    for (int i = 0; i < G.vexnum; ++i)cin >> G.vexs[i];//根据顶点数输入顶点    for (int i = 0; i < G.vexnum;++i)    for (int j = 0; j < G.vexnum; ++j)G.arcs[i][j] = { INFINITY, NULL };//初始化所有弧间距离为无穷大,弧的信息为NULL    for (int k = 0; k < G.arcnum; ++k)    {        cin >> v1 >> v2 >> w;//一条边依附的2个顶点和权值        int i = LocateVex(G, v1); int j = LocateVex(G, v2);        G.arcs[i][j].adj = w;        if (IncInfo)cin >>*( G.arcs[i][j].info);        G.arcs[j][i] = G.arcs[i][j];//v1和v2有弧则v2和v1一定有弧//无向图一定是对称矩阵    }    return;}void CreateDG(MGraph &G)//构造有向图{    cin >> G.vexnum >> G.arcnum >> IncInfo;    for (int i = 0; i < G.vexnum; ++i)cin >> G.vexs[i];    for (int i = 0; i < G.vexnum;++i)    for (int j = 0; j < G.vexnum; ++j)G.arcs[i][j] = { INFINITY, NULL };    for (int k = 0; k < G.arcnum; ++k)    {        int v1, v2;        cin >> v1 >> v2 ;        int i = LocateVex(G,v1);        int j = LocateVex(G, v2);        G.arcs[i][j].adj = 1;    }    return;}//图的构建思路:由一个函数选择要生成的图的类型(输入kind)->调用对应的图生成函数//图生成函数:输入顶点数,边数,边上是否还有其他信息->输入定点对应的数据->初始化边临街矩阵为 INFINITY ->通过输入每条边2个顶点来确定边//->为邻接矩阵赋1或权值(如果还有其他信息这时候为其他信息赋值)void CreatGraph(MGraph &G){    switch (G.kind)    {    case DG:return CreateDG(G);    }//......}

图的邻接表表示:

#include<iostream>#define MAX_VERTEX_NUM 20using namespace std;#define InfoType int#define VertexType int typedef struct ArcNode{    int adjvex;//该弧所指向的顶点位置(是个数字)    struct ArcNode *nexarc;//指向下一个节点    InfoType *info;//这条弧上的其他信息}ArcNode;//节点表示弧typedef struct VNode{    VertexType data;//顶点的值    ArcNode *firsttarc;//指向第一个依附该顶点弧的指针}VNode,AdjList[MAX_VERTEX_NUM];//头结点表示点typedef struct{    AdjList vertices;    int vexnum, arcnum;    int kind;}ALGraph;//表示整个邻接表 //建成的邻接表只有头结点表示顶点,后面的节点都表示边void Insert(VNode &A, int v)//2个参数,第一个是要插入的节点A,第二个是插入节点中的数据{    ArcNode*p = new ArcNode;    p->adjvex = v;    p->info = NULL;//为新节点初始化    p->nexarc = A.firsttarc;    A.firsttarc = p;//这里用了链表的前插法}int LocateVex(ALGraph G, int v){    for (int i = 0; i < G.vexnum; ++i)    {        if (G.vertices[i].data == v)        {            return i;        }    }    cout << "请输入合法数据!";    exit(0);}void CreatUDG(ALGraph&G){    int v1, v2;    cin >> G.vexnum >> G.arcnum;    for (int i = 0; i < G.vexnum; i++)G.vertices[i] = { 0, NULL };    for (int i = 0; i < G.vexnum; ++i)cin >> G.vertices[i].data;    for (int k = 0; k < G.arcnum; ++k)    {        ArcNode *p;        cout << "请输入哪两个点之间有弧";        cin >> v1 >> v2;        v1 = LocateVex(G, v1);        v2 = LocateVex(G, v2);        Insert(G.vertices[v1],v1);        Insert(G.vertices[v2], v2);    }    return;}//老套路:输入顶点数、弧数初始化头结点,输入头结点中的顶点,根据弧数来依次创建节点,插入节点//无向图每个节点要申请2次空间//邻接表每个头结点下跟的链表是从该节点出去的弧(当然这是针对有向图来说)//这里创建链表用的是前插法

图的十字链表表示:

#include<iostream>using namespace std;#define MAX_VERTEX_NUM 20#define InfoType int#define VertexType inttypedef struct ArcBox{    int tailvex, headvex;//该弧的头顶点和尾顶点    struct ArcBox *hlink, *tlink;//分别为弧头相同和弧尾相同的弧的链域(就是指向以该节点为入度/出度的链节)    InfoType *info;//指向该弧相关信息}ArcBox;//弧节点typedef struct VexNode{    VertexType data;    ArcBox *firstin, *firstout;//分别指向该节点第一条入弧和出弧}VexNode;//顶点节点typedef struct{    VexNode xlist[MAX_VERTEX_NUM];    int vexnum, arcnum;}OLGraph;//十字链表图结构int LocateVex(OLGraph G, VertexType v){    for (int i = 0; i < G.vexnum;++i)    if (G.xlist[i].data == v)return i;}void Insert(VexNode &Vin, VertexType vin, VexNode &Vout, VertexType vout)//从Vout顶点出来的弧进入vin顶点{    ArcBox*p = new ArcBox;    p->headvex = vout;    p->tailvex = vin;    p->hlink = Vout.firstout;    Vout.firstout = p;    p->tlink = Vin.firstin;    Vin.firstin = p;}//这个函数实现了链表的插入,值得注意的一点是对一个弧来说头顶点和尾顶点对应着对顶点来说的出链接和入链接void CreatDG(OLGraph &G)//有向图{    VertexType a;    int IncInfo;//标志有没有其他弧信息    cin >> G.vexnum >> G.arcnum >> IncInfo;    for (int i = 0; i < G.vexnum; ++i)    {        cin >> a;        G.xlist[i] = { a, NULL, NULL };    }//初始化,给顶点赋初值    for (int k = 0; k < G.arcnum; ++k)    {        VertexType v1, v2;        cin >> v1 >> v2;//v1是弧的起始点,v2是弧的尾        v1 = LocateVex(G, v1);        v2 = LocateVex(G, v2);        Insert(G.xlist[v2], v2, G.xlist[v1], v1);    }    return;}//十字链表只不过是比邻接表多了2个域而已,说白了还是把节点插入到链表里//过程都一样:cin顶点数、弧总数,初始化,输入各顶点值,输入弧的位置,把弧插入到链表中