之前有讲过链表的介绍以及链表抽象数据类型,现在接着完善单循环链表。
一般我们所见到的单链表就是如下图所示:
但是如果链表中的最后一个结点中的地址域并不是指向空而是指向头指针的位置,或者指向第一个元素的位置(这两种说法是一个意思)。那么这里的链表就构成了单循环链表了。
单循环链表的存储结构和之前别无二致。比较大的区别是,我们再做单循环链表的时候更习惯用尾指针(如上图所示),使用尾指针最大的优点就是,rear指针指向的尾结点中地址域就是首结点,换句话说通过尾指针利用循环链表的特殊性,我可以方便的找到最后一个元素和第一个元素。
typedef int DataType
typedef struct Node
{
DataType data;
strcut Node *next
}Node;
这里还是先介绍单循环链表的插入、获取结点个数、获取指定位置元素这三种抽象数据类型。
typedef int DataType
typedef struct Node
{
DataType data;
strcut Node *next
}Node;
int getSize(Node *rear)//获取链表中元素的个数
{
int size=0;
if(rear)
{
Node *p=rear->next;//p指向首地址
while(p!=rear)//从首地址遍历到尾地址
{
size++;
p=p->next;
}
size++;
}
return size;
}
Node* getptr(Node *rear,int pos)//获取指定下标位置元素
{
if(rear==NULL){
printf("链表为空\n");
return rear;
}
if(pos<0||pos>=getSize(rear)){
printf("所查找的位置在链表外\n")
return NULL;
}
Node *p=rear->next;
for(int i=0;i<pos;i++)
{
p=p->next;
}
return p;
}
bool insert(Node **rear,int position ,DataType d)//插入一个元素
{
if(position<0||position>getsize(*rear))
{
printf("插入位置超出链表的范围\n");
return false;
}
Node *node=(Node *)malloc(sizeof(Node));
node->data=d;//用来开辟一块链表结点的空间,存放数据域和指针域的元素
node->next=NULL;
if(position==0)
{
if(*rear==NULL)//在空表中插入
{
node->next=node;
*rear=node;
}
else//在非空表第一个位置插入
{
node->next=(*rear)->next;
(*rear)->next=node;
}
return true;
}
Node *p=getptr(*rear,position-1);
Node *r=p->next;
node->next=r;
p->next=node;
if(*rear==p)
{
*rear=node;
}
return true;
}
int main()
{
Node *rear=NULL;
insert(*rear,0,1);
insert(*rear,0,5);
insert(*rear,0,9);
return 0;
}
这里就重点讲述一下插入的程序,因为获取结点个数、获取指定位置元素的程序比较简单。
插入的程序中
if语句里面就是一个类似于容错保护的东西,不重要直接从malloc开始,首先在插入一个结点,这时候必须开辟一个空间来放置一个结点,需要用到malloc(C++中是new)。然后有几点需要注意,在空表中插入,在非空表的第一位置插入,在中间插入,以及在末尾插入这四种插入形式。
在空表插入:
如下图所示,这时候需要注意,本身没有链表,现在有了唯一个结点,那么就是自己指向自己,node->next=node ;同时自身也是尾指针指向的位置,故也有*rear=node(在插入函数中使用的是二级指针,因为存在修改结点指针的可能)。这里就是在空表中插入的形式。
在非空表的第一位置插入:
如下图所示,本身链表中有元素,现在在首位置插入第一个元素,node的next指针就需要指向原来的第一个元素也就是尾指针指向的下一个元素【node->next=(*rear)->next;】。同时尾指针指向的下一个元素也就是之前的第一个元素需要更换成node里面的data【(*rear)->next=node;】
这些都比较好理解,比较难懂是在中间插入:
Node *p=getptr(*rear,position-1);
Node *r=p->next;
node->next=r;
p->next=node;
以上图为例,假如现在是insert(&rear,1,6);
所以程序里面主要做的是得到插入位置前的位置指针,然后修改到插入的node ,并将node的next指针指向原来的位置1元素。
这里比较需要注意的是如果插入的尾元素,或者说这个时候尾指针还指向之前的尾元素,但是插入的是尾部(在末尾插入)
if(*rear==p)
{
*rear=node;//修改末尾指针的位置
}
单链表删除元素:
bool erase(Node **rear,int pos)
{
if(*rear==NULL||pos<0||pos>=getSize(*rear))
{
return false;
}
if(pos==0)
{
(*rear)->next=p->next;
free(p);
p=NULL;
return true;
}
p=getptr(*rear,pos-1);
Node *q=p->next;
p->next=q->next;
if(q==*rear)
{
*rear=p;
}
free(q);
q=NULL;
return true;
}
int main()
{
Node *rear=NULL;
insert(*rear,0,1);
insert(*rear,0,5);
insert(*rear,0,9);
erase(&rear,0);
return 0;
}
删除和插入类似这里就不细细说明了。