您的位置:首页 > 理论基础 > 数据结构算法

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

2019-01-22 15:45 274 查看

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

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

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

头结点与头指针的比较:

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

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

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

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

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

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

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

1.插入操作

[code]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.删除操作

[code]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.求单链表长度

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

4.单链表反转

[code]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个元素

[code]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.找出单链表的中间元素

[code]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个位置的元素

[code]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.清空操作

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

9.打印

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

测试函数和声明部分:

[code]//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;
}

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: