数据结构之线性表的链式存储——链表

为了弥补顺序表插入和删除操作需要移动大量元素的缺点,一般采用链表存储。通俗理解的话就是让所有元素不必考虑存放位置,任意位置存放即可,哪里有空位就存到哪,只是需要每个元素知道其下一个元素的位置在哪里就好。这样我们就可以通过第一个元素找到第二个元素,从而通过遍历找到所有元素。

为了表示每个数据元素ai与其直接后继元素ai+1之间的逻辑关系,对数据元素ai来说,除了存储本身的信息之外,还需要存储一个指示其直接后继的信息(即直接后继的存储位置)。我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称为指针或链。这两部分信息组成数据元素ai的存储映像,称为结点。n个结点 链接成一个链表,称为线性表的链式存储结构。

我们把链表中的第一个结点的存储位置叫做头指针。为了更加方便的对链表进行操作,会在单链表的第一个结点前附设一个结点,称为头结点。头结点的数据域可以不存储信息,头结点的指针域存储指向第一个结点的指针。

头结点与头指针的比较:

头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针;

头指针具有标识作用,所以常用头指针冠以链表的名字;

无论链表是否为空,头指针均不为空。头指针是链表的必要元素。

头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般不存储信息;

有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其他结点的操作就统一了;

头结点不一定是链表的必须要素。

以不带头结点的单链表为例,常见的几种操作为:

1.插入操作

bool LinkListInsert(LinkList* head, int i, ElemType e)
{
	//插入位置不合法
	if (i < 0)
	{
		return false;
	}
	//头插
	if (i == 0)
	{
		NODE* pos = (NODE*)malloc(sizeof(NODE));
		pos->data = e;
		pos->next = *head;
		*head = pos;
		return true;
	}
	//寻找插入位置i的前一个结点
	LinkList pos = *head;//声明一个结点,从当前结点开始
	for (int j = 0; pos != NULL && j < i - 1; j++)
	{
		pos = pos->next;
	}
	if (pos == NULL)
	{
		return false;
	}
	NODE* s = (NODE*)malloc(sizeof(NODE));
	s->data = e;
	s->next = pos->next;
	pos->next = s;
	return true;
}

2.删除操作

bool LinkListDelete(LinkList* head, int i)
{
	if (i < 0)
	{
		return false;
	}
	//头删
	if (i == 0)
	{
		NODE* pos = *head;
		*head = pos->next;
		free(pos);
		pos = NULL;
		return true;
	}
	else
	{
		//寻找删除位置的前一个结点
		LinkList pos = *head;
		for (int j = 0; j < i - 1 && pos != NULL; j++)
		{
			pos = pos->next;
		}
		//如果没找到
		if (pos == NULL || pos->next == NULL)
		{
			return false;
		}
		else
		{
			NODE* s = pos->next;
			pos->next = s->next;
			free(s);
			s = NULL;
		}
	}
}

3.求单链表长度

int LengthLinkList(LinkList head)
{
	int len = 0;
	NODE* pos = head;
	while (pos)
	{
		len++;
		pos = pos->next;
	}
	return len;
}

4.单链表反转

void ListReverse(LinkList* head)
{
	//如果单链表中没有元素或者只有一个元素
	if (*head == NULL || (*head)->next == NULL)
	{
		return;
	}
	//定义三个指针,p1,p2,p3
	NODE* p1 = *head;
	NODE* p2 = p1->next;
	NODE* p3 = p2->next;
	//p2的next指向p1
	p2->next = p1;
	while (p3 != NULL)
	{
		p1 = p2;
		p2 = p3;
		p3 = p3->next;
		p2->next = p1;
	}
	(*head)->next = NULL;
	*head = p2;
}

5.找出单链表中倒数第i个元素

NODE* FindLast(LinkList head, int i)
{
	//快慢指针
	NODE* pFast;
	NODE* pSlow;
        //判断i的合法性
	if (i <= 0)
	{
		return 0;
	}
	int j = 0;
	for (pFast = head; pFast != NULL && j < i - 1; j++)
	{
		pFast = pFast->next;
	}
	if (pFast == NULL)
	{
		return pFast;
	}
	pSlow = head;
	while (pFast->next != NULL)
	{
		pSlow = pSlow->next;
		pFast = pFast->next;
	}
	return pSlow;
}

6.找出单链表的中间元素

NODE* GetMiddle(LinkList head)
{
	NODE* pFast = head;
	NODE* pSlow = head;
	if (pFast == NULL)
	{
		return pSlow;
	}
	while (pFast->next != NULL)
	{
		pFast = pFast->next;
		if (pFast->next == NULL)
		{
			break;
		}
		pFast = pFast->next;
		pSlow = pSlow->next;
	}
	return pSlow;
}

7.获取单链表中第i个位置的元素

NODE* GetElem(LinkList head, int i)
{
	if (i < 0)
	{
		return NULL;
	}
	NODE* pos = head;//声明一个结点指向L的第一个结点
	for (int j = 0; j < i && pos != NULL; j++)
	{
		pos = pos->next;
	}
	return pos;
}

8.清空操作

void ClearList(LinkList* head)
{
	//当单链表不为空时,头删
	while (*head)
	{
		LinkListDelete(head, 0);
	}
}

9.打印

void ShowList(LinkList head)
{
	NODE* pos = head;//声明一个结点s指向L的第一个节点
	while (pos)
	{
		printf("%d ", pos->data);
		pos = pos->next;
	}
	puts("");
}

测试函数和声明部分:

//shengmingbufen
typedef int ElemType;
typedef struct _NODE
{
	ElemType data;
	_NODE* next;
}NODE;
typedef NODE* LinkList;
//测试函数
int main()   
{
	LinkList head = NULL;
	LinkListInsert(&head, 0, 11);
	LinkListInsert(&head, 1, 33);
	LinkListInsert(&head, 2, 55);
	LinkListInsert(&head, 3, 77);
	LinkListInsert(&head, 4, 99);
	LinkListInsert(&head, 5, 10);
	printf("%d\n", LengthLinkList(head));
	ShowList(head);
	LinkListInsert(&head, 6, 100);
	printf("%d\n", LengthLinkList(head));
	ShowList(head);
	//删除
	LinkListDelete(&head, 6);
	printf("%d\n", LengthLinkList(head));
	ShowList(head);
	//反转
	ListReverse(&head);
	ShowList(head);
	//获取i位置的元素,如果找到则将该位置的元素置为88
	//NODE* pos = GetElem(head, 0);
	//if (pos!=NULL)
	//{
	//	pos->data = 88;
	//}
	//ShowList(head);
	//获取倒数第i个元素,如果找到则将该位置的元素置为88
	//NODE* pos = FindLast(head, 1);
	//if (pos != NULL)
	//{
	//	pos->data = 88;
	//}
	//ShowList(head);
	//获取链表中间元素,并将该元素置为88
	//NODE* pos = GetMiddle(head);
	//if (pos != NULL)
	//{
	//	pos->data = 88;
	//}
	//ShowList(head);
	ClearList(&head);
	printf("%d\n", LengthLinkList(head));
	ShowList(head);
	return 0;
}

以上即为我对单链表的内容的总结~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值