目录
参考、格式
数据结构课本2.3节(严蔚敏版)
头文件LinkList.h
由于本部分函数过多,这里只介绍自己在实现过程中出现的问题。
一、将函数的小括号写成中括号
二、读取权限冲突
C++读取访问权限冲突引发异常问题_引发了异常: 读取访问权限冲突。-CSDN博客
没有记录错误代码,不过编码过程中多次出现过这个问题,原因是指针未初始化(特别是函数内部)
三、L->Last指针没有移动
//算法2.20 插入元素 改编自算法2.9
Status List_Insert_L(LinkList& L, int i, ElemType e) {
//在带头结点的单链线性表L的第i个元素之前插入元素e,1<=i<=L->len
//更准确的说法是在第i个结点之后(包括头结点)插入结点。
Link s = NULL, h = NULL;
if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法
if (!MakeNode(s, e))return ERROR;//结点存储分配失败
InsFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点
//基于课本中的这句注释,我觉得要实现的线性链表头结点和首元结点不一致
if (i > L->len)L->tail = s;//若增加的结点位于尾部,应修改尾指针
L->len++;//InsFirst函数没有让记录元素个数的变量增加
return OK;
}
四、函数指针的使用
头文件完整代码
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;//Status是函数的类型,其值是函数结果状态代码
typedef int ElemType;
//-----线性链表的存储结构---------
//-----实现单向非循环动态链表-----
//-----头结点与首元结点不一致-----
typedef struct LNode {//结点类型
ElemType data;
struct LNode* next;
}LNode,*Link,*Position;
typedef struct LinkNode{ //链表类型
Link head, tail; //分别指向线性链表中的头结点和最后一个结点
int len; //指示线性链表中数据元素的个数
}LinkNode,*LinkList;
Status MakeNode( Link & p,ElemType e ){
//分配由p指向的值为e的结点,并返回OK;若分配失败,则返回ERROR
p = (Link)malloc(sizeof(LNode));
if (!p)
return ERROR;
p->data = e;
return OK;
}
void FreeNode(Link& p) {//相当于free函数重命名
//释放p所指结点
free(p);
p = NULL;
}
Status InitList(LinkList& L) {
//构造一个空的线性链表L
L = (LinkList)malloc(sizeof(LinkNode));
if (!L)return ERROR;
L->head = L->tail = (Link)malloc(sizeof(LNode));
if (!L->head)return ERROR;
L->head->data = 0;
L->head->next = NULL;
L->len = 0;
return OK;
}
Status DestroyList(LinkList& L) {
//销毁线性链表L,L不再存在
Link p;
while (L->head) {//删除所有结点
p = L->head;
L->head = L->head->next;
free(p);
}
free(L);
L = NULL;
return OK;
}
Status ClearList(LinkList& L) {
//将线性链表L重置为空表,并释放原链表的结点空间
Link p = L->head;
while (L->head->next) {//保留一个结点,让其作为头结点
p = L->head->next;
L->head->next = p->next;
free(p);
}
L->tail = L->head;
L->len = 0;
return OK;
}
Status InsFirst(Link h, Link s) {
//已知h指向线性链表的头结点,将s所指结点插入到首元结点之前
s->next = h->next;
h->next = s;
//这个函数没修改L->len的值,课本算法2.20需要补充L->len++。
return OK;
}
Status DelFirst(Link h, Link& q) {
//已知h指向线性链表的头结点,删除链表中的首元结点并以q返回
q = h->next;
h->next = q->next;
q->next = NULL;
return OK;
}
Status Append(LinkList& L, Link s) {
//将指针s所指(彼此以指针相链)的一串结点链接在线性链表L的最后一个结点(用L->tail寻找)
//之后,并改变链表L的尾指针指向新的尾结点
while (s) {
L->tail->next = s;
L->tail = s;
L->len++;
s = s->next;
}
return OK;
}
Status Remove(LinkList& L, Link& q) {
//删除线性链表L中的尾节点并以q返回,改变链表L的尾指针指向新的尾结点
if (!L->tail)return ERROR;
Link p = L->head;
while (p->next != L->tail)
p = p->next;//寻找尾结点的上一个结点
q = L->tail;
L->tail = p;//改变尾结点
L->tail->next = NULL;
L->len--;
return OK;
}
Status InsBefore(LinkList& L, Link& p, Link s) {
//已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之前
//并修改指针p指向新插入的结点
Link q = L->head;
while (q->next != p)
q = q->next;//寻找结点q的上一个结点
q->next = s;
s->next = p;//插入s
p = s;//修改指针p
L->len++;
return OK;
}
Status InsAfter(LinkList& L, Link& p, Link s) {
//已知p指向线性链表L中的一个结点,将s所指的结点插入在p所指结点之后
//并修改指针p指向新插入的结点
s->next = p->next;
p->next = s;
p = s;
L->len++;
return OK;
}
Status SetCurElem(Link& p, ElemType e) {
//已知p指向线性链表L中的一个结点,用e更新p所指结点中数据元素的值
p->data = e;
return OK;
}
ElemType GetCurElem(Link p) {
//已知p指向线性链表L中的一个结点,返回p所指结点中数据元素的值
return p->data;
}
Status ListEmpty(LinkList L) {
//若线性链表L为空表,则返回TRUE,否则返回FALSE
if (!L->len)
return TRUE;
else
return FALSE;
}
int ListLength(LinkList L) {
//返回线性链表L中元素个数
return L->len;
}
Position GetHead(LinkList L) {
//返回线性链表L中头结点的位置
return L->head;
}
Position GetLast(LinkList L) {
//返回线性链表L中尾结点的位置
return L->tail;
}
Position PriorPos(LinkList L, Link p) {
//已知p指向线性链表L中的一个结点,返回p所指结点中的直接前驱的位置
//若无前驱,则返回NULL
Link q = L->head;
if (p == L->head)return NULL;
while (q->next != p)
q = q->next;
return q;
}
Position NextPos(LinkList L, Link p) {
//已知p指向线性链表L中的一个结点,返回p所指结点中的直接后继的位置
//若无后继,则返回NULL
return p->next;
}
Status LocatePos(LinkList L, int i, Link& p) {
//返回p指示线性链表L中第i个结点的位置并返回OK,i值不合法时返回ERROR
//头结点当作第0结点,首元结点第1个,尾结点第(L->len)个
if (i<0 || i>L->len)return ERROR;
int j = 0;Link q = L->head;
while (j < i) {
q = q->next;
++j;//j++也一样
}
p = q;
return OK;
}
//compare与visit都是函数指针,请看相关博客
Position LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) {
//返回线性链表L中第1个与e满足函数compare()判定关系的元素的位置,
//若不存在这样的元素,则返回NULL
int i = 1;Link p = L->head->next;
while (i <= L->len && !(*compare)(p->data, e)) {
p = p->next;
i++;
}
return p;
}
//遍历链表
Status ListTraverse(LinkList L, void (*visit)(Link)) {
//依次对L的每个元素调用函数visit()。一旦visit()失败,则操作失败。
Link p = L->head;
for (int i = 1;i <= L->len;i++) {
p = p->next;
visit(p);
}
printf("\n");
return OK;
}
//上面的为线性链表的基本算法
//算法2.20 插入元素 改编自算法2.9
Status List_Insert_L(LinkList& L, int i, ElemType e) {
//在带头结点的单链线性表L的第i个元素之前插入元素e,1<=i<=L->len
//更准确的说法是在第i个结点之后(包括头结点)插入结点。
Link s = NULL, h = NULL;
if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法
if (!MakeNode(s, e))return ERROR;//结点存储分配失败
InsFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点
//基于课本中的这句注释,我觉得要实现的线性链表头结点和首元结点不一致
if (i > L->len)L->tail = s;//若增加的结点位于尾部,应修改尾指针
L->len++;//InsFirst函数没有让记录元素个数的变量增加
return OK;
}
//删除元素
Status List_Delete_L(LinkList& L, int i, ElemType& e) {
Link s = NULL, h = NULL;
if (!LocatePos(L, i - 1, h))return ERROR;//i值不合法
if (i == L->len)L->tail = h;//若删除的结点位于尾部,应修改尾指针
DelFirst(h, s);//对于从第i个结点开始的链表,第i-1个结点是它的头结点
e = s->data;
FreeNode(s);
L->len--;
return OK;
}
//算法2.21 合并线性表 改编自算法2.12
Status MergeList_L(LinkList& La, LinkList& Lb, LinkList& Lc, int (*compare)(ElemType, ElemType)) {
//已知单链线性表La和Lb的元素按值非递减排列。
//归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列。
if (!InitList(Lc))return ERROR;//存储空间分配失败
Link ha = GetHead(La);Link hb = GetHead(Lb);//ha和hb分别指向La和Lb的头结点
Link pa = NextPos(La, ha);Link pb = NextPos(Lb, hb);//pa和pb分别指向La和Lb中当前结点
while (pa && pb) {//La和Lb均非空
int a = GetCurElem(pa);int b = GetCurElem(pb);//a和b为两表中当前比较元素
Link q = NULL;
if ((*compare)(a, b) <= 0) {//a<=b
DelFirst(ha, q);Append(Lc, q);pa = NextPos(La, ha);
}
else{//a>b
DelFirst(hb, q);Append(Lc, q);pb = NextPos(Lb, hb);
}
}
if (pa)Append(Lc, pa);//链接La中剩余结点
else Append(Lc, pb);//链接Lb中剩余结点
FreeNode(ha);FreeNode(hb);//释放La和Lb的头结点
return OK;
}
测试函数(主函数)test.cpp
InBefore、InAfter、PriorPos这三个函数没有测试。
#include "LinkList.h"
Status comp(ElemType c1, ElemType c2) /* 数据元素判定函数(平方关系) */
{
if (c1 == c2 * c2)
return TRUE;
else
return FALSE;
}
int compare(ElemType a, ElemType b) /*数据元素判定函数(大小关系)*/
{
return a - b;
}
void visit(Link p) /* ListTraverse()调用的函数(类型要一致) */
{
printf("%d ", p->data);
}
void dbl(Link p) /* ListTraverse()调用的另一函数(元素值加倍) */
{
p->data *= 2;
}
int main()
{
LinkList L, L1, L2;
Status i;
ElemType e0;
int j;
Link p, q;
i = InitList(L);
printf("初始化L后:L->len=%u\n", L->len);
for (j = 1;j <= 5;j++) {
i = List_Insert_L(L, 1, j);//有没有i=不影响结果,只有List_Insert(L,1,j);也行。
}
printf("在表头插入元素1到5,表L中元素为");
i = ListTraverse(L, visit);
i = ClearList(L);
i = ListEmpty(L);
if (i)
printf("表L为空\n");
for (j = 10;j > 5;j--) {
i = List_Insert_L(L, 1, j);
}
printf("表L中元素为");
i = ListTraverse(L, visit);
printf("表L中元素个数为%d", ListLength(L));
i = ListTraverse(L, dbl);
printf("表L中元素为");
i = ListTraverse(L, visit);
i = List_Delete_L(L, 3, e0);
printf("被删除的元素为%d\n", e0);
printf("表L中元素为");
i = ListTraverse(L, visit);
for (j = 10;j > 5;j--) {
i = List_Insert_L(L, 1, j);
}
printf("表L中元素为");
i = ListTraverse(L, visit);
printf("表L中元素个数为%d\n", ListLength(L));
i = Remove(L, q);
printf("被删除的元素为%d\n", q->data);
i = SetCurElem(L->tail, 30);
printf("表L中元素为");
i = ListTraverse(L, visit);
for (j = 3;j <= 4;j++) {
p = LocateElem(L, j, comp);
if (p)
printf("表L中第1个为%d的平方的元素是%d\n", j, p->data);
else
printf("表L中没有%d的平方的元素\n", j);
}
i = InitList(L1);
for (j = 13;j >= 3;j -= 2) {
i = List_Insert_L(L1, 1, j);
}
printf("表L1中元素为");
i = ListTraverse(L1, visit);
i = MergeList_L(L, L1, L2, compare);
printf("表L2中元素为");
i = ListTraverse(L2, visit);//此时L与L1头结点均被删除
i = DestroyList(L2);
return 0;
}