一、链表的结构
定义:是一组
任意的储存单元来存放线性表的结点
,这种存储单元可以是连续的,也可以是非连续的
特点:链表中结点的逻辑次序和物理次序不一定是相同
结点组成:数据域与指针域
单链表结点结构如下图:
N个结点链接成一个链表,即为线性表的链式存储结构,又因为只有一个指针域,又被称为单链表,或线性链表。
整个链表的存取必须从头开始,链表中第一个结点的位置叫做头指针,由于最后一个元素没有直接后继,则线性表的最后一个结点的指针为“空”(NULL),如下图:
有时为了方便对链表的操作,会在链表的第一个结点前加上一个结点,称为头结点,头结点一般不存储任何信息,有时存储线性表长度等附加信息。如下图:
头指针与头结点的比较:
二、案例分析:LeetCode-19
题意:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点
解题思路
删掉倒数第N个数,实际就是删掉第Length - N+1个数(删掉第一个元素:length - length + 1)
1.获取链表的长度Length
2.使结点指针p指向待删除的前一个位置
3.p->next = p->next->next //待删除结点的后驱给前驱
注意两种
:当结点为0 的时候,与删除结点是最后一个结点的时候
代码1:使用头指针
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* p=head; //定义一个结点指针p,与head指向第一个结点
int i=0,length=0;
while(p != NULL) //遍历结点获取链表的长度
{
p=p->next;
length++;
}
for(i=1,p=head;i<length-n;i++) //使指针走到待删除结点的前一个结点
{
p=p->next;
}
if(length==0) //链表为空的时候
head=NULL;
else if(length-n==0) //删除第一个位置结点,头指针会变化
head=head->next;
else
p->next=p->next->next; //删除其他位置结点
return head;
}
输入:[1 2 3 4 5]
预期输出:[1 2 3 5]
代码二:带头结点
int getLength(struct ListNode* head) {
int length = 0;
while (head) {
++length;
head = head->next;
}
return length;
}
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
struct ListNode* h = malloc(sizeof(struct ListNode));
h->val = 0, h->next = head;
int length = getLength(head);
struct ListNode* p = h;
for (int i = 1; i < length - n + 1; ++i) {
p = p->next;
}
p->next = p->next->next;
struct ListNode* ans = h->next;
free(h);
return ans;
}
可以很清楚的发现头指针与头结点的应用上的区别:
1、若不使用头结点,必然要对第一个结点的删除或者其他操作进行判断。
2、而使用头结点,首先得开辟一个结点空间,使用完后释放头结点
好处;不必考虑第一个结点处理问题,只需要返回头结点的后一个结点位置信息即可
即上述代码中结点指针ans,代码更加清晰明白!