【数据结构】从0到1的线性表

目录

1.单链表

首先,定义一个结构体

写出链表初始化函数 

写出头插法函数

写出尾插法函数 

写出删除节点函数

写出输出链表data的函数

2.单循环链表

链表初始化函数

尾插法函数

删除节点函数

链表输出函数

3.双链表

定义结构体 

其他函数 

4.双循环链表


线性表基本分为两种:数组和链表

两者的区别在于:数组是顺序存储(存储空间连续),链表是链式存储(存储空间不连续

由于数组比较简单,本文只介绍链表的基本操作

1.单链表

  • 首先,定义一个结构体

结构体可作为链表的每一个节点,即链表的每一个节点都是一个struct Node类型的变量 

在这里,data存储每个节点的值,next为每个节点指向后继节点的指针 

typedef struct Node
{
	int data;
	struct Node* next;
} Node;

在这里,为什么要用typedef呢?这其实是c语言非常经典的写法。

如果我们使用typedef重定义,当我们需要创建一个Node类型变量时,我们需要这么写:

struct Node a;

而当我们重定义之后,创建变量只需要这么写:

Node a;

   

  • 写出链表初始化函数 

一般用这个函数初始化链表的头结点 

我们使用malloc函数来分配内存空间,这样可以用多少分配多少,节省内存;函数中的if-else语句是为了判断是否正确分配内存空间,有的IDE中不加此语句会报错

由于malloc函数返回指向被分配内存的指针,所以是Node*类型

如果没有下一个节点,那么此节点的next指针指向NULL(即为空),如果有下一个节点,此节点的next指针指向下一个节点

小tip:可以在上面结构体重定义时加上一个*pnode,这样Node* list就可以写为pnode list啦

Node* initlist()
{
	Node* list = (Node*)malloc(sizeof(Node));
	if (list == NULL)
	{
		printf("error");
	}
	else
	{
		list->data = 0;
		list->next = NULL;
	}
	return list;
}

  •  写出头插法函数

头插法指将节点插入头结点的后面,所以如果用头插法依次插入1、2、3,实际在链表中的顺序是:头结点、3、2、1

小tip:注意每次修改指针时的顺序,不要把节点搞丢了

void headinsert(Node* list, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
		node->next = list->next;
		list->next = node;
		list->data++;
        //头结点的data一般存放这个链表除头结点外的节点个数,所以需要++
	}
}

  • 写出尾插法函数 

尾插法指将节点插入最后一个节点的后面,所以如果用尾插法依次插入1、2、3,实际在链表中的顺序是:头结点、1、2、3

在此函数中,我们需要利用while循环找到最后一个节点,将新节点链接在最后一个节点后面

void tailinsert(Node* list, int data)
{
	Node* head = list;
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
		node->next = NULL;
		list = list->next;
	}
	while (list->next != NULL)
	{
		list = list->next;
	}
	list->next = node;
	head->data++;
}

  • 写出删除节点函数

删除一个节点的基本操作是:利用while循环找到这个节点,将这个节点的前驱结点和后继节点链接在一起,然后将这个节点free掉 

所以,我们需要新建一个指针保存当前节点的前驱节点,否则循环到这个节点,就找不到它的前驱结点了(因为我们现在写的不是双向链表)

void delete_list(Node* list, int data)
{
	Node* pre = list;
	Node* current = list->next;
	while (current)
	{
		if (current->data == data)
		{
			pre->next = current->next;
			free(current);
			break;
		}
		pre = current;
		current = current->next;
	}
	list->data--;
}

  • 写出输出链表data的函数

循环打印即可~ 

void printlist(Node* list)
{
	list = list->next;
	while (list)
	{
		printf("%d ", list->data);
		list = list->next;
	}
	printf("\n");
}

到此,单链表的基本操作函数就都写完啦,下面我们可以写个main函数验证一下

int main()
{
	Node* list = initlist();
	headinsert(list, 1);
	headinsert(list, 2);
	headinsert(list, 3); 
	tailinsert(list, 6);
	tailinsert(list, 7);
	tailinsert(list, 8);
	printlist(list);

	delete_list(list, 2);
	printlist(list);
	return 0;
}

2.单循环链表

不同于单链表,单循环链表的最后一个节点的next指针指向的是头结点(就像一个环一样),所以操作基本上是一致的,下面只列出不一样的操作

  •  链表初始化函数

 此处的区别是,初始化头结点时,next指针指向自己

Node* initlist()
{
	Node* list = (Node*)malloc(sizeof(Node));
	if (list == NULL)
	{
		printf("error");
	}
	else
	{
		list->data = 0;
		list->next = list; //当没有尾节点时,头结点指向自己
	}
	return list;
}

  • 尾插法函数

此处的区别是,新节点的next指针指向头结点 ,并且while循环的判断条件是list->next != head

void tailinsert(Node* list, int data)
{
	Node* head = list;
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
	}
	while (list->next != head)
	{
		list = list->next;
	}
	node->next = head;
	list->next = node;
	head->data++;
}

  •  删除节点函数

 此处的区别是,while循环的判断条件不同

int deletelist(Node* list, int data)
{
	Node* pre = list;
	Node* node = list->next;
	while (node != list)
	{
		if (node->data == data)
		{
			pre->next = node->next;
			free(node);
		}
		pre = node;
		node = node->next;
	}
    list->data--;
}

  • 链表输出函数

此处的区别是,while循环的判断条件不同

void printlist(Node* list)
{
	Node* node = list->next;
	while (node != list)
	{
		printf("%d ", node->data);
		node = node->next;
	}
	printf("\n");
}

3.双链表

  • 定义结构体

不同于单链表,双链表是双向的,即可以通过一个节点找到它的前驱结点和后继节点,所以结构体中有pre和next两个指针 

typedef struct Node
{
	int data;
	struct Node* pre;
	struct Node* next;
} Node;

  • 其他函数 

双链表的操作函数与单链表区别不大,只不过从两个指针变成了四个指针(哪四个嘞?要插入节点的next和pre指针,前驱结点的next指针和后继节点的pre指针)

剩余函数放在下面大家自行查看

小tip:注意每次修改指针时的顺序,不要把节点搞丢了(当然双链表没有单链表容易丢) 

//初始化链表
Node* initlist()
{
	Node* l = (Node*)malloc(sizeof(Node));
	if (l == NULL)
	{
		printf("error");
	}
	else
	{
		l->data = 0;
		l->next = NULL;
		l->pre = NULL;
	}
	return l;
}

//头插法
void headinsert(Node* l, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
		if (l->data == 0)
		{
			node->next = l->next;
			l->next = node;
			node->pre = l;
		}
		else
		{
			node->pre = l;
			node->next = l->next;
			l->next->pre = node;
			l->next = node;
		}
		l->data++;
	}
}

//尾插法
void tailinsert(Node* l, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	Node* head = l;
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
		while (l->next)
		{
			l = l->next;
		}
		node->next = NULL;
		l->next = node;
		node->pre = l;
		head->data++;
	}
}

//删除节点
void deletelist(Node* l, int data)
{
	Node* node = l->next;
	while (node)
	{
		if (node->data == data)
		{
			node->pre->next = node->next;
			node->next->pre = node->pre;
			free(node);
			return TRUE;
		}
		node = node->next;
	}
}

4.双循环链表

经过上面三个链表的讲解,想必此时不用说你也知道,双循环链表就是最后一个节点的next指针指向头结点,且头结点的pre指针指向最后一个节点,剩余函数自行查看

//初始化时头结点的两个指针均指向自己
Node* initlist()
{
	Node* l = (Node*)malloc(sizeof(Node));
	if (l == NULL)
	{
		printf("error");
	}
	else
	{
		l->next = l; 
		l->pre = l;
		l->data = 0;
	}
	return l;
}

//头插法
void headinsert(Node* l, int data)
{
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL)
	{
		printf("error");
	}
	else
	{
		node->data = data;
		node->next = l->next;
		node->pre = l;
		l->next->pre = node;
		l->next = node;
		l->data++;
	}
}

//尾插法
void tailinsert(Node* l, int data)
{
	Node* node = l;
	while (node->next != l)
	{
		node = node->next;
	}
	Node* n = (Node*)malloc(sizeof(Node));
	if (n == NULL)
	{
		printf("error");
	}
	else
	{
		n->data = data;
		n->next = l;
		l->pre = n;
		n->pre = node;
		node->next = n;
		l->data++;
	}
}

//删除节点,注意while循环判断条件
int deletelist(Node* l, int data)
{
	Node* node = l->next;
	while (node != l)
	{
		if (node->data == data)
		{
			node->pre->next = node->next;
			node->next->pre = node->pre;
			free(node);
			l->data--;
			return 1;
		}
		node = node->next;
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gravity_w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值