循环链表与双链表

循环链表与双链表:

循环链表:
循环链表是一种特殊的链式存储结构。它的特点是表中最后一个节点的指针域指向头节点,使得整个链表形成一个环,这种结构的优点是从任何一个节点出发都可以访问到其他所有节点,增加了链表的灵活性。
一个循环链表的实例:
#include <stdio.h>
#include <stdlib.h>
struct Node {
    int data;
    struct Node* next;
};
int main() {
    int n, i, data;
    struct Node* head, * new_node, * temp;
    printf("Enter the number of nodes: ");
    scanf("%d", &n);
    // 创建第一个节点
    head = (struct Node*)malloc(sizeof(struct Node));
    if (!head) {
        printf("Memory allocation failed.");
        return 0;
    }
    printf("Enter the data for node 1: ");
    scanf("%d", &data);
    head->data = data;
    head->next = NULL;
    temp = head;
    // 创建剩余节点
    for (i = 2; i <= n; ++i) {
        new_node = (struct Node*)malloc(sizeof(struct Node));
        if (!new_node) {
            printf("Memory allocation failed.");
            return 0;
        }
        printf("Enter the data for node %d: ", i);
        scanf("%d", &data);
        new_node->data = data;
        new_node->next = NULL;
        temp->next = new_node;
        temp = new_node;
    }
    //循环连接
    new_node->next = head;
    // 输出链表中的数据元素
    printf("\nCreated Linked List is: ");
    temp = head;
    struct node* k = head;
    int l = 1;
    while (temp != k||l==1) {
        printf("%d ", temp->data);
        l = 0;
        temp = temp->next;
    }
    printf("\n");
    // 释放内存
    temp = head;
    l = 1;
    while (temp != k||l==1) {
        head = temp->next;
        free(temp);
        l = 0;
        temp = head;
    }
    return 0;
}

从上可以看出,与一般的单链表相比,多了 new_node->next = head;从而可以循环连接。

除此之外,在输出链表元素时也与一般的单链表不同,需要特殊的手段避免死循环:

 struct node* k = head;//记录头结点的位置,再次循环到头结点时,停止打印
    int l = 1;
    while (temp != k||l==1) {
        printf("%d ", temp->data);
        l = 0;
        temp = temp->next;
    }
    printf("\n");
    // 释放内存
    temp = head;
    l = 1;
    while (temp != k||l==1) {
        head = temp->next;
        free(temp);
        l = 0;
        temp = head;
    }
同样,我们也可以通过一些操作,让两个有头结点的循环链表相连接(假设它们的尾指针分别为reara,rearb):
p=reara->next;
reara->next=rearb->next->next;
q=rearb->next;
rearb->next=p;
free(q);
要注意的是,第一个链表的尾指针连接的不是第二个链表的头结点,而是之后的节点。

下面再来看看双链表:

什么是双链表:

制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CShelly6%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20231031195134020.png&pos_id=img-PwPL9kca-1702262514092)

如图所示:
双链表,也被称为双向链表,是链表的一种。在双链表中,每个数据节点都有两个指针,分别指向直接后继和直接前驱,与单链表相比,双链表的主要区别在于其结构的构造有所不同,在单链表中,每个节点都有一个储存数据的部分和一个指向后继节点的指针。这意味着单链表的遍历操作必须从前节点到后节点进行然而,在双链表中,每个节点除了有储存数据的部分和一个指向后继节点的指针(next)外,还有一个指向前驱节点的指针(pre)这样,双链表就可以实现从后节点到前节点的遍历操作。这种结构使得双链表在进行某些操作时,如插入和删除节点,比单链表更加高效。因为在双链表中,可以直接找到任何节点的前驱节点,而在单链表中,则需要从头开始遍历才能找到前驱节点。当然,在换取方便的同时,这也使得双链表在内存使用上比单链表更加消耗资源,因为它需要额外的空间来存储pre指针。

双链表的使用:

双链表的大多操作都与单链表类似,只不过多了一个前驱指针。
以下是一个双链表节点的C语言定义示例:
struct dnode{
	int data;
	struct next*;
	struct pre*;
};
当然,我们也可以创建一个头结点:
struct dnode*head=(struct dnode*)malloc(sizeof(struct dnode));
head->next=NULL;
head->pre=NULL;
也可以增加一个节点,这与单链表相比,有一定的变化:
struct dnode*new=(struct dnode*)malloc(sizeof(struct dnode));
//假如在当前节点p后面添加。
new->next=p->next->next;//1
new->pre=p;//2
p->next->pre=new;//3
p->next=new;//4
注意3,4步都使用到了p->next;如果先进行了第四步,那么第三步就会出错。所以,顺序很重要,
不过我们可以将三四步这样写,就避免了顺序错误的问题:
new->next->pre=new;
new->pre->next=new;
也可删除一个节点,比如删除p节点:
p->next->pre=p->pre;
p->pre->next=p->next;
free(p);
用完之后,别忘了释放空间,可以用双链表的整表删除:
void free_dnode(struct dnode* head) {
	struct dnode* p = head;
	while (p->next) {
		struct dnode* k = p->next;
		free(p);
		p = k;
	}
	free(p);
	
}
完整的代码如下;
#include <stdio.h>
#include <stdlib.h>
struct dnode {
	int data;
	struct dnode* pre;
	struct dnode* next;
};
struct dnode* init_head(void) {
	struct dnode* head = (struct dnode*)malloc(sizeof(struct dnode));
	head->next = NULL;
	head->pre = NULL;
	return head;
}
void add_dnode(struct dnode* head, int a, int b) {
	struct dnode* new = (struct dnode*)malloc(sizeof(struct dnode));
	new->data = b;
	struct dnode* p = head;
	while (a > 0 && p->next != NULL) {
		p = p->next;
		a--;
	}
	new->pre = p;
	new->next = p->next;
	if (p->next != NULL) {
		p->next->pre = new;
	}
	p->next = new;
}
void print(struct dnode* head) {
	struct dnode* p = head->next; 
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void del_dnode(struct dnode* head, int place) {
	struct dnode* p = head;
	while (place > 0 && p->next != NULL) {
		p = p->next;
		place--;
	}
	if (p->next != NULL) { 
		struct dnode* to_delete = p->next;
		p->next = to_delete->next;
		if (to_delete->next != NULL) {
			to_delete->next->pre = p;
		}
		free(to_delete);
	}
}
void free_dnode(struct dnode* head) {
	struct dnode* p = head;
	while (p->next) {
		struct dnode* k = p->next;
		free(p);
		p = k;
	}
	free(p);	
}
int main() {
	struct dnode* head = init_head(); 
	int k;
	scanf("%d", &k);
	for (int i = 0; i < k; i++) {
		int a, b;
		scanf("%d%d", &a, &b); 
		add_dnode(head, a, b);
	}
	int place;
	scanf("%d", &place);
	del_dnode(head, place);
	print(head);
    free_dnode(head);
	head = NULL;//避免出现悬挂指针
	return 0;
}
我们也可以像单链表一样,用数组来模拟一个双链表:
先来看看一个实例:
#include <stdio.h>
#include <string.h>
int l[10000];
int r[10000];
int data[10000];
int idx = 2;
void init() {
    r[0] = 1; // 左头节点
    l[1] = 0; // 右尾结点
}
void add_l(int x) { // 头插
    data[idx] = x;
    l[idx] = 0;
    r[idx] = r[0];
    l[r[0]] = idx;
    r[0] = idx;
    idx++;
}
void add_r(int x) { // 尾插
    data[idx] = x;
    r[idx] = 1;
    l[idx] = l[1];
    r[l[1]] = idx;
    l[1] = idx;
    idx++;
}
void insert_r(int pos, int x) { // 右插
pos++;
data[idx] = x;
l[idx] = pos;
r[idx] = r[pos];
l[r[pos]] = idx;
r[pos] = idx;
idx++;
}
void insert_l(int pos, int x) { // 左插
    pos++;
    pos = l[pos];
    data[idx] = x;
    l[idx] = pos;
    r[idx] = r[pos];
    l[r[pos]] = idx;
    r[pos] = idx;
    idx++;
}
void del(int k) { // 删除
    k++;
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}
int main() {
    int m;
    scanf("%d", &m);
    getchar();
    init();
    while (m--) {
        char str[1000];
        gets(str);
    if (str[0] == 'L') {
        int x;
        sscanf(str, "%*s %d", &x);
        add_l(x);
    }
    else if (str[0] == 'R') {
        int x;
        sscanf(str, "%*s %d", &x);
        add_r(x);
    }
    else if (str[0] == 'D') {
        int x;
        sscanf(str, "%*s %d", &x);
        del(x);
    }
    else if (str[0] == 'I') {
        int x, pos;
        sscanf(str, "%*s %d %d", &pos, &x);
        if (str[1] == 'L') {
            insert_l(pos, x);
        }
        else if (str[1] == 'R') {
            insert_r(pos, x);
        }
    }
}
for (int i = r[0]; i != 1; i = r[i]) {
    printf("%d ", data[i]);
}
return 0;
}
数组模拟的的双链表不但有双链表的所有功能,还避免了频繁分配空间,在处理大数据时,更加快速,而相比与数组,又具备了链表可以插入元素的功能。
当然,我们也可以把它变成双向循环链表,改动如下:
void make_circular(struct dnode* head) {
    struct dnode* p = head;
    while (p->next != NULL) {
        p = p->next;
    }
    p->next = head;
    head->pre = p;
}
输出也会相应变化,以下是完整版本:
#include <stdio.h>
#include <stdlib.h>
struct dnode {
	int data;
	struct dnode* pre;
	struct dnode* next;
};
struct dnode* init_head(void) {
	struct dnode* head = (struct dnode*)malloc(sizeof(struct dnode));
	head->next = NULL;
	head->pre = NULL;
	return head;
}
void add_dnode(struct dnode* head, int a, int b) {
	struct dnode* new = (struct dnode*)malloc(sizeof(struct dnode));
	new->data = b;
	struct dnode* p = head;
	while (a > 0 && p->next != NULL) {
		p = p->next;
		a--;
	}
	new->pre = p;
	new->next = p->next;
	if (p->next != NULL) {
		p->next->pre = new;
	}
	p->next = new;
}
void print(struct dnode* head) {
	struct dnode* p = head->next; 
	while (p != NULL && p != head) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void del_dnode(struct dnode* head, int place) {
	struct dnode* p = head;
	while (place > 0 && p->next != NULL) {
		p = p->next;
		place--;
	}
	if (p->next != NULL) { 
		struct dnode* to_delete = p->next;
		p->next = to_delete->next;
		if (to_delete->next != NULL) {
			to_delete->next->pre = p;
		}
		free(to_delete);
	}
}
void free_dnode(struct dnode* head) {
	struct dnode* p = head;
	while (p->next && p->next != head) {
		struct dnode* k = p->next;
		free(p);
		p = k;
	}
	free(p);	
}
void make_circular(struct dnode* head) {
    struct dnode* p = head;
    while (p->next != NULL) {
        p = p->next;
    }
    p->next = head;
    head->pre = p;
}
int main() {
	struct dnode* head = init_head(); 
	int k;
	scanf("%d", &k);
	for (int i = 0; i < k; i++) {
		int a, b;
		scanf("%d%d", &a, &b); 
		add_dnode(head, a, b);
	}
	make_circular(head);
	int place;
	scanf("%d", &place);
	del_dnode(head, place);
	print(head);
    free_dnode(head);
	head = NULL;//避免出现悬挂指针
	return 0;
}

以上就是本次的分享,下周见!

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao Ling.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值