数据结构与算法的代码实现(C++版)
- 1. 线性表的顺序表示和实现
- 1.1 线性表的初始化
- 1.2 线性表的销毁
- 1.3 线性表的清空
- 1.4 线性表的长度
- 1.5 判断线性表是否为空
- 1.6 线性表的线性表取值
- 1.7 线性表的顺序查找
- 1.8 线性表的插入
- 1.9 线性表的删除
- 总结
- 2. 线性表的链式表示和实现
- 2.1 单链表
- 2.1.1 单链表的初始化(带头结点的单链表)
- 2.1.2 判断单链表为空
- 2.1.3 单链表的销毁
- 2.1.4 单链表的清空
- 2.1.5 单链表的表长
- 2.1.6 单链表的取值
- 2.1.7 单链表的按值查找
- 2.1.8 单链表的插入
- 2.1.9 单链表的删除第i个结点
- 2.1.10 单链表的头插法
- 2.1.11 单链表的尾插法
- 总结
- 2.2 循环链表
- 2.2.1 循环链表的合并
- 总结
- 2.3 双向链表
- 2.3.1 双向链表的插入
- 2.3.2 双向链表的删除
- 2.4 单链表、循环链表、双向链表的比较
- 2.5 顺序表和链表的比较
- 2.6 线性表的应用
- 2.6.1 线性表的合并
- 2.6.2 有序表的合并(顺序表实现)
- 2.6.3 有序表的合并(链表实现)
- 2.7 线性表的应用
- 2.7.1 稀疏多项式的运算
- 3. 栈的顺序表示和实现
- 3.1 栈的初始化
- 3.2 判断栈是否为空
- 3.3 求顺序栈的长度
- 3.4 清空栈
- 3.5 销毁栈
- 3.6 顺序栈的入栈
- 3.7 顺序栈的出栈
- 4. 栈的链式表示和实现
- 4.1 链栈的初始化
- 4.2 判断链栈是否为空
- 4.3 链栈的入栈
- 4.4 链栈的出栈
- 4.5 取栈顶元素
- 5. 队列的顺序表示和实现
- 5.1 循环队列的初始化
- 5.2 求循环队列的长度
- 5.3 入队
- 5.4 出队
- 5.5 取队头元素
- 6. 队列的链式表示和实现
- 6.1 链队的初始化
- 6.2 链队的销毁
- 6.3 链队的入队
- 6.4 链队的出队
- 6.5 求链队的队头元素
- 7. 串的模式匹配算法
- 7.1 BF算法
- 7.2 BF算法的时间复杂度
- 7.3 KMP算法
1. 线性表的顺序表示和实现
1.1 线性表的初始化
// 线性表的定义
// 其实就是构造结构体:数组+长度
struct SqList
{
ElemType* elem; //顺序线性表的表头
// 也可以这样定义,但是是数组静态分配,上述是动态的,因为可以使用指针指向数组首地址
//ElemType elem[MAX_SIZE];
int length; //顺序线性表的长度
};
// 线性表的初始化
bool InitList(SqList& L)
{
L.elem = new ElemType[MAXSIZE]; // //在堆区开辟内存
if (!L.elem)
{
return false;
}
L.length = 0; //设定空表长度为0
return 1;
}
1.2 线性表的销毁
// 线性表的销毁
void DestroyList(SqList& L)
{
if (L.elem)
{
delete L.elem;
}
}
1.3 线性表的清空
// 线性表的清空
void CLearList(SqList& L)
{
L.length = 0;
}
1.4 线性表的长度
// 线性表的长度
int GetLength(SqList& L)
{
return L.length;
}
1.5 判断线性表是否为空
// 判断线性表是否为空
bool IsEmpty(const SqList& L)
{
// static_cast<bool>(L.length) 的作用是将 L.length 的值转换为布尔值 (bool)。
return static_cast<bool>(L.length);
}
1.6 线性表的线性表取值
// 线性表取值
// 随机存取的时间复杂度是:O(1)
bool GetELem(const SqList &L, const size_t i, ElemType &e)
{
if (i < 1 || i > MAXSIZE)
{
cerr<<"out of range"<<endl;
return false;
}
e = L.elem[i-1];
return true;
}
1.7 线性表的顺序查找
// 线性表的查找
// 顺序查找的时间复杂度是:O(n)
int LocateList(const SqList& L, const ElemType& e)
{
for (int i = 0; i < L.length; i++)
{
if (L.elem[i] == e)
{
return i + 1; //查找成功,返回下标值
}
}
return 0; // 查找失败,返回0
}
1.8 线性表的插入
// 线性表的插入
// 顺序存储的插入的时间复杂度是:O(n)
bool InsertList(SqList& L, const ElemType& e, const int& i)
{
if (i < 1 || i > L.length)
{
return false; // i值不合法
}
if (L.length == MAXSIZE)
{
return false; // 当前存储空间已满
}
// 将位于插入位置之后的元素依次向后挪动一位
for (int j = L.length - 1; j >= i - 1; j--)
{
L.elem[j + 1] = L.elem[j];
}
// 插入元素
L.elem[i - 1] = e;
// 线性表长度+1
L.length++;
return true;
}
1.9 线性表的删除
// 线性表的删除
// 顺序存储的删除的时间复杂度是:O(n)
bool EraseList(SqList& L, const int& i)
{
if (i < 1 || i > L.length)
{
return false; // i值不合法
}
// 从要删除的i位置开始遍历
// 也就是将位于删除位置之后的元素依次向前移一位
for (int j = i; j < L.length; j++)
{
L.elem[j - 1] = L.elem[j];
}
// 线性表长度-1
L.length--;
return true;
}
总结
(1)利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致
(2)在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等
这种存取元素的方法被称为随机存取法
时间复杂度
1.查找、插入、删除算法的平均时间复杂度为O(n)
就是求次数,比如查找
,就是在第几个位置需要的查找次数的累加和 / 元素个数。
比如插入
,就是在第几个位置插入需要移动元素个数的累加和 / n种可能。
比如删除
,就是在第几个位置删除需要移动元素个数的累加和 / n种可能。
空间复杂度
2.显然,顺序表操作算法的空间复杂度S(n)=O(1) (没有
占用辅助空间)
优点
:
1.存储密度大
(结点本身所占存储量 / 结点结构所占存储量,是1:1)
2.可以随机存取
表中任一元素
缺点
:
1.在插入、删除某一元素时,需要移动大量
元素
2.浪费
存储空间
3.属于静态存储形式,数据元素的个数能自由扩充
2. 线性表的链式表示和实现
2.1 单链表
2.1.1 单链表的初始化(带头结点的单链表)
// 单向链表的定义
struct Lnode
{
ElemType data; //结点的数据域
Lnode* next; //结点的指针域, 因为指针还是指向下一结点,结点类型就是Lnode
};
// typedef 都是常用来创建类型别名的工具。
// 将 Lnode * 这个指针类型定义为 LinkList,使得代码更简洁易读。
typedef Lnode* LinkList;
// 链表的初始化
bool InitList(LinkList& L)
{
// LinkList& L = Lnode* L,L其实是Lnode类型的指针
// 表示头指针
L = new Lnode; // 在堆区开辟一个头结点,结点的数据类型为Lnode
//L->next = NULL; // 空表,也就是说头结点的指针指向为空
L->next = nullptr; // // 使用 C++11 的 nullptr,类型安全
return 1;
}
2.1.2 判断单链表为空
// 判断链表是否为空
bool IsEmpty(const LinkList& L)
{
if (L->next)
{
// 非空
return false;
}
else
{
// 为空
return true;
}
}
2.1.3 单链表的销毁
// 单链表的销毁
void DestroyList(LinkList& L)
{
LinkList p;
while (L)
{
// 就是定义1个指针p指向头结点,然后将头指针指向下一个结点,并删除当前p指向的结点
p = L;
L = L->next;
delete p;
}
}
2.1.4 单链表的清空
// 单链表的清空
void CLearList(LinkList& L)
{
LinkList p, q;
p = L->next;
while (p) // p非空,表示还没到表尾
{
q = p->next;
delete p;
p = q;
}
L->next = nullptr; // 头结点指针域为空
}
2.1.5 单链表的表长
// 单链表的长度
int GetLength(LinkList& L)
{
LinkList p;
p = L->next; // 将p指向首元结点
int i = 0; // 计数
while (p)
{
// 遍历单链表,统计结点数
i++;
p = p->next;
}
return i;
}
2.1.6 单链表的取值
// 单链表的取值
bool GetElem(const LinkList& L, const int& i, const ElemType& e)
{
// 因为逻辑顺序和物理顺序相差1,我们说的取第3个数,3代表是物理顺序。
// 所以我们定义1个指针指向头结点,并当前节点为1,开始遍历直到i=j停止循环
LinkList p = L->next;
int j = 1; // 计数器
while (p && i > j)
{
p = p->next;
j++;
}
if (!p || j > i) return false; // 第i个元素不存在
e = p->data;
return true;
}
2.1.7 单链表的按值查找
// 单链表的按值查找,返回L中值为e的数据元素的地址
// 时间复杂度:0(n)
LinkList LocateElem(LinkList& L, ElemType& e)
{
// 在线性表L中查找值为e的数据元素
// 找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
LinkList p = L->next;
while (p && p->data != e)
{
p = p->next;
}
return p;
}
// 单链表的按值查找,返回L中值为e的位置序号
int LocateElem(LinkList& L, ElemType& e)
{
// 返回L中值为e的数据元素的位置序号,查找失败返回
LinkList p = L->next;
int j = 1;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p)
{
return j;
}
else
{
return 0;
}
}
2.1.8 单链表的插入
// 单链表的插入
// 时间复杂度:0(1)
bool InsertList(LinkList& L, const int& i, const ElemType& e)
{
LinkList p = L;
int j = 0;
while (p && j < i -1) // 寻找第i - 1个结点, p指向i - 1结点
{
p = p->next;
j++;
}
if (!p || j > i - 1)
{
return 0; // i大于表长+1或者小于1,插入位置非法
}
// 生成新结点s,将结点s的数据域置为e
LinkList s = new Lnode;
s->data = e;
// 将结点s插入L中
s->next = p->next;
p->next = s;
}
2.1.9 单链表的删除第i个结点
// 单链表的删除
// 将单链表L中第i个数据元素删除
// 时间复杂度:0(1)
bool EraseList(LinkList& L, const int& i, const ElemType& e)
{
LinkList p = L, q;
int j = 0;
while (p && j < i - 1) // 寻找第 i 个结点的前驱
{
p = p->next;
j++;
}
if (!(p->next) || j > i - 1)
{
return 0; // 删除位置不合理
}
q = p->next; // 临时保存被删结点的地址以备释放
p->next = q->next; // 改变删除结点前驱结点的指针域
e = q->data;
delete q;
return true;
}
2.1.10 单链表的头插法
头插法是倒序插入,先插入An,再是An-1,直到A1;而尾插法是正序,先插A1,再一直到An。
// 单链表的头插
// n表示结点个数
// 算法时间复杂度:O(n)
void CreatListHead(LinkList& L, int n)
{
L = new Lnode;
L->next = nullptr; // 先建立一个带头结点的单链表
for (int i = 0; i < n; i++)
{
LinkList p = new Lnode;
cin >> p->data; // 输入元素值
p->next = L->next; // 插入到表头
L->next = p;
}
}
2.1.11 单链表的尾插法
// 单链表的尾插
// 算法时间复杂度:O(n)
void CreatListTail(LinkList& L, int n)
{
L = new Lnode;
L->next = nullptr;
LinkList r = L; // 尾指针r指向头结点
for (int i = 0; i < n; i++)
{
LinkList p = new Lnode;
cin >> p->data; // 生成新结点,输入元素值
p->next = nullptr;
r->next = p; // 插入到表尾
r = p; // r指向新的尾结点
}
}
总结
1.基本上链表的操作
,都是和指针
挂钩的,就是额外定义
1个指针,因为直接对头指针操作,很容易找不到next元素了。比如销毁
,额外的指针从头结点开始;如果是清空
,则从首元结点开始;比如,计数
,额外的指针从头结点开始。
2.并且如果是插入等操作,先顾后面
的结点,如果先链接前面的结点,后面的结点就找不到
了。
2.2 循环链表
2.2.1 循环链表的合并
// 两个链表的合并
// 算法时间复杂度:O(1)
LinkList ConnectList(LinkList& Ta, LinkList& Tb)
{
// 假设Ta、Tb都是非空的单循环链表
LinkList p = Ta->next; // ①p存表头结点
Ta->next = Tb->next->next; // ②Tb表头连结Ta表尾
delete Tb->next; // ③释放Tb表头结点
Tb->next = p; // ④修改指针
return Tb;
}
总结
1.单链表使用头指针
,比较方便;而单循环链表中,使用尾指针
,比较方便。
2.单链表必须通过头指针
逐个遍历访问每个结点,而单循环链表可以通过任意
一个结点出发。
2.3 双向链表
2.3.1 双向链表的插入
// 双向链表的定义
// 首先定义了一个结构体 DuLnode,然后通过 typedef 定义了一个指向该结构体的指针类型 DuLinkList。
// 这里 typedef 的部分只定义了 DuLnode* 的别名 DuLinkList,而 DuLnode 是单独的结构体定义。
typedef struct DuLnode
{
ElemType data; //结点的数据域
DuLnode* prior, * next;
}*DuLinkList;
// 双向链表的初始化
void InitList(DuLinkList& L)
{
L = new DuLnode;
L->prior = nullptr;
L->next = nullptr;
}
// 双向链表的第i个位置插入元素
bool InsertList(DuLinkList& L, const int& i, const ElemType& e)
{
// 在带头结点的双向循环链表L中第i个位置之前插入元素e
DuLinkList p = L->next;
int j = 1;
while (p->next && j < i) // 移动指针到i处
{
p = p->next;
j++;
}
if (j < i || j < 1)
{
return 0; // i大于表长插入位置非法
}
//在堆区创建要插入的结点
DuLinkList s = new DuLnode;
s->data = e;
// 重新建立链接关系, 将结点s插入链表中
s->prior = p->prior; //第一步:s的前趋等于p的前趋
p->prior->next = s; //第二步,用p的前趋结点的next指向插入元素s,更改了第一条链
s->next = p; //第三步:s的后继指向p
p->prior = s; //第四步:p的前趋改为指向s,更改了第二条链
return true;
}
2.3.2 双向链表的删除
// 双向链表的删除某个元素
bool ListErase_DuL(DuLinkList& L, const int& i, const ElemType& e)
{
DuLinkList p = L->next;
int j = 1;
while (p && j < i) // 寻找第i个结点,并令p指向其前驱
{
p = p->next;
j++;
}
if (j < i || j < 1)
{
return 0; // i大于表长插入位置非法
}
p->prior->next = p->next;
p->next->prior = p->prior;
delete p;
return true;
}
2.4 单链表、循环链表、双向链表的比较
2.5 顺序表和链表的比较
2.6 线性表的应用
2.6.1 线性表的合并
// 线性表的合并
// 通用算法:顺序表和链表都可以
void Union(LinkList& Ta, LinkList Tb)
{
La_len = ListLength(Ta);
Lb_len = ListLength(Tb);
for (int i = 1; i <= Lb_len; i++)
{
GetElem(Lb, i, e);
if (!Locate(Ta, e))
{
ListInsert(&Ta, ++La_len, e);
}
}
}
2.6.2 有序表的合并(顺序表实现)
// 用顺序存储结构合并两个有序表
void MergeList(const SqList& list1, const SqList& list2, SqList& list3)
{
int* pa = list1.elem;
int* pa_last = list1.elem + list1.length - 1;
int* pb = list2.elem;
int* pb_last = list2.elem + list2.length - 1;
list3.length = list1.length + list2.length;
list3.elem = new int[list3.length];
int* pc = list3.elem;
while (pa <= pa_last && pb <= pb_last) // 两个表都非空
{
// 依次“摘取”两表中值较小的结点
if (*pa <= *pb)
{
*pc = *pa;
pa++;
pc++;
}
else
{
*pc = *pb;
pb++;
pc++;
}
}
// pb表已到达表尾,将pa中剩余元素加入pc
while (pa <= pa_last)
{
*pc = *pa;
pa++;
pc++;
}
// pa表已到达表尾,将pb中剩余元素加入pc
while (pb <= pb_last)
{
*pc = *pb;
pb++;
pc++;
}
}
2.6.3 有序表的合并(链表实现)
// 用链式存储结构合并两个有序表
// 时间复杂度是:O(n)
// 空间复杂度是:O(1)
void MergeList(const LinkList& La, const LinkList& Lb, LinkList& Lc)
{
LinkList pa = La->next;
LinkList pb = Lb->next;
Lc = La;
// 用La的头结点作为Lc的头结点
LinkList pc = Lc;
while (pa && pb)
{
if (pa->data <= pb->data)
{
pc->next = pa;
pc = pa;
pa = pa->next;
}
else
{
{
pc->next = pb;
pc = pb;
pb = pb->next;
}
}
pc->next = pa ? pa : pb;
delete pb;
}
}
2.7 线性表的应用
2.7.1 稀疏多项式的运算
// 存放多项式的单链表的定义
struct PNode
{
float coef; //数据域:系数
int expn; //数据域:指数
PNode* next; //指针域
};
typedef PNode* Polynomial;
// 多项式创建
void CreatePolyn(Polynomial& p, int n)
{
// 建立—个带头结点的单链表
p = new PNode;
p->next = nullptr;
for (int i = 0; i < n; i++)
{
Polynomial s = new PNode; // 生成新结点
cin >> s->coef >> s->expn; // 输入系数和指数
Polynomial pre = p;
Polynomial q = p->next;
while (q && q->expn < s->expn)
{
pre = q;
q = q->next;
}
s->next = q;
pre->next = s;
}
}
// 稀疏多项式的相加
void Merge(Polynomial& La, Polynomial& Lb, Polynomial& Lc)
{
Polynomial pa = La->next;
Polynomial pb = Lb->next;
Lc = La;
Polynomial pc = Lc; // pc指向多项式的当前结点,初值为pa的头结点
while (pa && pb)
{
// 指数相同的情况下
if (pa ->expn == pb->expn)
{
// 系数相加
pa->coef += pb->coef;
pc->next = pa;
// pa、pb、pc指针均向后移
pa = pa->next;
pb = pb->next;
pc = pc->next;
}
// pa小,pc与pa连接
else if (pa->expn < pb->expn)
{
pc->next = pa;
// pa、pc指针均向后移
pa = pa->next;
pc = pc->next;
}
// pb小,pc与pb连接
else if (pa->expn > pb->expn)
{
pc->next = pb;
// pb、pc指针均向后移
pb = pb->next;
pc = pc->next;
}
}
// 将pa 或者pb中余下的接到pc后
pc->next = (pa ? pa : pb);
delete Lb;
}
3. 栈的顺序表示和实现
3.1 栈的初始化
// 顺序栈的定义
struct SqStack
{
ElemType* base; // 栈底指针
ElemType* top; // 栈顶指针
int stacksize; // 栈可用最大容量
};
// 顺序栈的初始化
bool InitStack(SqStack& S) // 构造一个空栈
{
S.base = new ElemType[MAXSIZE];
if (!S.base)
{
return false;
}
//栈顶指针等于栈底指针
S.top = S.base;
S.stacksize = MAXSIZE;
}
3.2 判断栈是否为空
// 判断顺序栈是否为空
bool IsEmpty(SqStack& S)
{
if (S.base == S.top)
{
return true;
}
else
{
return false;
}
}
3.3 求顺序栈的长度
// 求顺序栈的长度
int StackLength(SqStack& S)
{
return static_cast<int>(S.top - S.base);
}
3.4 清空栈
// 清空栈
bool ClearStack(SqStack& S)
{
if (S.base)
{
S.base == S.top;
return true;
}
else
{
return false;
}
}
3.5 销毁栈
// 销毁顺序栈
bool DestoyStack(SqStack& S)
{
if (S.base)
{
delete S.base;
S.base = S.top = nullptr;
S.stacksize = 0;
return true;
}
}
3.6 顺序栈的入栈
// 入栈
bool Push(SqStack& S, ElemType& e)
{
// (1)判断是否栈满, 若满则出错(上溢)
if (static_cast<int>(S.top-S.base) == S.stacksize)
{
return false;
}
// (2)元素e压入栈顶
*S.top = e;
// (3)栈顶指针加1
S.top++;
}
3.7 顺序栈的出栈
// 出栈
bool Pop(SqStack& S, ElemType& e)
{
// (1)判断是否栈空,若空则出错(下溢)
if (S.base == S.top)
{
return false;
}
// (2)栈顶指针减1
S.top--;
// (3)获取栈顶元素
e = *S.top;
}
4. 栈的链式表示和实现
4.1 链栈的初始化
// 链栈的定义
typedef struct StackNode
{
ElemType data; // 数据域
StackNode* next; // 栈顶指针
}*LinkStack;
// 链栈的初始化
bool InitStack(LinkStack& S) // 构造一个空栈
{
// 这里链栈没有使用链表的头结点,是因为这里会更麻烦
S = nullptr;
return true;
}
4.2 判断链栈是否为空
// 判断链栈是否为空
bool IsEmpty(LinkStack& S)
{
if (S==nullptr)
{
return true;
}
else
{
return false;
}
}
4.3 链栈的入栈
// 入栈
bool Push(LinkStack& S, ElemType& e)
{
LinkStack P = new StackNode; // 生成新结点P
P->data = e; // 将新结点数据域设置为e
P->next = S; // 将新结点插入栈顶
S = P; // 修改栈顶指针
return true;
}
4.4 链栈的出栈
// 出栈
bool Pop(LinkStack& S, ElemType& e)
{
if (S==nullptr)
{
return false;
}
e = S->data;
LinkStack P = S;
S = S->next;
delete P;
return true;
}
4.5 取栈顶元素
// 获取栈顶元素
ElemType GetTop(LinkStack& S)
{
if (S != nullptr)
{
return S->data;
}
}
5. 队列的顺序表示和实现
5.1 循环队列的初始化
// 队列的定义
typedef struct SqQueue
{
ElemType* base; // 初始化的动态分配存储空间
int front; // 头指针,若队列不空,指向队列头元素
int rear; // 尾指针,若队列不空,指向队列尾元素的下一个位置
};
// 队列的初始化
bool InitQueue(SqQueue& Q) // 构造一个空栈
{
Q.base = new ElemType[MAXSIZE]; // 分配数组空间
if (!Q.base)
{
return false; // 分配失败
}
// 头指针尾指针置为0,队列为空
Q.front = 0;
Q.rear = 0;
}
5.2 求循环队列的长度
// 求队列的长度
int GetLength(SqQueue& Q)
{
return (Q.rear - Q.base + MAXSIZE) % MAXSIZE;
}
5.3 入队
// 循环队列入队
bool EnQueue(SqQueue& Q, ElemType& e)
{
if ((Q.rear + 1) % MAXSIZE == Q.front)
{
return false; // 队满
}
Q.base[Q.rear] = e; // 新元素加入队尾
Q.rear = (Q.rear + 1) % MAXSIZE; // 队尾指针+1
return true;
}
5.4 出队
// 循环队列出队
bool DeQueue(SqQueue& Q, ElemType& e)
{
if (Q.rear == Q.front)
{
return false; // 队空
}
Q.base[Q.front] = e; // 保存队头元素
Q.front = (Q.front + 1) % MAXSIZE; // 队头 指针+1
return true;
}
5.5 取队头元素
// 取队头元素
ElemType GetHead(SqQueue& Q)
{
if (Q.front != Q.rear) // 队列不为空
{
return Q.base[Q.front];
}
}
6. 队列的链式表示和实现
6.1 链队的初始化
// 链队的初始化
bool InitQueue(LinkQueue& Q)
{
Q.front = Q.rear = new Qnode;
Q.front->next = nullptr;
return true;
}
6.2 链队的销毁
// 链队的销毁
bool DestroyQueue(LinkQueue& Q)
{
while (Q.front)
{
Qnode* p = Q.front->next;
delete Q.front;
Q.front = p;
}
return true;
}
6.3 链队的入队
// 入队
bool EnQueue(LinkQueue& Q, const ELemType& e)
{
//把元素插在最后面
Qnode* p = new Qnode;
p->data = e;
p->next = nullptr;
Q.rear->next = p;
Q.rear = p;
return true;
}
6.4 链队的出队
// 出队
bool DeQueue(LinkQueue& Q, const ELemType& e)
{
if (Q.front == Q.rear) // 队空
{
return false;
}
Qnode* p = Q.front->next;
e = p->data;
Q.front->next = p->next;
delete p;
}
// 出队
bool DeQueue(LinkQueue& Q, const ELemType& e)
{
if (Q.front == Q.rear) // 队空
{
return false;
}
Qnode* p = Q.front->next;
e = p->data;
Q.front->next = p->next;
if (Q.rear == p)
{
//
Q.rear = Q.front;
}
delete p;
}
6.5 求链队的队头元素
// 求链队的队头元素
bool GetHead(LinkQueue& Q, const ELemType& e)
{
if (Q.front == Q.rear) // 队空
{
return false;
}
e = Q.front->next->data;
return true;
}
7. 串的模式匹配算法
7.1 BF算法
#define MAXLEN 25
// 串的顺序存储定义
struct SString
{
char ch[MAXLEN]; // 存储串的一维数组
int length; // 串的当前长度
};
int index_BF(SString S, SString T)
{
// 如果不是从第一个字符开始匹配,要从指定的位置开始查找,将i参数化 --> int i = pos;
int i = 1, j = 1;
while (i <= S.length && j <= T.length)
{
// 主串和子串当前字符相等
if (S.ch[i] == T.ch[j])
{
// 依次匹配下一个字符
i++;
j++;
}
// 主串、子串指针回溯重新开始下-次匹配
else
{
// 主串回到下一个字符
i = i - j + 2;
// 子串回到第一个字符等到下一次匹配
j = 1;
}
}
if (j >= T.length)
{
return i - T.length; // 返回匹配的第一个字符的下标
}
else
{
return 0; // 模式匹配不成功
}
}
7.2 BF算法的时间复杂度
7.3 KMP算法
理解
:
k-1就是比较元素的个数,末尾是从j-1开始,前端是首元素1开始。比如,有2个公共前后缀,那k - 1 = 2,k = 3。找不到相等的公共前后缀,就是其他情况,k = 1。
// KMP算法
int index_BF(SString S, SString T)
{
// 如果不是从第一个字符开始匹配,要从指定的位置开始查找,将i参数化 --> int i = pos;
int i = 1, j = 1;
while (i <= S.length && j <= T.length)
{
if (j == 0 || S.ch[i] == T.ch[j])
{
// 依次匹配下一个字符
i++;
j++;
}
// 主串、子串指针回溯重新开始下-次匹配
else
{
j = next[j]; // i不变,j后退
}
}
if (j >= T.length)
{
return i - T.length; // 返回匹配的第一个字符的下标
}
else
{
return 0; // 模式匹配不成功
}
}