数据结构

线性表

1.相同数据类型

2.有限序列

基本操作:创销,增删改查

传入参数时,何时要用引用 &——对参数的修改结果需要“带回来”

顺序表

1.顺序表实现——静态分配

#define MaxSize 10 // 定义最大长度 

typedef struct {
	int data[MaxSize]; // ElemType = int, * 用静态的“数组”存访数据元素 
	int length; // 顺序表的当前长度 
} SqList;

2.顺序表实现——动态分配

​ malloc(申请空间),free(释放空间)

#define InitSize 10 // 顺序表的初始长度
typedef struct {
	int * data; // ElemType = int, * 声明动态分配数组的指针 
	int MaxSize; // 顺序表的最大容量
	int length; // 顺序表的当前长度 
} SqList;

// 初始化顺序表 
void InitList(SqList &L) {
	// 用 malloc 函数申请一片连续的存储空间 
	L.data = (int *)malloc(InitSize * sizeof(int));
	L.length = 0;
	L.MaxSize = InitSize;
}

顺序表的特点:

①随机访问,即可以在0(1)时间内找到第i个元素。
②存储密度高,每个节点只存储数据元素。
③拓展容量不方便(即便采用动态分配的方式实现,拓展长度的时间复杂度也比较高)
④插入、删除操作不方便,需要移动大量元素

顺序表的基本操作

插入和删除

插入:ListInsert(&L,i,e) 在表L中的第i个位置上插入指定元素e。

删除:ListDelete(&L,i,&e) 删除表L中第i个位置的元素,并用e返回删除元素的值。

顺序表——按位查找

GetElem(L,i):按位查找操作。获取表L中第i个位置的元素的值。

顺序表——按值查找

LocateElem(L,e):按值查找操作。在表L中查找具有给定关键字值的元素。

链表

单链表

  • 每个结点除了存储数据元素外,还要存储指向下一个结点(后继)的指针
  • 优点:不要求大片连续空间,改变容量方便
  • 缺点:不可随机存取,要耗费一定空间存放指针

如果每次都要写struct很麻烦,所以可以利用typedef关键字——数据类型重命名:typedef<数据类型><别名>

单链表基本操作

增删改查

按值查找

按位序插入(带头结点)

按位序插入(不带头结点)

注意:单链表不具备“随机访问”的特性,只能依次扫描

双链表

typedef struct DNode{            //定义双链表结点类型
    ElemType data;               //数据域
    struct DNode *prior, *next;  //前驱和后继指针
}DNode, *DLinklist;

循环链表

循环单链表

循环双链表

静态链表

静态链表:分配一整片连续的内存空间,各个结点集中安置。

#define MaxSize 10        //静态链表的最大长度

typedef struct{           //静态链表结构类型的定义
    ELemType data;        //存储数据元素
    int next;             //下一个元素的数组下标
}SLinkList[MaxSize];

void testSLinkList(){
    SLinkList a;
}

栈和队列

栈(Stack)

栈是特殊的线性表:只允许在一端进行插入或删除操作, 其逻辑结构与普通线性表相同

栈顶,栈底,栈空

特点:后进先出

缺点:栈的大小不可变,解决方法——共享栈

创&销

  • InitStack(&S) 初始化栈:构造一个空栈S,分配内存空间;

  • DestroyStack(&S) 销毁栈:销毁并释放栈S所占用的内存空间;

增&删

  • Push(&S, x) 进栈:若栈S未满,则将x加入使其成为新栈顶;
  • Pop(&S, &x) 出栈:若栈S非空,则弹出(删除)栈顶元素,并用x返回;

查&其他

  • GetTop(S, &x) 读取栈顶元素:若栈S非空,则用x返回栈顶元素;(栈的使用场景大多只访问栈顶元素);
  • StackEmpty(S) 判空: 断一个栈S是否为空,若S为空,则返回true,否则返回false;

顺序栈

#define MaxSize 10         //定义栈中元素的最大个数

typedef struct{
    ElemType data[MaxSize];       //静态数组存放栈中元素
    int top;                      //栈顶元素
}SqStack;

void testStack(){
    SqStack S;       //声明一个顺序栈(分配空间)
                     //连续的存储空间大小为 MaxSize*sizeof(ElemType)
}

链栈

只能采用头插法插入或删除数据的链表;

typedef struct Linknode{
    ElemType data;              //数据域
    struct Linknode *next;      //指针域
}*LiStack;                      //栈类型的定义

栈的应用

  • 栈在括号匹配中的应用
  • 中缀表达式 (需要界限符)
  • 后缀表达式 (逆波兰表达式)
  • 栈在递归中的应用

队列 (Queue)

队列是操作受限的线性表,只允许在一端进行插入 (入队),另一端进行删除 (出队)

队头,队尾,空队列

特点:先进先出,只允许在队尾插入,队头删除

# define MaxSize 10;     //定义队列中元素的最大个数
typedef struct{
    ElemType data[MaxSize];   //用静态数组存放队列元素
                              //连续的存储空间,大小为——MaxSize*sizeof(ElemType)
    int front, rear;          //队头指针和队尾指针
}SqQueue;

循环队列(判满)

方案一: 牺牲一个单元来区分队空和队满

队尾指针的再下一个位置就是队头,即 (Q.rear+1)%MaxSize == Q.front

  • 循环队列——入队:只能从队尾插入(判满使用方案一)
bool EnQueue(SqQueue &Q, ElemType x){
    if((Q.rear+1)%MaxSize == Q.front)        //队满
        return false;
    Q.data[Q.rear] = x;                      //将x插入队尾
    Q.rear = (Q.rear + 1) % MaxSize;         //队尾指针加1取模

    return true;
}
  • 循环队列——出队:只能让队头元素出队
//出队,删除一个队头元素,用x返回
bool DeQueue(SqQueue &Q, ElemType &x){
    if(Q.rear == Q.front)              //队空报错
        return false;  

    x = Q.data[Q.front];
    Q.front = (Q.front + 1) % MaxSize; //队头指针后移动

    return true;
}
  • 循环队列——获得队头元素
bool GetHead(SqQueue &Q, ElemType &x){
    if(Q.rear == Q.front)              //队空报错
        return false;  

    x = Q.data[Q.front];
    return true;
}

方案二: 不牺牲存储空间,设置size

定义一个变量 size用于记录队列此时记录了几个数据元素,初始化 size = 0,进队成功 size++,出队成功size--,根据size的值判断队满与队空

队满条件:size == MaxSize

队空条件:size == 0

方案三: 不牺牲存储空间,设置tag

定义一个变量 tag,tag = 0 --最近进行的是删除操作;tag = 1 --最近进行的是插入操作;

每次删除操作成功时,都令tag = 0;只有删除操作,才可能导致队空;
每次插入操作成功时,都令tag = 1;只有插入操作,才可能导致队满;

队满条件:Q.front == Q.rear && tag == 1

队空条件:Q.front == Q.rear && tag == 0

队列的链式存储结构

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

typedef struct{               //链式队列
    LinkNode *front, *rear;   //队列的队头和队尾指针
}LinkQueue;

双端队列

  • 双端队列允许从两端插入两端删除的线性表;
  • 如果只使用其中一端的插入、删除操作,则等同于栈;
  • 输入受限的双端队列:允许一端插入两端删除的线性表;
  • 输出受限的双端队列:允许两端插入一端删除的线性表;

队列应用

树的层次遍历,图的广度优先遍历等

顺序表

#define MAXLEN 255   //预定义最大串长为255

typedef struct{
    char ch[MAXLEN];   //静态数组实现(定长顺序存储)
                       //每个分量存储一个字符
                       //每个char字符占1B
    int length;        //串的实际长度
}SString;

堆分配

//动态数组实现
typedef struct{
    char *ch;           //按串长分配存储区,ch指向串的基地址
    int length;         //串的长度
}HString;

HString S;
S.ch = (char *) malloc(MAXLINE * sizeof(char)); //基地址指针指向连续空间的起始位置
                                                //malloc()需要手动free()
S.length;

链式存储

typedef struct StringNode{
    char ch;           //每个结点存1个字符
    struct StringNode *next;
}StringNode, * String;

属性:

结点的层次(深度) -- -从上往下数
结点的高度--从下往上数
树的高度(深度)--总共多少层

结点的度- - -有几个孩子(分支)
树的度--各结点的度的最大值

顺序存储

#define MaxSize 100

struct TreeNode{
   ElemType value; //结点中的数据元素
   bool isEmpty;   //结点是否为空
}

链式存储

//二叉树的结点

struct ElemType{
   int value;
};

typedef struct BiTnode{
   ElemType data;          //数据域
   struct BiTNode *lchild, *rchild; //左、右孩子指针
}BiTNode, *BiTree;

二叉树

  1. 满二叉树

  2. 完全二叉树

  3. 二叉排序树

  4. 平衡二叉树

满二叉树

特点:
①只有最后一-层有叶子结点入
②不存在度为1的结点
③按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父节点为[i/2] (如果有的话)

完全二叉树

特点:
①只有最后两层可能有叶子结点
②最多只有一个度为1的结点
③按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父节点为[i/2] (如果有的话)
④i< [n/2]为分支结点,i>[n/2] 为叶子结点

平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。

posted @ 2021-08-16 22:14  贝贝子  阅读(120)  评论(2编辑  收藏  举报