- 数据结构的基本概念
- 算法的基本概念
- 算法的时间复杂度
- 算法的空间复杂度
- 线性表(逻辑结构)的定义和基本操作
- 顺序表(存储结构:顺序存储)的定义和基本操作
- 单链表(存储结构:链式存储)的定义和基本操作
- 双链表的定义和基本操作
- 循环链表的定义和基本操作
- 静态链表的定义和基本操作
- 链表和顺序表的比较
- 栈的定义(逻辑结构:线性表)和基本操作
- 顺序栈(存储结构:顺序存储)的定义和基本操作
- 链栈(存储结构:链式存储)的定义和基本操作
- 队列的定义(逻辑结构:线性表)和基本操作
- 队列的顺序实现
- 队列的链式实现
- 双端队列(栈是只在一端进出的队列)
- 栈的应用
- 括号匹配
- 中缀表达式转后缀表达式
- 中缀表达式求值、后缀表达式求值
数据结构前三章
概述
数据结构的基本概念
程序 = 算法 + 数据结构
数据结构 = 数据的逻辑结构、数据的物理结构(存储结构)、数据的运算
算法的基本概念
算法是数据运算的顺序和执行方式
算法的时间复杂度与空间复杂度
时间复杂度:算法的运行次数与数据规模n的关系
空间复杂度:算法所占用的内存单元与数据规模n的关系
用O表示法
线性表
定义:相同类型元素 的有限序列
操作:
//初始化
InitList(List &L);
//销毁列表
DestroyList(List &L);
//插入
ListInsert(List &L,ElemType x);
//删除i位元素
ListDelete(List &L,int i,ElemType &x);
//返回x地址
LocateElem(List L,ElemType x);
//返回i位元素x
GetElem(List L,ELemType &x);
//判空
IsEmpty(List L);
//判满
IsFull(List L);
//返回线性表长
ListLong(List L);
线性表的顺序存储
顺序表
顺序表的特点:
优点:随机读取,存储密度大
缺点:扩容不方便,插入、删除数据不方便
逻辑结构:线性表
存储结构:顺序存储
代码定义
静态分配
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];
int length;
}SqList;
void InitList(SqList &L){
for(int i = 0; i < MaxSize;i++)L.data[i] = 0;
L.length = 0;
}
bool IsEmpty(SqList &L){
if(L.length == 0)return true;
return false;
}
bool InsertList(SqList &L,int i,ElemType e){
if(i < 0|| i > length||L.length == MaxSize)return false;
if(i == length)L.data[i] = e;
else{
for(int j = length - 1;j >= i - 1;j--){
L.data[j+1] == L.data[j];
}
L.data[i - 1] = e;
}
L.length ++;
return true;
}
动态分配
#define InitSize 10
typedef struct{
ElemType *data;
int maxSize;
int length;
}SqList;
bool InitList(SqList &L){
L.data = (int *)malloc(InitSIze*sizeof(int));
if(L.data == NULL)return false;
L.length = 0;
L.maxSize = InitSize;
return true;
}
bool IncreaseSize(SqList &L,int len){
int *p = L.data;
L.data = (int *)malloc((L.MaxSize + len)*sizeof(int));
if(L.data == NULL)return false;
for(int i = 0;i < L.length;i++)L.data[i] == p[i];
L.maxSize += len;
return true;
}
线性表的链式存储
单链表
定义:每个节点存储数据及指向下一个节点指针的线性表
链表的特点:
优点:扩容方便,不要求大片连续的空间
缺点:不可随机存取,存储密度小,要耗费一定空间存储指针
实现:
无头节点
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
void InitList(LinkList &L){
L = NULL;
}
bool IsEmpty(LinkList L){
if(L == NULL)return true;
return false;
}
//指定节点后插入(非首个节点)
bool InsertNextNode(LNode *p,x){
if(p == NULL)return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
s -> data = x;
s -> next = p -> next;
p -> next = s;
return true;
}
//首个节点插入
bool InsertFirstNode(LinkList &L,x){
LNode *s = (LNode *)malloc(sizeof(LNode));
if(s == NULL)return false;
s -> data = x;
s -> next = L;
L = s;
return true;
}
//获取第i位元素
LNode *GetElem(LinkList L,int i){
if(i < 1)return NULL;
if(i == 1)return L;
for(int j = 1;j < i && L!= NULL;j++)L = L -> next;
return L;
}
有头节点
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode*LinkList;
//初始化
bool InitList(LinkList &L){
L = (LNode *)malloc(sizeof(LNode));
L -> next = NULL;
}
//判空
bool IsEmpty(LinkList L){
if( L -> next == NULL)return true;
return false;
}
//获取i位元素
LNode *GetElem(LinkList L,int i){
if(i < 1)return NULL;
for(int j = 0;j < i && L!= NULL;j++){
L = L -> next;
}
return L;
}
//后插
bool InsertNextNode(LNode *p,ElemType e){
if(p == NULL)return false;
LNode *s == (LNode *)malloc(sizeof(LNode));
if(s == NULL)return false;
s -> data = e;
s -> next = p -> next;
p -> next = s;
return true;
}
//前插
bool InsertPriorNode(LNode *p,ElemType e){
if(p == NULL)return false;
LNode *s = (LNode *)malloc(sizeof(LNode));
s -> next = p -> next;
s ->value = p -> value;
p -> next = s;
p -> value = e;
return true;
}
//指定位置插入节点
bool ListInsert(LinkList L,int i,ElemType x){
L = GetElem(L,i - 1);
return InsertNextNode(L,x);
}
//删除指定位置节点
bool DeleteNode(LinkList L,int i,ElemType &e){
L = GetElem(L,i - 1);
if(L == NULL)return flase;
if(L -> next == NULL)return flase;
LNode *s = L -> next;
e = s -> data;
L -> next = s -> next;
free(s);
return true;
}
//删除节点
bool DeleteNode(LNode *p){
if(p == NULL)return false;
if(p -> next == NULL){
free(p);
return true;
}
LNode *s = p -> next;
p -> data = s -> data;
p ->next = s -> next;
free(s);
return true;
}
//后插法创建链表
LinkList ListTailInsert(LinkList &L){
int x;
InitList(&L);
LNode *s= L;
scanf("%d",&x);
while(x != 9999){//输入9999时循环结束
InsertNextNode(&s,x);
s = s -> next;
scanf("%d",&x);
}
p -> next = NULL;
return L;
}
//前插法创建链表
LinkList ListHeadInsert(LinkList &L){
int x;
L = (LNode *)malloc(sizeof(LNode));
L -> next = NULL;
scanf("%d",&x);
while(x != 9999){
InsertNextNode(L,x);
scanf("%d",&x);
}
return L;
}
LinkList ListInverse(LinkList &L){
LNode *h = (LNode *)malloc(sizeof(LNode));
h -> next = NULL;
LNode *s = L;
while(s != NULL){
InsertNextNode(h,s -> data);
s = s -> next;
}
return h;
}
双向链表(循环链表)
//有头节点
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
bool InitLinkList(DLinkList &L){
L = (DNode *)malloc(sizeof(DNode));
if(L == NULL)return false;
L -> prior = L -> next = NULL;
return true;
}
//Init循环
bool InitLinkList(DLinkList &L){
L = (DNode *)malloc(sizeof(DNode));
if(L == NULL)return false;
L ->prior = L;
L ->next = L;
return true;
}
//在p节点后插入
bool InsertNextNode(DNode *p,ElemType x){
LNode *s = (LNode *)malloc(sizeof(LNode));
if( p == NULL||s == NULL)return false;
s -> data = x;
s -> next = p -> next;
if(p -> next != NULL){
p -> next -> prior = s;
}
s -> prior = p;
p -> next = s;
return true;
}
bool DeleteNextNode(DNode *p){
if(p == NULL)return flase;
DNode *q = p -> next;
if(q == NULL)return false;
p -> next = q -> next;
if(q -> next != NULL)q -> next -> prior = p;
free(q);
return true;
}
void DestroyDLL(DLinkList &L){
while(L->next != NULL)DeleteNextNode(L);
free(L);
L = NULL;
}
静态链表
分配一整片连续的空间,每个节点由数据和游标(数组下标代替指针)组成
优点:增删操作不需要移动大量元素
缺点:不能随机存取,只能从头节点开始依次往后查找;固定容量不变
#define MaxSize 10
typedef struct {
ElemType data;
int next;
}SLinkList[MaxSize];
void InitList(SLinkList L){
L[0] -> next = -1;
for(int i = 1;i < MaxSize;i ++){
L[i] -> next = -2;
}
}
bool FirstEmptyLoc(SLinkList L,int &x){
for(int i = 0;i < MaxSize;i ++){
if(L[i] -> next == -2){
x = i;
return true;
}
}
return false;
}
bool InsertList(SLinkList L,int i,ElemType x){
int index = 0;
int newIndex = 0;
if(!FirstEmptyLoc(L,&newIndex))return false; //队列满
for(int j = 0;j < i - 2||index == -2; i++){
if(L[index] == -2)return false;
index = L[index] -> next;
}
L[newIndex] -> next = L[Index] -> next;
L[Index] -> next = newIndex;
return true;
}
bool DeleteNode(SLinkList L,int i){
if(L == NULL || L[0] -> next = -1)return false;
int index = 0;
for(int j = 0;j < i - 1||index = -2; j++){
index = L[index] -> next;
} //搜索链表第i-1个节点的游标
if(index == -2)return false; //i值非法
int delIndex = L[index] -> next; //第i个节点游标
if(L[delIndex] == -2)return flase; //第i个节点不存才
L[index] -> next = L[delIndex] -> next; //第i-1个节点游标指向第i+1个节点
L[delIndex] -> next = -2; //第i个节点游标判空
return true;
}
顺序表和链表的比较
逻辑结构
都是线性表,逻辑结构都是线性结构
物理结构(存储结构)
顺序表是顺序存储
优点:支持随机存取,存储密度高
缺点:大片连续空间的分配不方便,改变容量不方便
链表是链式存储
优点:离散的小空间分配方便,改变容量方便
缺点:不能随机存取,存储密度低
数据的运算/基本操作
- 顺序表创建需要分配大量连续空间,若采用静态数组的方式存储,则无法改变容量,若采用动态数组的方式存储,则需要用malloc函数重新分配内存空间,将所有已存在的数据移动
- 链表创建只需要分配一个头节点(或声明一个头指针),方便扩容
- 顺序表增删元素可以直接定位到元素所在的位置,但需要移动所有后继元素,时间开销主要来源于后继元素的移动(时间复杂度O(n))
- 链表增删元素需要从头指针遍历后续节点定位到目标节点,找到目标节点后只需要改变指针的指向,时间开销主要来源于定位目标节点(遍历前驱节点)(时间复杂度O(n))
栈
栈的定义
线性表是相同类型元素的有限序列
栈是一种先进后出的线性表(FILO)
基本操作:
- InitStack
- Push
- Pop
- GetTop
- IsEmpty
- IsFull
栈的实现
顺序栈
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];
int top;
}SqStack;
void InitStack(SqStack S){
S.top = -1;
}
bool IsEmpty(SqStack S){
if(S.top == -1)return true;
return false;
}
bool IsFull(SqStack S){
if(S.top == MaxSize -1)return ture;
return false;
}
bool Push(SqStack &S,ElemType x){
if(IsFull(S))return false;
S.data[++top] == x;
return true;
}
bool Pop(SqStack &s,ElemType &x){
if(IsEmpty(s))return false;
x = S.data[top--];
retuern ture;
}
bool GetTop(SqStack &s,ElemType &x){
if(isEmpty(s))return false;
x = S.data[top];
return true;
}
共享栈
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];
int top0;
int top1;
}ShSqStack;
void InitStack(ShSqStack &s){
s.top0 = -1;
s.top1 = MaxSize;
}
bool IsFull(ShSqStack &s){
if(s.top0 + 1 == S.top1)return ture;
return false;
}
bool IsEmpty(ShSqStack &s){
if(s.top1 - s.top0 == MaxSize + 1)return ture;
return false;
}
链栈
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkStack;
void InitStack(ListStack &s){
s = (LNode *)malloc(sizeof(LNode));
s -> next = NULL;
}
bool IsEmpty(ListStack s){
if(s -> next == NULL)return ture;
return false;
}
bool Push(ListStack s,ElemType e){
if(s == NULL)return false;
LNode *p = (LNode *)malloc(sizeof(LNode));
if(p == NULL)return false;
p -> data = e;
p -> next = s -> next;
s -> next = p;
retuen ture;
}
boll Pop(ListStack s,ElemType &x){
if(IsEmpty(s))return false;
LNode *p = s -> next;
x = p -> data;
s -> next = p -> next;
return true;
}
bool GetTop(ListStack s,ElemType &x){
if(IsEmpty(s))return false;
x = s -> next -> data;
return ture;
}
队列
队列的定义
线性表是相同元素的有限序列
顺序表是顺序结构存储的线性表
链表是链式结构存储的线性表
栈是先进后出的线性表//FILO
顺序栈是顺序结构存储的先进后出的线性表
链栈是链式结构存储的先进后出的线性表
队列是先进先出的线性表//FIFO
队列可以用顺序结构实现(可以通过对数组游标取模实现循环队列)
队列可以用链式结构实现 考虑带头节点、不带头节点(不用循环队列)
基本操作
- 初始化
- 判空
- 判满
- 入队
- 出队
- 获取队头元素
队列的实现
顺序实现
#define MaxSize 10
typedef struct{
ElemType data[MaxSize];
int front,rear;
}SqQueue;
void InitQueue(SqQueue &q){
q.front = 0;
q.rear = 0;
}
bool IsEmpty(SqQqueue &q){
if(q.front == q.rear)return ture;
return false;
}
bool IsFull(SqQueue &q){
if((q.rear + 1)%MaxSize == q.front)return ture;
return false;
}
//入队
bool EnQueue(SqQueue &q,ElemType x){
if(IsEmpty(q))return false;
q.data[q.rear] = x; //考察边界条件,当插入第一个节点时,rear = 0,要先给data[0]赋值
q.rear = (q.rear + 1)%MaxSize; //构成循环队列,当游标指向最后一个时,+1指回首节点
return false;
}
//出队
bool DeQueue(SqQueue &q,ElemType &x){
if(IsEmpty(&q))return flase;
x = q.data[q.front];
q.front = (q.front + 1)%MaxSize;
return true;
}
//获取队首元素
bool GetFront(SqQueue &q,ElemType &x){
if(IsEmpty(&q))return flase;
x = q.data[q.front];
return ture;
}
链式实现
带头节点
typedef struct LNode{ //定义一个链表节点类型
ElemType data;
struct LNode *next;
}LNode;
typedef struct{ //定义一个包含队首节点和队尾节点的链队列类型
LinkNode *front,*rear;
}LinkQueue;
void InitQueue(LinkQueue &q){
q.front = q.rear =(LinkNode *)malloc(sizeof(LinkNode));
q.front -> next = NULL;
}
bool IsEmpty(LinkQueue &q){
if(q.front -> next == NULL)return true;
return false;
}
//带头节点
bool EnQueue(LinkQueue &q,ElemType e){
LNode *s = (LNode *)malloc(sizeof(LNode));
if(s == NULL)return false;
s -> data = e;
s -> next = NULL;
q.rear -> next = s;
q.rear = s;
return true;
}
//带头结点
bool DeQueue(LinkQueue &q,ElemType &x){
if(IsEmpty(q))return false;
LNode *s = q.front -> next;
x = s -> data;
q.front -> next = s -> next;
if(q.rear = s) //如果s是最后一个节点,修改队尾节点的指向
q.rear = q.front; //队尾节点指向队首,队空
free(s);
return true;
}