C语言数据结构(三),静态链表,循环链表,双向链表

《C语言数据结构》严蔚敏,吴伟民版。

上章内容链接:https://blog.csdn.net/daqino1/article/details/88703321
下章内容链接:https://blog.csdn.net/daqino1/article/details/88832307

以下内容为基础的静态链表,循环链表,双向链表。

静态链表: 数组的一个分量表示一个结点,同事用游标代替指针指示结点在数组中的相对位置。数组的第0分量可看成头结点,其指针域指示链表的第一个结点。缺点:需要预先分配一个较大的空间。优点:做线性表插入和删除操作时,不需要移动元素,仅修改指针。
定位静态链表的数据:算法2.13

//--------------------------------线性表的静态单链表存储结构---------------------------------------
#define MAXSIZE 1000 // 链表的最大长度
typedef struct {
	ElemType data;
	int cur;
}component, SLinkList[MAXSIZE];

int LocateElem_SL(SLinkList S, ElemType e) {
	// 在静态单链线性表L中查找第1个值为e的元素
	// 若找到,则返回它在L中的位序,否则返回0
	// i 指示表中的第一个结点
	i = S[0].cur;
	while (i && S[i].data != e) {
		// 在表中顺链查找
		i = S[i].cur;
	}
	
	return i;
}

初始化静态单链线性表:算法2.14

void InitSpace_SL(SLinkList &space) {
	for (i = 0; i < MAXSIZE-1; ++i) {
		space[i].cur = i + 1;
	}
	space[MAXSIZE-1].cur = 0;
}

从备用空间取一个结点: 算法2.15

int Malloc_SL(SLinkList &space) {
	// 若备用空间链表非空,则返回分配的结点下标,否则返回0
	i = space[0].cur;
	if (space[0].cur) {
		space[0].cur = space[i].cur;
	}

	return i;
}

将空闲结点连结到备用链表上:算法2.16

void Free_SL(SLinkList &space, int k) {
	// 将下标为k的空闲结点回收到备用链表
	space[k].cur = space[0].cur;
	space[0].cur = k;
}

在一维数组中建立表示集合(A-B)U(B-A):算法2.17

void difference(SLinkList &space, int &S) {
	// 依次输入集合A和B的元素,在一维数组space中建立表示集合(A-B)U(B-A)
	// 的静态链表,S为其头指针。假设备用空间足够大,space[0].cur为其头指针
	// 初始化备用空间
	InitSpace_SL(space);
	// 生成S的头结点
	S = Malloc_SL(space);
	// r指向S的当前最后结点
	r = S;
	// 输入A和B的元素个数
	scanf(m, n);
	// 建立集合A的链表
	for (j = 1; j <= m; ++j) {
		// 分配结点
		i = Malloc_SL(space);
		// 输入A的元素值
		scanf(space[i].data);
		// 插入到表尾
		space[r].cur = i;
		r = i;
	}
	
	// 尾结点的指针为空
	space[r].cur = 0;
	// 依次输入B的元素, 若不在当前表中,则插入,否则删除
	for (j = 1; j <= n; ++j) {
		scanf(b);
		p = S;
		// k指向集合A中的第一个结点
		k = space[S].cur;
		// 在当前表中查找
		while (k != space[r].cur && space[k].data != b) {
			p = k;
			k = space[k].cur;
		}
		
		// 当前表中不存在该元素,插入在r所指结点之后,且r的位置不变
		if (k == space[r].cur) {
			i = Malloc_SL(space);
			space[i].data = b;
			space[i].cur = space[r].cur;
			space[r].cur = i;
		} else {
			// 该元素已在表中,删除
			space[p].cur = space[k].cur;
			Free_SL(space, k);
			// 若删除的是尾元素,则需要修改尾指针
			if (r == k) {
				r = p;
			}
		}
	}
}

循环链表: 单链线性表中,最后一个结点的指针指向头结点,整个链表形成一个环。从表中任一结点出发,均可找到表中其他的结点。
循环条件: 判断的方法不是p或p->next是否为空。而是是否等于头指针。

双向链表:比单链表还多一个指针域,直接可以查询到当前结点的前趋,而不用顺指针查询。
双向链表的插入和删除操作:算法2.18和算法2.19

//---------------------------线性表的双向链表存储结构--------------------------------
typedef struct DuLNode {
	ElemType data;
	struct DuLNode * prior;
	struct DuLNode * next;
}DuLNode, *DuLinkList;

Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) {
	// 在带头结点的双链循环线性表L中第i个位置之前插入元素e
	// i的合法值为 1 <= i <= 表长+1
	// 在L中确定第i个元素的位置指针p
	if (!(p = GetElemP_DuL(L, i))) {
		// p = NULL, 即第i个元素不存在
		return ERROR;
	}
	
	if (!(s = (DuLinkList)malloc(sizeof(DuLNode)))) {
		return ERROR;
	}

	s->data = e;
	s->prior = p->prior;
	p->prior->next = e;
	s->next = p;
	p->prior = s;
	return OK;
}

Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e) {
	// 删除带头结点的双链循环线性表L的第i个元素,i的合法值为1<=i<=表长
	// 在L中确定第i个元素的位置指针p
	if (!(p = GetElemP_DuL(L, i))) {
		// p = NULL, 即第i个元素不存在
		return ERROR;
	}

	e = p->data;
	p->prior->next = p->next;
	p->next->prior = p->prior;
	free(p);
	return OK;
}

带头结点的线性链表定义,插入和合并操作: 算法2.20和2.21

// 结点类型
typedef struct LNode {
	ElemType data;
	struct LNode *next;
} *Link, *Position;

// 链表类型
typedef struct {
	Link head, tail;	// 分别指向线性表中的头结点和最后一个结点
	int len;			// 指示线性表中数据元素的个数
}

// 分配有p指向的值为e的结点,并返回OK;若分配失败,则返回ERROR
Status MakeNode(Link &p, ElemType e);
// 释放p所指结点
void FreeNode(Link &p);
// 构造一个空的线性链表L
Status InitList(LinkList &L);
// 销毁线性链表L,L不再存在
Status DestroyList(LinkList &L);
// 将线性链表L重置为空表,并释放原链接的结点空间
Status ClearList(LinkList &L);
// 已知h指向线性链表的头结点,将s所指结点插入在第一个结点之前
Status InsFirst(Link h, Link s);
// 已知h指向线性链表的头结点,删除链表中的第一个结点并以q返回
Status DelFirst(Link h, Link &q);
// 将指针s所指(彼此以指针相链)的一串结点链接在线性链表L的最后一个结点之后,并改变链表L的尾指针指向新的尾结点
Status Append(LinkList &L, Link s);
// 删除线性链表L中的尾结点并以q返回,改变链表L的尾指针指向新的尾结点
Status Remove(LinkList &L, Link &q);
// 已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之前,并修改指针p指向新插入的结点
Status InsBefore(LinkList &L, Link &p, Link s);
// 已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之后,并修改指针p指向新插入的结点
Status InsAfter(LinkList &L, Link &p, Link s);
// 已知p指向线性链表中的一个结点,用e更新p所指结点中数据元素的值
Status SetCurElem(Link &p, ElemType e);
// 已知p指向线性链表中的一个结点,返回p所指结点中数据元素的值
Status GetCurElem(Link p);
// 若线性链表L为空表,则返回TRUE,否则返回FALSE
Status ListEmpty(LinkList L);
// 返回线性链表L中元素个数
int ListLength(LinkList L);
// 返回线性链表L中头结点的位置
Position GetHead(LinkList L);
// 返回线性链表L中最后一个结点的位置
Position GetLast(LinkList L);
// 已知p指向线性链表L中的一个结点,返回p所指结点的直接前驱的位置,若无前驱,则返回NULL
Position PriorPos(LinkList L, Link p);
// 已知p指向线性链表L中的一个结点,返回p所指结点的直接后继的位置,若无后继,则返回NULL
Position NextPos(LinkList L, Link p);
// 返回p指示线性链表L中第i个结点的位置,并返回OK,i值不合法时返回ERROR
Status LocatePos(LinkList L, int i, Link &p);
// 返回线性链表L中第1个与e满足函数compare判定关系的元素的位置, 若不存在这样的元素,在返回NULL
Position LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType));
// 依次对L的每个元素调用函数visit(), 一旦visit()失败,则操作失败
Status ListTraverse(LinkList L, Status(*visit)());

Status ListInsert_L(LinkList &L, int i, ElemType e) {
	// 在带头结点的单链线性表L的第i个元素之前,插入元素e
	if (!LocatePos(L, i-1, h)) {
		// i值不合法
		return ERROR;
	}
	
	if (!MakeNode(s, e)) {
		// 结点存储分配失败
		return ERROR;
	}

	// 对于从第i个结点开始的链表,第i-1个结点是它的头结点
	InsFirst(h, s);

	return OK;
}

void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc, int (*compare)(ElemType, ElemType)) {
	// 已知单链线性表La和Lb的元素按值非递减排列
	// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
	if (!InitList(Lc)) {
		// 存储空间分配失败
		return ERROR;
	}
	
	// ha和hb分别指向La和Lb的头结点
	ha  = GetHead(La);
	hb = GetHead(Lb);
	// pa和pb分别指向La和Lb中当前结点
	pa = NextPos(ha);
	pb = NextPos(hb);
	
	// La和Lb均非空
	while (!Empty(La) && !Empty(Lb)) {
		// a和b为两表中当前比较元素
		a = GetCurElem(pa);
		b = GetCurElem(pb);
		if ((*compare)(a, b) ,= 0) {
			// a <= b
			DelFirst(ha, q);
			Appand(Lc, q);
			pa = NextPos(La, pa);
		} else {
			// a > b
			DelFirst(hb, q);
			Appand(Lc, q);
			pb = NextPos(Lb, pb);
		}
	}

	if (!Empty(La)) {
		// 链接La中剩余结点
		Appand(Lc, pa);
	} else {
	    // 链接Lb中剩余结点
		Appand(Lc, pb);
	}

	// 释放La和Lb的头结点
	FreeNode(ha);
	FreeNode(hb);

	return OK;
}

链表应用:求解一元多项式及相加

上章内容链接:https://blog.csdn.net/daqino1/article/details/88703321
下章内容链接:https://blog.csdn.net/daqino1/article/details/88832307

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值