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