数据结构
线性表
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的结点
③按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父节点为[i/2] (如果有的话)
完全二叉树
特点:
①只有最后两层可能有叶子结点
②最多只有一个度为1的结点
③按层序从1开始编号,结点i的左孩子为2i,右孩子为2i+1;结点i的父节点为[i/2] (如果有的话)
④i< [n/2]为分支结点,i>[n/2] 为叶子结点
平衡二叉树:树上任一结点的左子树和右子树的深度之差不超过1。