数据结构之链表

链表

链表是一个常用且非常有用的数据结构,比如我之前的寻找多个色块的聚类算法中便是用了单向链表来储存图像数据。

先要知道什么是链表?链表的作用(为什么会使用链表)?链表的分类与特点,然后再选择一种链表来深入研究。

本文就按照上述步骤来讲述链表。

 

1、什么是链表?

地址不连续的节点序列,彼此通过指针相互连接,组成链表。

 

2、链表的作用(为什么要使用链表)

在c语言里面,由于数组需要提前申明大小,动态数组也需要一个明确的大小申请,这对于需要不断插入与删除对象的任务就会很有问题,因为如果使用数组来完成,为了满足要求,就不得不分配一个超级大的空间,这样子就会造成空间资源的浪费,而链表就是专门为了解决这类需要频繁插入与删除的任务而生成的。

除此之外,很多算法的都使用链表来存储数据,比如二叉树。

 

3、链表的分类与特点

(1)单向线性链表

(2)双向线性链表

(3)单向循环链表

(4)双向循环链表

(5)数组链表,链表的数据是一个指针,指向一个连续地址空间,即数组

(6)链表数组,数组中每个元素是指针,指针指向一个链表。

(7)多维链表,即一个链表的数据位放置一个指针,该指针又指向一个链表。二维链表,又称十字链表。

 

一般双向线性链表使用的最多。

 

4、双向线性链表

双向线性链表由节点组成,每一个节点包含一个数据和两个指针,两个指针分别指向该节点的前节点和后节点(如果没有则为空)。

一个链表结构体成员包括4个指针,一个指向头节点的指针,一个指向尾节点的指针,一个正向迭代指针,一个反向迭代指针。这里的迭代指针是为了完成用户需要的迭代操作而设立的,这样子做的好处是对链表实现的细节进行了比较好的封装,用户可以完全不用管怎么实现的,调用我们提供的函数即可,由于一些原因,后面的代码中没有实现这个迭代功能,不过这个实现起来也比较简单。

 

双向线性链表要实现的操作包括:

1、在尾部加入新节点

2、在头部加入新节点

3、左插节点

4、右插节点

5、删除一个位置的节点

6、删除链表中所有特定数据的节点

7、访问链表中的一个位置的数据

8、销毁链表

 

下面是一个c语言版本的双向线性链表,来自2014年达内培训的数据结构老师的讲解,代码可以说是完美。

头文件ls.h

/*双向线性链表*/

#ifndef LS_H
#define LS_H

#include <sys/types.h>
#include <stdbool.h>

/*node*/
typedef struct ListNode{
	int data;
	struct ListNode* prev;  /*front point*/
	struct ListNode* next;  /*back point*/
} LIST_NODE;

/*链表*/
typedef struct List{
	LIST_NODE* head; /*head point*/
	LIST_NODE* tail;/*tail point*/
	LIST_NODE* frwd;/*正向迭代指针*/
	LIST_NODE* bkwd;/*反向迭代指针*/
} LIST;

/*init*/
void list_init(LIST* list);

/*free rest node and recover to init*/
void list_deinit(LIST* list);

/*is empty?*/
bool list_emoty(LIST* list);

/*add to tail*/ 
void list_append_tail(LIST* list,int data);

/*add to head*/
void list_append_head(LIST* list, int data);

/*add to one node front*/
void list_insert_front(LIST* list,size_t pos,int data);

/*add to one node back*/
void list_insert_back(LIST* list, size_t pos, int data);

/*rand visit*/
int* list_at(LIST* list,size_t pos);

/*delete*/
bool list_erase(LIST* list,size_t pos);

/*delete all 'data'*/
void list_remove(LIST* list,int data);

/*clear all*/
void list_clear(LIST* list);

/*how many*/
size_t list_size(LIST* list);

#endif/*LS_H*/

c文件

#include <stdlib.h>
#include "ls.h"

/*create a new node*/
static LIST_NODE* create_node(int data,LIST_NODE* prev,LIST_NODE* next){
	LIST_NODE* node=malloc(sizeof(LIST_NODE));
	node->data=data;
	node->prev=prev;
	node->next=next;
	return node;
}

/*delete node,return that node's next and prev*/
static LIST_NODE* destroy_node(LIST_NODE* node,LIST_NODE** prev){
	LIST_NODE* next =node->next;
	if(prev)
		*prev=node->prev;
	free(node);
	return next;	
}

/*init*/
void list_init(LIST* list){
	list->bkwd = NULL;
	list->frwd = NULL;
	list->head=NULL;
	list->tail=NULL;
}

/*free rest node and recover to init*/
void list_deinit(LIST* list){
	while(list->head)
		list->head=destroy_node(list->head,NULL);
	list->tail = NULL;
}

/*is empty?*/
bool list_emoty(LIST* list){
	return !list->head&&!list->head;
}

/*add to tail*/
void list_append_tail(LIST* list,int data){
	list->tail=create_node(data,list->tail,NULL);
	if(list->tail->prev)
		list->tail->prev->next=list->tail;
	else
		list->head=list->tail;
}

/*add to head*/
void list_append_head(LIST* list, int data) {
	list->head = create_node(data, NULL,  list->head);
	if (list->head->next)
		list->head->next->prev = list->head;
	else
		list->tail = list->head;
}

/*add to one node front*/
void list_insert_front(LIST* list,size_t pos,int data){
	LIST_NODE* find=NULL;
	for(find=list->head;find;find=find->next)
		if(!pos--){
			LIST_NODE* node=create_node(data,find->prev,find);
			if(node->prev)
				node->prev->next=node;
			else
				list->head=node;
			node->next->prev=node;
			return true;
		}
	return false;
}

/*add to one node back*/
void list_insert_back(LIST* list, size_t pos, int data) {
	LIST_NODE* find = NULL;
	for (find = list->tail; find; find = find->prev)
		if (!pos--) {
			LIST_NODE* node = create_node(data, find, find->next);
			if (node->next)
				node->next->prev = node;
			else
				list->tail = node;
			node->prev->next = node;
			return true;
		}
	return false;
}

/*rand visit*/
int* list_at(LIST* list,size_t pos){
	LIST_NODE* find=NULL;
	for(find=list->head;find;find=find->next)
		if(!pos--)
			return &find->data;
	return NULL;
}

/*delete*/
bool list_erase(LIST* list,size_t pos){
	LIST_NODE* find=NULL;
	for(find=list->head;find;find=find->next)
		if(!pos--){
			LIST_NODE* prev=NULL;
			LIST_NODE* next=destroy_node(find,&prev);
			if(prev)
				prev->next=next;
			else
				list->head=next;
			if(next)
				next->prev=prev;
			else
				list->tail=prev;
			return true;
		}
}

/*delete all aaaas*/
void list_remove(LIST* list,int data){
	LIST_NODE* find=NULL,*next=NULL;
	for(find=list->head;find;find=next){
		next=find->next;
		if(find->data==data){
		    LIST_NODE* prev=NULL;
			LIST_NODE* next=destroy_node(find,&prev);
			if(prev)
				prev->next=next;
			else
				list->head=next;
			if(next)
				next->prev=prev;
			else
				list->tail=prev;
		}
	}
}

/*clear all*/
void list_clear(LIST* list){
	list_deinit(list);
}

/*how many*/
size_t list_size(LIST* list){
	size_t size=0;
	LIST_NODE* node=NULL;
	for(node=list->head;node;node=node->next){
		++size;
	}
	return size;
}

测试文件

/*测试双向线性链表*/

#include <stdio.h>
#include "ls.h"
#include <sys/types.h>
#include <stdbool.h>
/*push data in list*/
void list_add(LIST* list) {
	list_append_tail(list,1);
	list_append_tail(list,2);
	list_append_tail(list,3);
	list_append_tail(list,4);
	list_append_tail(list,4);
	list_append_tail(list,4);
	list_append_tail(list,3);
	list_append_tail(list,2);
	list_append_tail(list,1);
}
/*print list's member*/
void list_print(LIST* list) {
	LIST_NODE* node = NULL;
	size_t size = list_size(list); /*gain the size of list*/
	printf("there are %d members:\n",size);
	for (node = list->head; node; node = node->next)
		printf("%d ",node->data);
	printf("\n");
}

/*test insert front*/
void list_insert_front_test(LIST* list) {
	int data, pos;
	printf("please enter pos>>\n");
	scanf("%d",&pos);
	printf("please enter data>>\n");
	scanf("%d", &data);
	list_insert_front(list,pos,data);
}

/*test_erase*/
void list_erase_test(LIST* list) {
	int pos;
	printf("please enter pos>>\n");
	scanf("%d", &pos);
	list_erase(list, pos);
}

/*test_eraseall*/
void list_eraseall_test(LIST* list) {
	int data;
	printf("please enter the data you want to remove>>\n");
	scanf("%d", &data);
	list_remove(list, data);
}
int main() {
	LIST list;
	list_init(&list);
	list_add(&list);
	list_print(&list);
	while (1) {
		list_eraseall_test(&list);
		list_print(&list);
	}
	list_deinit(&list);
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一朝英雄拔剑起

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

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

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

打赏作者

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

抵扣说明:

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

余额充值