Bootstrap

数据结构初阶学习——抽象数据类型ADT(C语言描述)

1.链表

链表中一个单元的结构就是由一块数据域和一块指针域组成的,其中数据域中存储着链表所要存储的结构,而指针域是指向下一个单元的那块内存。而指针就像是一个链子一样,将一个一个链表单元(节点)串接起来。

相较于数组,链表的内存在空间中并不是连续的,这就使得链表可以存储得更加灵活,但同时链表有一个致命的缺陷,那就是无法随机访问,造成访问速率的下降

链表的示意图

链表的声明(在此为双向链表,在指针域多了一个forward指向前一个结点): 

struct DoubleNode;
typedef DoubleNode* DList;
typedef DoubleNode* PtrToDoubleNode;
typedef int EleType;

struct DoubleNode
{
	PtrToDoubleNode forward;
	PtrToDoubleNode next;
	EleType element;
};

相应的,链表也应通过函数来实现对应的操作

1.创建链表

创建链表只需通过动态内存分配函数申请空间,后用相应指针进行维护,然后将其前指针和后指针都置为NULL(为了防止意外访问其他内存),再将相应的输入值存放入数据域。

DList CreatDoubleList(EleType value)
{
	DList list = (DList)malloc(sizeof(DoubleNode));
	if (list == NULL)
	{
		printf("缺少足够空间");
		return NULL;
	}

	list->forward = NULL;
	list->next = NULL;
	list->element = value;
	return list;
}

2.头增

该操作完成在链表的头部增加一个节点的操作

其代码实现如下图:

void PushHead(DList* list,EleType value)
{
	if (*list)
	{
		PtrToDoubleNode Newhead = (PtrToDoubleNode)malloc(sizeof(DoubleNode));
		if (!Newhead)
		{
			printf("无足够空间");
			return;
		}
		Newhead->element = value;
		Newhead->next = *list;
		Newhead->forward = NULL;
		(*list)->forward = Newhead;
		*list = Newhead;
	}
	else
	{
		*list = CreatDoubleList(value);
	}
}

注意头节点指针的改变需要传头节点指针的指针才能使头节点指针指向新创建的节点处,以及考虑链表中无节点的情况即为创建新链表。

3.头删

该操作完成在头部删除一个节点的操作,同时也需注意头节点指针的改变,所以应该传头节点指针的指针及二级指针。还要考虑链表为空时不应该进行头删操作,代码块如下

void PopHead(DList* list)
{
	if ((*list)->next == NULL)//only exist header
	{
		free(*list);
		*list = NULL;
	}
	else if ((*list) == NULL)
	{
		printf("已经为空链表,无须再次删除");
		return;
	}
	else
	{
		PtrToDoubleNode NewHead = (*list)->next;
		free(*list);
		(*list) = NewHead;
	}
}

4.打印链表

打印链表的思路很简单,只需遍历链表到尾节点即可(尾节点的next指针为NULL)

void PrintDList(DList list)
{
	PtrToDoubleNode cur;
	cur = list;
	while (cur)
	{
		printf("%d->", cur->element);
		cur = cur->next;
	}
	printf("NULL");
}

5.找到最后一个节点

也是遍历链表,最后返回最后一个节点的地址

PtrToDoubleNode FindLast(DList list)
{
	PtrToDoubleNode cur = list;
	if (cur == NULL)
		return cur;
	while (cur->next)
	{
		cur = cur->next;
	}
	return cur;
}

6.找到某一个节点

该功能通过主调函数提供的值来进行寻找,如果节点的数据域与主调函数提供的值相匹配,就返回该节点的指针,如果找不到就返回空指针。如果存在多个相同的值,那么只返回第一个找到的值。其代码如下:

PtrToDoubleNode FindEle(DList list, EleType value)
{
	if (list == NULL)
	{
		printf("该链表为空");
		return NULL;
	}
	PtrToDoubleNode cur = list;

	//find and skip
	while (cur && cur->element != value)
	{
		cur = cur->next;
	}
	return cur;

}

7.删除给定值

该功能根据主调函数给的值来进行遍历链表,找到值后对其进行删除操作,并且考虑要删除的值为第一个值(即为头删)。其代码如下

void PopEle(DList* list, EleType ToBePop)
{
	PtrToDoubleNode aim = FindEle(*list, ToBePop);
    if (aim->forward == NULL)
	{
		PopHead(list);//是头节点的情况
	}

    else if (aim == NULL)
	{
		printf(" 无法找到该值");
		return;
	}

	else
	{
		
		aim->forward->next = aim->next;
		free(aim);
	}
}

8.前插

该功能实现在主调函数给定值的情况下,找到该值的位置并且在该值的前面插入新节点,并且考虑要在头节点进行插入的情况(头增)。链表为空的情况已在调用FindEle()中有对策,所以不用在次考虑,并且考虑找不到该值的状态

void InsertBefore(DList* list, EleType value, EleType aim)
{
	PtrToDoubleNode ptr_aim = FindEle(*list, aim);
    if (ptr_aim == NULL)
	{
		printf("链表中无该值");
		return;
	}

	else if (ptr_aim->forward == NULL)
	{
		PushHead(list, value);
	}

	else
	{
		PtrToDoubleNode NewNode = (PtrToDoubleNode)malloc(sizeof(DoubleNode));
		NewNode->element = value;//give value to newnode
		NewNode->next = ptr_aim;//newnode's next pointer point to aim
		NewNode->forward = ptr_aim->forward;//newnode's forward pointer point to fore_aim
		ptr_aim->forward = NewNode;
		NewNode->forward->next = NewNode;
	}
}

9.后插

该功能实现在主调函数给定值的情况下,找到该值的位置并且在该值的后面插入新节点,链表为空的情况已在调用FindEle()中有对策,所以不用在次考虑。并且考虑找不到该值的状态

void InsertBhind(DList list, EleType value, EleType aim)
{
	PtrToDoubleNode ptr_aim = FindEle(list, aim);
	PtrToDoubleNode NewNode = (PtrToDoubleNode)malloc(sizeof(DoubleNode));
	if (ptr_aim->next == NULL)
	{
		//push tail
		ptr_aim->next = NewNode;
		NewNode->forward = ptr_aim;
		NewNode->next = NULL;
		NewNode->element = value;
	}
	else if (ptr_aim == NULL)
	{
		printf("链表中无该值");
		return; //has reminded at FindEle()
	}
	else
	{
		PtrToDoubleNode aim_next = ptr_aim->next;
		NewNode->next = aim_next;
		ptr_aim->next = NewNode;
		NewNode->forward = ptr_aim;
		aim_next->forward = NewNode;
		NewNode->element = value;
	}
}

10.尾删

该功能实现将链表的尾部节点删除,并且考虑空链表的状态

void PopTail(DList* list)
{
	PtrToDoubleNode last = FindLast(*list);
	if (*list == NULL)
	{
		printf("该表为空表,无需进行删除操作");
		return;
	}
	else if (last->forward == NULL)
	{
		free(*list);
		*list = NULL;
	}
	else
	{
		PtrToDoubleNode fore_last = last->forward;
		free(last);
		fore_last->next = NULL;
	}
}

11.尾增

该功能实现在链表的末尾进行增加节点操作,并根据输入的值对节点的数据域进行赋值

void PushTail(DList* list, EleType value)
{
	PtrToDoubleNode last = FindLast(*list);
	PtrToDoubleNode NewNode = (PtrToDoubleNode)malloc(sizeof(DoubleNode));
	if (last == NULL)
	{
		PushHead(list, value);
		return;
	}

		//push tail
	last->next = NewNode;
	NewNode->forward = last;
	NewNode->next = NULL;
	NewNode->element = value;
}

;