目录
1.1 循环链表
循环链表是链式存储结构的另一种形式。
特点:表中的最后一个结点的指针域指向头结点,整个链表形成一个环。因此,从表中的任意一个结点出发均可以找到表中的其他结点。与普通的线性链表的操作基本一致,差别在于循环条件不是p或p->next 是否为空,而是它们是否等于头指针。
1.2 双向链表
由于以上讨论的线性链表结构都是只有一个直接指向后继结点的指针域,所以遍历的方式都是只能从某一个结点向后遍历,为了克服这一个缺点,可以使用双向链表
特点:双向链表的结点有两个指针域,一个指向直接后继结点,一个指向直接前驱结点
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode,*DuLinkList;
双链表的建立
头插法
void CreatListF(DLinkNode *&L,ElemType a[], int n){
DLinkNode *s;
L=(DLinkNode *)malloc(sizeof(DLinkNode));
L->prior=L->next=NULL;
for(int i=0;i<n;i++){
s=(DLinkNode *)malloc(sizeof(DLinkNode));
s->data=a[i];
s->next=L->next;
if(L->next!=NULL){
L->next->prior=s;
}
L->next=s;
s->prior=L;
}
}
函数接受三个参数:指向双向链表的指针L
(通过引用传递),一个存储元素的数组a[]
,以及元素的数量n
。
在函数内部,首先通过malloc
函数为链表头节点L
分配内存空间,并将其前驱指针prior
和后继指针next
都设置为NULL
,表示链表为空。
然后,通过循环遍历数组a[]
,依次创建链表节点并将元素赋值给节点的data
成员。对于每个新创建的节点s
,将它的next
指针指向原链表头节点的下一个节点,并更新原链表头节点的后继指针指向s
。如果原链表头节点的下一个节点不为空,则将其前驱指针指向s
。最后,将s
的前驱指针指向原链表头节点,完成节点的插入操作。
尾插法
void CreateListR(DLinkNode*& L, ElemType a[], int n) {
DLinkNode* s;
L = (DLinkNode*)malloc(sizeof(DLinkNode));
L->prior = L->next = NULL;
DLinkNode* tail = L; // 尾指针,初始指向头节点
for (int i = 0; i < n; i++) {
s = (DLinkNode*)malloc(sizeof(DLinkNode));
s->data = a[i];
s->next = NULL;
s->prior = tail;
tail->next = s;
tail = s; // 更新尾指针为当前节点
}
}
它也接受三个参数:指向双向链表的指针L
(通过引用传递),存储元素的数组a[]
以及元素的数量n
。
在函数内部,首先通过malloc
函数为链表头节点L
分配内存空间,并将其前驱指针prior
和后继指针next
都设置为NULL
,表示链表为空。然后,声明一个尾指针tail
,初始时指向头节点。
接下来,通过循环遍历数组a[]
,依次创建链表节点并将元素赋值给节点的data
成员。对于每个新创建的节点s
,将其next
指针设置为NULL
,将其prior
指针指向当前尾节点tail
,然后将当前尾节点的next
指针指向s
,实现节点的插入。最后,更新尾指针tail
为当前节点s
,以便下一次循环时将新节点插入到链表尾部。
总结
双向链表(Doubly Linked List):
- 双向链表是一种链表数据结构,每个节点除了包含指向下一个节点的指针(
next
指针)外,还包含指向前一个节点的指针(prev
指针)。 - 双向链表可以双向遍历,可以从头节点向后遍历,也可以从尾节点向前遍历,这使得双向链表的某些操作更高效,例如在任意位置插入和删除节点。
- 双向链表占用更多的内存空间,因为每个节点需要存储额外的指针。
循环链表(Circular Linked List):
- 循环链表是一种链表数据结构,在单向链表或双向链表的基础上,将尾节点的
next
指针指向头节点,形成一个闭环。 - 循环链表可以通过遍历到尾节点后,再次返回到头节点,从而实现循环遍历,可以方便地处理循环相关的问题。
- 循环链表适用于需要循环访问数据的场景,比如实现循环队列和循环缓冲区等。