C++实现《数据结构与算法》相关算法--线性表

线性表相关算法实现

在数据结构与算法学习过程中,为更加深刻理解算法的实现,本文对课程中线性表部分算法进行了实现。本文仅提供算法代码参考,相关算法详解请参考青岛大学王卓老师的视频课程:数据结构与算法基础(青岛大学-王卓)

#include <cstdlib>
#include <iostream>
using namespace std;
// 函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define MAXSIZE 100
//Status 是函数的类型,其值是函数结果状态代码
typedef int Status;

顺序表相关算法实现

定义数据元素结构及表结构

// 定义数据元素类型,该数据元素仅有一个类型为int的数据项
typedef int ElemType; 

typedef struct {
	ElemType *elem;// 指针数组
	int length;// 顺序表的长度,访问即可获取表长
} SqList; //定义顺序表

SqList L; //定义变量L,L是SqList这种类型的,是个顺序表

初始化顺序表

Status InitList_Sq(SqList& L) {         // 构造一个空的顺序表L
	L.elem = new ElemType[MAXSIZE];     // 为顺序表分配空间
	if(!L.elem) exit(OVERFLOW);        // 储存分配失败
	L.length = 0;                       // 空表长度置为0
	return OK;
}

销毁顺序表

与清空顺序表的区别是,销毁顺序表需要释放全部存储空间

void DestroyList(SqList& L) {
	if (L.elem) delete L.elem; //释放存储空间
}

清空顺序表

此操作相当于用L.lenght=0标记该表为空,在对表进行更新或插入时,直接覆盖数据即可。若需要释放存储空间,可使用销毁算法。

void ClearList(SqList& L) {
	L.length = 0; //将线性表的长度置为0
}

计算顺序表长度

直接访问L.length即可,不必遍历表。

int GetLength(SqList L) {
	return (L.length);
}

判断顺序表是否为空

以L.length=0为依据判断表空。

int IsEmpty(SqList L) {
	if (L.length == 0) return 1;
	else return 0;
}

获取顺序表指定位置i的数据元素

int GetElem(SqList L, int i, ElemType& e) {
	if (i<1 || i>L.length) return ERROR;
	// 判断i值是否合理,若不合理,返回ERROR
	e = L.elem[i - 1]; //第i-1的单元存储着第i个数据
	return OK;
}

按值查找顺序表中数据元素的位置(下标)

// 按值查找
int LocateElem(SqList L, ElemType e) {
	//在线性表L中查找值为e的数据元素,返回其序号
	for (int i = 0; i < L.length; i++)
		if (L.elem[i] == e) return i + 1; //查找成功,返回序号
	return 0;//查找失败,返回0
}

在顺序表中插入数据元素

下代码中ERROR可以替换为对应的错误提示。

// 插入算法
Status ListInsert_Sq(SqList& L, int i, ElemType e) {
	if (i<1 || i>L.length + 1)return ERROR;  //判断i值不合法,返回ERROR
	if (L.length == MAXSIZE)return ERROR;    //当前存储空间已满
	for (int j = L.length - 1; j >= i - 1; j--)
		L.elem[j + 1] = L.elem[j];           //插入位置及之后的元素后移
	L.elem[i - 1] = e;                       //将新元素e放入第i个位置
	L.length++;                              //表长增1
	return OK;
}

删除顺序表中指定位置i的数据元素

// 表删除
Status ListDelete_Sq(SqList& L, int i,ElemType &e) {
	if ((i < 1) || (i > L.length))return ERROR; //判断i值不合法
	e = L.elem[i - 1];
	for (int j = i; j <= L.length - 1; j++)
		L.elem[j - 1] = L.elem[j];             //被删除元素之后的元素向前迁移
	L.length--;                                //表长减1
	return OK;
}

两个有序顺序表(从小到大)合并,新表也是从小到大的有序表

// 有序表的合并 - 顺序表实现
void MergeList_Sq(SqList LA, SqList LB, SqList& LC) {
	
	ElemType *pa = LA.elem;
	ElemType *pb = LB.elem; // 指针pa和pb的初值分别指向两个表的第一个元素
	LC.length = LA.length + LB.length; // 新表长度为待合并两表的长度之和
	LC.elem = new ElemType[LC.length]; // 为合并后的新表分配一个数组空间
	ElemType *pc = LC.elem;
	ElemType* pa_last = LA.elem + LA.length - 1; // 指针pa_last指向LA表的最后一个元素
	ElemType* pb_last = LB.elem + LB.length - 1; // 指针pa_last指向LB表的最后一个元素

	while (pa <= pa_last && pb <= pb_last) {
		if (*pa <= *pb) *pc++ = *pa++; // 依次摘取两表中值较小的结点
		else *pc++ = *pb++;
	}
	while (pa <= pa_last) *pc++ = *pa++;
	while (pb <= pb_last) *pc++ = *pb++;
}

测试代码及输出结果

测试代码:
int main()
{
	
	SqList LA;
	SqList LB;
	SqList LC;
	InitList_Sq(LA);
	InitList_Sq(LB);
	InitList_Sq(LC);
	int e;
	for (int i = 0; i <= 5; i++) { // LA输入5个数据元素
		cout << "为LA输入元素" << endl; // 从小到大有序输入数据元素,创建有序表
		cin >> e;
		ListInsert_Sq(LA, i, e);
	};
	for (int j = 0; j <= 3;j++) { // LB输入3个数据元素
		cout << "为LB输入元素" << endl; // 从小到大有序输入数据元素,创建有序表
		cin >> e;
		ListInsert_Sq(LB, j, e);
	};
	cout << "合并前:" << endl;
	for (int i = 0; i < LA.length; i++) cout << "LA: " << LA.elem[i] << endl;
	for (int i = 0; i < LB.length; i++) cout << "LB: " << LB.elem[i] << endl;

	MergeList_Sq(LA, LB, LC);
	cout << "合并后:" << endl;
	for (int i = 0; i < LC.length; i++) cout << "LC: " << LC.elem[i] << endl;
	
	system("pause");
	return 0;
}
输出结果:

在这里插入图片描述

链表相关算法实现

定义数据元素结构及表结构

这是一个具有学生姓名,学号,分数三个数据项的数据元素。此处数据元素类型ElemType与前面顺序表冲突,不可在同一程序中同时使用。

typedef struct{
	string num;   //数据域
	string name;
	double score;
}ElemType;


typedef struct Lnode { //声明结点的类型和指向节点的指针类型
	ElemType data;     //结点的数据域
	struct Lnode *next;//结点的指针域
}Lnode, *LinkList;      //LinkList为指向结构体Lnode的指针类型

单链表初始化

Status InitList_L(LinkList& L) {
	// 创建头结点
	L = new Lnode; //或L = (LinkList)malloc(sizeof(LNode));
	L->next = NULL;//L现在是个空表
	return OK;
}

判断单链表是否为空

判断头结点的next域是否为空,若为空,则为空表。

int ListEmpty(LinkList L) { //若L为空表,则返回1,否则返回0
	if (L->next)
		return 0;
	else
		return 1;
}

销毁单链表

访问next域遍历整个链表,逐个释放数据结点的存储空间。

Status DestroyList_L(LinkList& L) { //销毁单链表L
	Lnode* p;   // 或LinkList p;
	while (L) { // 判断L指针是否为null
		p = L;
		L = L->next;
		delete p;
	}
	return OK;
}

清空单链表

销毁除头结点外的全部数据节点,并将头结点的next域置为NULL。

Status ClearList(LinkList& L) { //将L重置为空表
	Lnode* p, *q; // 或LinkList p,q;
	p = L->next;
	while (p) {   // 判断是否到表尾
		q = p->next;
		delete p;
		p = q;
	}
	L->next = NULL;// 头结点指针域为空
	return OK;
}

计算单链表长度

通过next域遍历单链表,计算表长。

int ListLength_L(LinkList L) {// 返回L中数据元素的个数
	LinkList p;
	p = L->next; // p指向第一个结点
	int i = 0;
	while (p) {  // 遍历单链表,统计结点数
		i++;
		p = p->next;
	}
	return i;
}

获取指定位置i的数据元素

Status GetElem_L(LinkList L, int i, ElemType& e) {
	//获取指定位置的数据元素,通过e返回
	LinkList p;
	p = L->next; int j = 1;  //初始化
	while (p && j < i) { //向后扫描,直到p指向第i个元素或p为空
		p = p->next; ++j;
	}
	if (!p || j > i) return ERROR; //第i个元素不存在
	e = p->data;                   //取第i个元素
	return OK;

}

根据数据项的值查找数据元素,并返回结点

// 根据数据域中name字段查找
Lnode* LocateElem_L_Lnode(LinkList L, ElemType e) {
	// 在线性表L中查找值为e的数据元素
	// 找到,则返回L中值为e的数据元素地址,查找失败返回NULL
	LinkList p;
	p = L->next;
	while ((p) && ((p->data.name) != e.name))
		p = p->next;
	return p;
}

根据数据项的值查找数据元素,并返回其在表中的位置i

// 根据数据域中name字段查找
int LocateElem_L_index(LinkList L, string name) {
	// 在线性表L中查找值为e的数据元素
	// 找到,则返回L中值为e的数据元素位置序号,查找失败返回0
	LinkList p;
	p = L->next; int j = 1;
	while ((p) && ((p->data.name) != name))
	{
		p = p->next;
		j++;
	}
	if (p) return j;
	else return 0;
}

在单链表中插入数据元素

// 在L中第i个元素之前插入数据元素e
Status ListInsert_L(LinkList& L, int i, ElemType e) {
	LinkList p = L; int j = 0;
	while (p && j < i - 1) { p = p->next; ++j; }// 寻找第i-1个结点,p指向i-1结点
	if (!p || j > i - 1)return ERROR;           // i大于表长+1或小于1,插入位置非法
	LinkList s = new Lnode; s->data = e;        // 生成新结点s,将结点s的数据域置为e
	s->next = p->next;                          // 将结点s插入L中
	p->next = s;
	return OK;
}

删除单链表中第i个元素

// 将线性表L中第i个数据元素删除
Status ListDelete_L(LinkList& L, int i, ElemType& e) {
	LinkList p = L; int j = 0;
	while (p->next && j < i - 1) {
		p = p->next;
		++j;
	}// 寻找第i个结点,并令p指向其前驱
	if (!(p->next) || j > i - 1) return ERROR; // 删除位置不合理
	LinkList q = p->next;                      // 临时保存被删结点的地址以备释放
	p->next = q->next;                         // 改变删除结点前驱结点的指针域
	e = q->data;                               // 保存删除结点的数据域
	delete q;                                  // 释放删除结点的空间
	return OK;
}

使用头插法建立单链表

// 头插法建立单链表
void CreateList_H(LinkList& L, int n) {
	InitList_L(L);   // 建立空链表
	int i = 0;
	for(i=n;i>0;--i){
		LinkList p = new Lnode; // 生成新结点

		cout << "输入data.name" << endl;
		cin >> p->data.name;

		cout << "输入data.num" << endl;
		cin >> p->data.num;

		cout << "输入data.score"<< endl;
		cin >> p->data.score;

		p->next = L->next; // 插入到表头
		L->next = p;
	}

}

使用尾插法建立单链表(带尾指针的循环链表)

// 尾插法
void CreateList_R(LinkList &L, int n) {
	InitList_L(L);   // 建立空链表
	LinkList r = L; // 尾指针r指向头结点
	for (int i = 0; i < n; ++i) {
		LinkList p = new Lnode;
		cout << "输入data.name" << endl;
		cin >> p->data.name;

		cout << "输入data.num" << endl;
		cin >> p->data.num;

		cout << "输入data.score" << endl;
		cin >> p->data.score; // 生成新结点,输入元素值
		p->next = NULL;
		r->next = p; // 插入到表尾
		r = p; // r指向新的尾结点
	}
}

带尾指针的循环链表的合并

// 带尾指针的循环链表的合并(尾插法创建单循环链表)
LinkList Connect(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;
}

定义双向链表

// 双向链表定义
typedef struct DuLNode {
	ElemType data;
	struct  DuLNode* prior, * next;
}DuLNode,*DuLinkList;

获取双向链表中指定位置的数据元素,返回结点指针p

DuLinkList GetElemP_DuL(DuLinkList L, int i) {
	//获取指定位置的数据元素,返回结点指针p
	DuLinkList p;
	p = L->next; int j = 1;  //初始化
	while (p && j < i) { //向后扫描,直到p指向第i个元素或p为空
		p = p->next; ++j;
	}
	if (!p || j > i) return ERROR; //第i个元素不存在
	return p;

}

双向链表指定位置i插入元素

// 在带头结点的双向循环链表L中第i个位置之前插入元素e
Status ListInsert_DuL(DuLinkList& L, int i, ElemType e) {
	DuLinkList p;
	if (!(p = GetElemP_DuL(L, i)))return ERROR;
	// 获取第i个位置元素
	DuLinkList s = new DuLNode;
	s->data = e;
	s->prior = p->prior;
	p->prior->next = s;
	s->next = p;
	p->prior = s;
	return OK;
}

删除双线链表指定位置i的数据元素

// 双向链表的删除
Status ListDelete_DuL(DuLinkList &L, int i, ElemType& e) {
	// 删除带头结点的双向循环链表L的第i个元素,并用e返回
	DuLinkList p;
	if (!(p = GetElemP_DuL(L, i))) return ERROR;
	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	delete p;
	return OK;
}

合并有序链表(从小到大)

// 有序链表合并
void MergeList_L(LinkList& La, LinkList& Lb, LinkList& Lc) {
	LinkList pa = La->next; LinkList pb = Lb->next;
	LinkList pc = Lc = La; // 用La的头结点作为Lc的头结点
	while (pa && pb) {
		if (pa->data.score <= pb->data.score) {
			pc->next = pa; pc = pa; pa = pa->next;
		}
		else {
			pc->next = pb; pc = pb; pb = pb->next;
		}
	}
	pc->next = pa ? pa : pb; // 插入剩余段(三目运算符)
	delete Lb; // 释放Lb的头结点
}

测试代码及输出结果

测试代码:

此处仅测试有序链表合并算法,其余算法自行测试,下方注释代码也有部分测试代码。

此处应用头插法创建从小到大的有序链表,因此在输入数据项时应从大到小输入。

int main()
{

	LinkList LA;
	LinkList LB;
	LinkList LC;

	// 头插法创建从小到大的有序链表,需要从最大的数据元素开始从大到小输入
	cout << "创建LA并添加数据" << endl;
	CreateList_H(LA, 3); // 插入3个数据元素
	cout << "创建LB并添加数据" << endl;
	CreateList_H(LB, 2); // 插入2个数据元素
	InitList_L(LC);

	int LA_length = ListLength_L(LA);
	int LB_length = ListLength_L(LB);
	int LC_length = 0;

	cout << "合并前:" << endl;
	LinkList qa = LA;
	LinkList qb = LB;
	LinkList qc = LC;

	for (int i = 0; i < LA_length; i++) {
		qa = qa->next;
		cout << "LA: " << qa->data.name << qa->data.score << endl;
	}
	for (int i = 0; i < LB_length; i++) {
		qb = qb->next;
		cout << "LB: " << qb->data.name << qb->data.score << endl;
	}
	for (int i = 0; i < LC_length; i++) {
		qc = qc->next;
		cout << "LC: " << qc->data.name << qc->data.score << endl;
	}

	MergeList_L(LA, LB, LC);
	LC_length = ListLength_L(LC);

	cout << "合并后:" << endl;
	qc = LC;
	for (int i = 0; i < LC_length; i++) {
		qc = qc->next;
		cout << "LC: " << qc->data.name << qc->data.score << endl;
	}
	
	// 其余操作算法测试如下
	/*
	LinkList L;
	ElemType e;
	InitList_L(L);
	int is_empty = ListEmpty(L);

	cout << "L = " << L << endl;

	cout << "L是否为空: " << is_empty << endl;

	int n = 3;
	CreateList_H(L, n);

	cout << "L是否存在: " << L << endl;

	double score_i = 0;
	string name = "plj";
	//char name_i[8] = "ppp";
	int data_index = LocateElem_L_index(L, name);
	GetElem_L(L, data_index, e);
	score_i = e.score;
	string name_i = e.name;
	cout << "取出第i个score数据" << score_i << endl;
	cout << "取出第i个name数据" << name << endl;

	DestroyList_L(L);
	*/

	system("pause");

	return 0;

}
输出结果:

在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值