zoukankan      html  css  js  c++  java
  • 基本数据结构

    众所周知, 数据结构分为以下四个方面 :

    1. 集合 ( 结点之间没什么联系, 不需要总结 )

    2. 线性 ( 一条直线 )

    3. 树状 ( 类似家谱 )

    4. 图  ( 难, 暂时先不总结 )

    数据结构的定义: 分为结点的定义和结点之间关系的定义.


    线性结构

    - 顺序表

    typedef struct {

        int elem[100];

        int length;  // 这里的lenth是指当前分配的长度

    } SqList;

    由以上结构可以看出, 结点的值存储在 elem 中,而结点之间的关系就是数组隐含, 所以不需要另外在定义关系.

    - 单链表

    typedef struct LNode{

        int elem;

        struct LNode *next;

    } LNode, *LinkList;

    结点: LNode 是用来保存结点的

    关系: LinkList 就是链表头指针, 关系是通过 next 指针联系起来的.

    头指针: LinkList 就是头指针, 指向头结点的指针.

    头结点: (1)对带头结点的链表, 在表的任何结点之前插入结点或删除表中任何结点, 所要做的都是修改前一个结点的指针域, 而任何元素都有前驱结点, 若链表没有头结点, 则首元素结点没有前驱结点, 在其前插入结点或删除结点时操作会复杂些.(2)对带头结点的链表, 表头指针时指向结点的非空指针, 因此空表与非空表处理是一样的.

    - 循环链表

    所谓循环链表, 其实

    结点: 存储情况, 同上边完全一样.

    关系: 头指针的 next 指向自己, 这样的话就是循环链表了, 当插入结点时, 新的结点 p->next = L->next(指向头结点).

    - 双向链表

    typedef struct DLNode {

        int elem;

        struct DLNode *prior;

        struct DLNode *next;

    } DLNode, *DLinkList;

    结点: DLNode

    结构: 头指针 DLinkList, 通过 next 和 prior 来反映元素之间的线性关系.

    - 静态链表

    所谓静态链表: 是指用数组模拟操作, 实现的链表, 其中指针域, 使用数组下标表示.

    typedef struct {

        int elem;

        int next;

    } SLNode, slinklist[MAXSIZE];

    结点: SLNode, 其中的 next 就是模拟指针.

    关系: slinklist 是一个SLNode的数组, 数组中的 next 隐含关系.

    栈和队列: 是限制操作的线性表

    - 顺序栈 

    typedef struct {

        int elem[100];

        int top;

    } SqStack;

    结点: 数组中的元素;

    关系: SqStack.

    为什么没有链式栈, 因为栈这种结构限制了, 后进先出, 即只能从栈顶出战, 即 top 会记录栈顶位置, 所以它虽然是顺序结构, 但是插入和删除操作并不需要移动元素, 所以, 当然是顺序栈好一些.

    - 顺序队列( 循环队列 )  

    typedef struct {

        int elem[100];

        int front;

        int rear;

    } SqQueue;

    结点: elem数组中的元素

    关系: 隐含在数组中, 注意 front 和 rear 的位置, 关系还是隐含在数组中, 队列是先进先出, front 记录了队列头, rear 记录了队列尾, 从 front出, rear进, 注意队列判空和判满条件: 如下

    因为 出队列时, 头指针 front 会向后移动, 此时, 前一个存储区域虽然出队列了, 但是仍然占据了存储空间没有释放, 这样就势必造成了空间的浪费, 这样最好的办法是使用循环队列, 但是循环队列如何判空和判满呢?

     image

    如上图: 从结构上看, 队列里只剩下了 3 个存储单元, 前边浪费了大量存储空间, 所以要使用循环队列, 并且不能通过 front == rear 来简单的判断判空或判满, 浪费一个存储空间, 即 (rear + 1) % 存储空间 = front, 则判断为慢, 关键看谁最上谁, 如果 front追上rear 空队列, 如果是 rear追上了 front满队列. 为什么要 (rear + 1)%存储空间呢? 因为当 rear已经在数组最右边时, 如果单纯的 rear+1, 那么已经超过数组最大范围, 但是(rear+1)%存储空间, 如果 rear+1没有超过存储空间, 那么取模与不取模操作都一样, 但是如果 rear+1超过了数组范围,那么取模以后, 又回到了第一个了, 这样就达到了循环的目的, 而 (rear+1)%存储空间 == front 表示 rear 已经循环到了 front的前一个存储空间了.

    - 链式队列

    typedef struct Qnode {

        int elem;

        struct Qnode *next;

    } Qnode, *Qlink;

    typedef struct SQlink {

        Qlink front;

        Qlink rear;

    } *linkqueue;

    结点: Qnode

    关系: linkqueue

    注意: 链式队列不需要循环队列, 因为不存在空间浪费的情况, 当有出队列的结点时, 直接释放该结点的内存就可以了.


    数组相关, 矩阵压缩存储

    - 三元组

    typedef struct {

        int i, j; // 非零元 的行和列

        int elem;

    } Tripe;

    typedef struct {

        Tripe Matrix[MAX_SIZE];

        int mu, nu, tu;  // 矩阵的行, 列数, 及非零元个数

    } TMatrix;

    结点: Tripe;

    关系: TMatrix

    特点: 非零元在数组中按行逻辑顺序存储便于进行依次顺序处理矩阵运算, 但是, 如果我想找到一行的非零元, 就比较麻烦, 还是需要从头开始找, 由此引出 行逻辑链接顺序表存储法.

    - 行逻辑链接顺序表

    typedef struct {

        int i, j; // 非零元 的行和列

        int elem;

    } Tripe;

    typedef struct {

        Tripe Matrix[MAX_SIZE];

        int mu, nu, tu; // 矩阵的行, 列数, 及非零元个数

        int rpos[MAXRC+1];  // 各行第一个非零元的位置表

    } LMatrix;

    结点: Tripe

    关系: LMatrix

    这个存储结构跟三元组基本上一样, 只是多了一个记录在数组中, 第几个元素还是是第几行的开始非零元. 这里的 rpos[MAXRC+1] 记录的是第几行在数组中的非零元的起始位置, 例如 rpos[2] = 5 表示 第 2 行非零元的起始位置, 在Matrix=[5]

    - 十字链表存储发   

    以上的存储方式, 说白了, 还是顺序存储, 如果矩阵非零元个数和位置变化较大, 就比较适合使用链式存储结构.

    typedef struct mxtripe {

        int elem;

        int i, j;

        struct mxtripe *right;

        struct mxtripe *end;

    }MxTripe, *OLink;

    typedef struct {

        OLink *rhead;  // rhead 指向的是一个行向量, 该向量指向 元素类型

        OLink *chead;  // chead 指向的是一个列向量, 该向量指向 元素类型

        int mu, nu, tu;

    }CrossList;

    rhead, chead 指向的是向量的首地址, 即数组.

    image

    可见 rhead 指向 行级指针数组, chead 指向 列级指针数组.

    十字链表在做矩阵运算时非常方便.


    - 树的双亲表示法

    typedef struct treenode {

       int elem;

       int parent;

    } PT;

    typedef struct {

        PT nodes[MAX_TREE_SIZE];

        int r, n;  // 根结点和结点总数

    } PTree;

    image

    结点: PT

    关系: PTree, 其中关系也是隐含在结点的 parent中.

    这种存储方式, 很显然, 找儿子特别困难. 找parent相对容易.

    - 树的孩子链表 表示法

    typedef struct CTNode {  // 孩子结点, 此节点如果缺少 child, 保存信息并不完整

        int child;  // 在数组中的下标

        struct CTNode *next;

    } *ChildPtr;

    typedef struct {  // 树中的结点

        int data;

        ChildPtr firstchild;  // 孩子链表头指针

    } CTBox;

    typedef struct {  // 树结构

        CTBox nodes[MAX_TREE_SIZE];

        int n,r;  // 结点数 和 根位置( 在数组中 )

    } CTree;

    此种结构, 找到孩子很容易, 但是由孩子找 parent 就很麻烦.

    image

    - 树的孩子兄弟 表示法( 也叫二叉树表示法或二叉链表 表示法 ) 推荐

    typedef struct CSNode {

        int elem;

        struct CSNode *firstchild, *nextsibling;  // 左孩子, 右兄弟

    } CSNode, *CSTree;

    结点: CSNode

    关系: 首先定义一个结点为根结点, 然后利用 firstchild 指针指向第一个孩子, 依次继续, 具体结构图, 如下:

    image

    二叉树

    - 顺序存储结构

    typedef TelemType SqBiTree[MAX_TREE_SIZE];

    SqBiTree bt;

    结点: 存放在数组中.

    关系: 通过结点存放在数组中的位置来判断结点之间的关系.

    缺点: 浪费很多存储空间, 另外结点之间的关系不明显. 下图中黄颜色的全部是浪费的, 而且还有很多浪费的, 因为是按照完全二叉树的方式存储的.

    image

    - 二叉树, 二叉链表表示法

    typedef struct BiNode {

        int elem;

        struct BiNode *leftChild, *rightChild;

    } BiNode, *BiTree;

    因为 二叉树的特点是最多只有2个儿子, 所以可以分为左右两个儿子, 然后进行存储.

    结点: BiNode

    关系: leftchild, rigthchild

    可以看到, 这种方式的存储方法, 跟实际画图是一样的. 而且这种方式很像 左孩子又兄弟表示法, 这也是树与二叉树互换的依据.

    image

    - 二叉树, 三叉链表表示法

    typedef struct BiNode {

        int elem;

        struct BiNode *leftChild, *rightChild, *parent;

    } BiNode, *BiTree;

    从定义上可以看出, 对边二叉链表表示法, 只是多了个指针指向 parent .

    结点: BiNode

    关系: leftchild, rightchild, parent

    image

    树的遍历

    先序遍历: 根左右

    中序遍历: 左根右

    后续遍历: 左右根

    森林与二叉树的转换

    由于二叉树和树都可以用 二叉链表作为存储结构, 那么以二叉链表作为媒介可导出树与二叉树之间的一个对应关系, 从物理上, 他们的二叉表是相同的, 只是解释不同. 如下图:

    image

    image

  • 相关阅读:
    [ 随手记 4 ]C/C++ 模板(Template)使用/重载区别
    [ 随手记 3 ] 堆区/栈区/堆栈/队列
    [ 随手记 2 ] C/C++ 数组/指针/传数组到函数/指针数组/数组指针
    柯西方程的另外一种解法
    十分强大的CC抛物线定理(数学)
    模板_BIT
    模板_SEG_TREE
    模板_SPLAY
    模板_LCA
    NOIP游(GUNCU)记
  • 原文地址:https://www.cnblogs.com/moveofgod/p/2967016.html
Copyright © 2011-2022 走看看