数据结构与算法的代码实现(C++版)

news2024/9/21 8:03:00

数据结构与算法的代码实现(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;  // 模式匹配不成功
	}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2097757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EPLAN如何在插入设备的时候修改部件的数据?

EPLAN如何在插入设备的时候修改部件的数据&#xff1f; 默认情况下&#xff0c;插入部件的时候&#xff0c;部件的数据是无法更改的&#xff0c;那么如何设置在插入部件的时候对部件的数据进行修改呢&#xff1f; 具体可参考以下内容&#xff1a; 设置完成后&#xff0c;点击右…

QT实现简易记事本

QT的Qmainwindow实现简易记事本,不带有UI界面。 1、功能展示 1、编辑文件时&#xff0c;文件名左上角有个“*”,代表文件未保存 2、菜单栏有文件、编辑的选项 3、工具栏有保存、新建、打开、调节字体、调节颜色。 4、文件菜单中有&#xff0c;新建&#xff0c;保存、打开、…

【C语言从不挂科到高绩点】06-流程控制语句-循环语句

Hello&#xff01;彦祖们&#xff0c;俺又回来了&#xff01;&#xff01;&#xff01;&#xff0c;继续给大家分享 《C语言从不挂科到高绩点》课程 本套课程将会从0基础讲解C语言核心技术&#xff0c;适合人群&#xff1a; 大学中开设了C语言课程的同学想要专升本或者考研的…

2024测试开发必知必会:Pytest框架实战!

应用场景&#xff1a; pytest 框架可以解决我们多个测试脚本一起执行的问题。 它提供了测试用例的详细失败信息&#xff0c;使得开发者可以快速准确地改正问题。它兼容最新版本的 Python。它还兼容 unittest、doctest 和 nose&#xff0c;开箱即用。接下来我们详细了解下pyte…

LLM与知识图谱

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

昆仑山矿泉水《大湾区旅行者》正式开机,携手共赴一场净化心灵之旅

在这个快节奏的时代&#xff0c;我们总在寻找一片能让心灵得以栖息的地方。今年&#xff0c;昆仑山矿泉水与深圳卫视再度携手&#xff0c;推出充满人文温情与自然探索的慢生活旅行综艺《大湾区旅行者》&#xff0c;目前已于8月29日开机启航。本次旅行&#xff0c;昆仑山矿泉水与…

废品回收小程序,从传统到现代化的回收模式

在数字化发展的当下&#xff0c;废品回收行业也进行了更新换代&#xff0c;由传统“喊卖”&#xff0c;到目前的线上回收&#xff0c;将互联网小程序与废品回收相结合&#xff0c;打造一个线上回收模式&#xff0c;让大众可以直接通过手机进行回收。小程序的出现不仅解决了传统…

四层神经网络,反向传播计算过程;四层神经网络中:y的函数公式是什么

目录 四层神经网络,反向传播计算过程 网络结构 前向传播 损失函数 反向传播 1. 计算输出层到隐藏层 2 的梯度 2. 计算隐藏层 2 到隐藏层 1 的梯度 3. 计算输入层到隐藏层 1 的梯度 梯度下降更新 四层神经网络,反向传播计算过程 在四层神经网络中,使用均方差损失函数…

H264结构及RTP封装

H264是一种针对视频的压缩编码方式。 一、压缩方法 H264主要基于以下几种方法&#xff0c;将数据进行压缩&#xff1a; 1.帧内预测压缩&#xff1a;解决空间域数据冗余的问题 2.帧间预测压缩&#xff1a;&#xff08;运动估计与补偿&#xff09;解决时间域数据冗余的问题 3…

项目实训:创建一张贺卡以及一只盒子——WEB开发系列27

以下是两道关于基础 CSS 盒模型和其他盒子相关特性的练习题&#xff0c;适合测试对这些概念的掌握程度&#xff0c;通过实际的设计任务来深入理解这些概念。 练习题 1: 设计一张中秋节海报贺卡 任务描述 制作一张精美的中秋节海报贺卡&#xff0c;用于庆祝这个传统节日。你的…

visual studio 2022更新以后,之前的有些工程编译出错,升级到Visual studio Enterprise 2022 Preview解决

系列文章目录 文章目录 系列文章目录前言一、解决方法 前言 今天遇到一个问题&#xff1a;visual studio 2022升级成预览版以后&#xff0c;之前的有些工程编译出错。首先代码、项目设置都没有改变&#xff0c;只是更新了visual studio 2022。 在编译工程时&#xff0c;编译器…

Source-code-of-charging-云快充协议1.5+互联互通协议+新能源汽车充电桩系统

介绍 SpringBoot 框架&#xff0c;充电桩平台充电桩系统充电平台充电桩互联互通协议云快充协议1.5-1.6协议新能源汽车二轮车公交车二轮车充电-四轮车充电充电源代码充电平台源码Java源码 软件架构 软件功能 小程序端&#xff1a;城市切换、附近电站、电桩详情页、扫码充电、…

视频智能分析打手机检测算法安防监控打手机检测算法应用场景、算法源码、算法模型介绍

随着智能手机的普及&#xff0c;手机已成为人们生活中不可或缺的一部分。然而&#xff0c;在某些场合&#xff0c;如驾驶、会议、学校课堂等&#xff0c;不当使用手机可能会导致安全隐患或干扰他人。因此&#xff0c;开发出一种能够准确识别并阻止不当使用手机的行为检测算法显…

技术故障和情感危机,哪个更难修复?

在数字化时代&#xff0c;软件服务的稳定性至关重要。 然而&#xff0c;数字时代的感情同样脆弱! 超单身时代 这一概念主要用来描述一个国家或地区中单身人口占据较大比例的社会现象。以日本为例&#xff0c;根据日本国立社会保障与人口问题研究所的推算&#xff0c;到2035年…

[bevformer渲染可视化] 2d框可视化 并可视化出小目标

可视化代码&#xff1a; 代码使用方法&#xff1a; 1.复制代码全文到任意python文件中 2.下载nuscenes v1.0-mini数据集,修改数据集路径&#xff0c;保证能读取到数据集 3.按照需求修改代码&#xff08;本文是2dbox面积的面积和整个图片的面积比小于0.03视为小目标&#xff0…

电流互感器的操作、类型和极性注意事项

了解电流互感器及其在将大电流转换为小电流方面的重要作用&#xff0c;包括绕组比、类型以及保持计量极性的重要性。 电流互感器 (CT) 主要用于改变电压值。第二个结果是它们也会改变当前值。 升压变压器减少次级绕组中的电流&#xff0c;降压变压器增加次级绕组中的电流&…

vscode Git代码版本回退

在项目文件夹右键打开git bash&#xff0c;输入命令git log 查看提交的历史 commit&#xff0c;git log --prettyoneline将版本信息压缩到一行 使用git log可能会显示不全&#xff0c;按enter逐行查看&#xff0c;按end跳至末尾查看完成后&#xff0c;按q即可退出 找到自己想…

云计算实训40——部署nmt、部署project_exam_system项目

NMT N指的是nginx M指的是mysql T指的是tomcat 一、环境准备 #使用基础的docker指令来创建镜像&#xff0c;实现项目的发布 #安装docker、编辑daemon.json文件、安装docker-compose编排容器、启动docker #拖拽docker.sh的脚本[rootdocker--1 ~]# rz -Erz waiting to rec…

Qt QPushButton 按钮右上角增加小红点

简述 在项目里有时候应用类按钮在模块上新时&#xff0c;需要增加小红点提示。 效果图&#xff1a; 实现 实现的方法有很多 &#xff0c;下面介绍超简单的一种。 直接在按钮上贴一个 QLabel 即可。 #pragma once#include <QtWidgets/QWidget> #include "ui_paf…

宁武县乡村e镇招商引资暨产品推介会在京举办

2024年8月26日&#xff0c;宁武县乡村e镇招商引资暨产品推介会在北京市朝阳区五洲皇冠国际酒店举办。此次活动由山西省宁武县委、县政府主办&#xff0c;中国联合国采购促进会、中国商业文化研究会乡村振兴工作委员会协办&#xff0c;旨在宣传推介宁武县丰富的自然资源和农特产…