目录
一、循环链表
1、定义和特点
注:以下算法中循环链表的节点声明同单链表
2、初始化循环链表
创建一个空的循环单链表,它只有头结点,由L指向它。该结点的next域指向该头结点
Status InitList(LinkList &L)
{
L = (LinkList)malloc(sizeof(LNode));
if(!L)
exit(OVERFLOW);
L->next = L;
return Ok;
}
3、求循环链表的长度
p初始指向头结点,当p再次指向头结点时,对循环链表的一轮遍历结束
int ListLength(LinkList L)
{
int i = 0;
LinkList p = L->next; //p指向第一个结点
while(p != L){ //如果没有到表尾
i++;
p = p->next;
}
return i;
}
4、遍历循环链表找到值为x的结点个数(重点在于遍历操作)
int CountNode(LNode *L, int x)
{
int i = 0;
LNode *p = L->next;
while(p != L){
if(p->data == x) i++;
p = p->next;
}
return i;
}
5、循环链表的典型应用——约瑟夫环
题解代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
int no; //小孩的编号
struct node* next; //指向下一个结点指针
}Child;
void CreateList(Child*& L, int n);
void Joseph(int n, int m);
void main()
{
int n = 6, m = 5;
printf("n=%d, m=%d:\n", n, m);
Joseph(n, m);
printf("\n");
}
//用尾插法创建不带头结点的小孩圈循环单链表
void CreateList(Child*& L, int n)
{
int i;
Child* p, * tc; //tc指向新建循环单链表的尾结点
L = (Child*)malloc(sizeof(Child));
L->no = 1;
tc = L;
for (i = 2; i <= n; i++)
{
p = (Child*)malloc(sizeof(Child));
p->no = i;
tc->next = p;
tc = p;
}
tc->next = L;
}
//求解约瑟夫序列
void Joseph(int n, int m)
{
int i, j;
Child* L, * p, * q; //注意这里L一直指向每一轮第一个报数的结点
CreateList(L, n);
for (i = 1; i <= n; i++) //需要出列n个小孩
{
p = L; //从L结点开始报数
j = 1;
while (j < m - 1) //报数报到L结点后的第m-1个结点
{
j++; //报数递增
p = p->next; //移到下一个结点
}
q = p->next; //报数到了第m个结点,用q来指向它
printf("%d ", q->no); //将该节点出列,即输出
p->next = q->next; //在循环链表中删除该节点
free(q); //释放其空间
L = p->next; //下一个开始报数的结点
}
}
运行结果
二、双向链表
1、定义和特点
2、双向链表的类型声明
与单链表一样,双向链表也分为非循环的双向链表和双向循环链表,以下均是针对带头结点的双向循环链表
typedef struct DuLNode{
ElemType data;
DuLNode *prior, *next;
}DuLNode,*DuLinkList;
3、求双向循环链表的长度
其设计思路与单循环链表的算法完全相同
int ListLength(DuLinkList L)
{
int i = 0;
DuLinkList p = L->next; //p指向第一个结点
while(p != L){ //p还没到表头时,继续循环
i++;
p = p->next;
}
return i;
}
4、双向循环链表插入算法
先在双向循环链表中查找第i-1个结点,若成功找到该节点(由p所指向),创建一个以e为值的新结点s,将s结点插入到p之后即可
Status ListInsert(DuLinkList L, int i, ElemType e)
{
DuLinkList p, s;
if(i < 1 || i > ListLength(L) + 1) //i的值不合法的情况下
return ERROR;
p = GetElemP(L, i - 1); //用p来保存第i的元素的前驱元素
if(!p) return ERROR;
s = (DuLinkList)malloc(sizeof(DuLNode));
if(!s)
exit(OVERFLOW);
//将s插入到位置i
s->data = p;
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
return OK;
}
5、双向循环链表删除结点算法
先在双向循环链表中找到第i个结点,若成功找到该结点(有p所指向),通过其前驱结点和后继结点的指针域的改变来删除p结点
Status ListDelete(DuLinkList L, int i, ElemType &e)
{
DuLinkList p;
if(i < i)
return ERROR;
p = GetElemP(L, i); //用p来保存第i位置的元素
if(!p)
return ERROR;
e = p->data;
//将p结点从L中删除
p->prior->next = p->next;
p->next->prior = p->prior;
//释放其空间
free(p);
return OK;
}