1 移除链表元素
. - 力扣(LeetCode)
该题的思路是创建一个新链表,然后遍历原链表,将不是要求移除的链表元素放到新链表中,最后返回创建的新链表 就能达到移除链表元素的作用了。
当然这只是一种做法,还有很多能够实现该功能的做法,我们尽量思考怎样写才能让我们的代码空间复杂度和时间复杂度都更小。
typedef struct ListNode ListNode ;
ListNode* removeElements(ListNode* head, int val)
{
//创建新链表
ListNode* newhead,*newtail;
newhead = newtail = NULL;
//遍历原链表
ListNode* pcur = head;
while (pcur != NULL)
{
if (pcur->val != val)
{
//链表为空
if (newhead == NULL)
{
newhead = newtail = pcur;
}
//链表不为空
else
{
newtail->next = pcur;
newtail = newtail->next;
}
}
pcur = pcur->next;
}
if (newhead)
newtail->next = NULL;
return newhead;
}
2.反转链表
. - 力扣(LeetCode)
在该题中,一个比较巧妙的方法是创建三个指针n1,n2,n3,n1初始化为空,n2初始化为头节点,n3为头结点的下一个节点,让中间指针n2从头开始遍历,把n2节点的next指针修改为指向n1,这样就能达到反转链表的效果了。
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
if (head == NULL)
{
return NULL;
}
ListNode* n1, * n2, * n3,*pcur;
n1 = NULL;
n2 = head;
n3 = n2->next;
while (n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
3.链表的中间节点
. - 力扣(LeetCode)
在该题中,我们使用链表中常用的一种技术 -- 快慢指针。定义两个指针,慢指针一次走一步,快指针一次走两步,当快指针为空或快指针的下一个节点为空时停止,这是慢指针就是我们要找的中间节点。需要注意的是while循环的条件,fast和fast->next的位置不能交换,因为如果链表节点数为偶数,那么fast最后会指向NULL,而fast->next会报错。
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head)
{
ListNode* slow, * fast;
slow = fast = head;
while (fast && fast->next)//此处fast和fast->next位置不能交换
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
4.合并连两个有序链表
. - 力扣(LeetCode)
该题的思路是:创建一个新链表,定义一个头指针newhead一个尾指针newtail,遍历比较所给的两个链表并比较它们的值,如果list1的值小于list2的值,则将新链表尾指针newltail指向list1,newtail和list1再指向下一个节点,反之操作list2。遍历完其中一个链表之后,将newtail直接指向另一个链表。当然,如果所给的两个链表其中一个链表为空,则直接返回另一个链表,如果两个链表都为空,返回另一个链表也就是返回空。
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
if (list1 == NULL)
{
return list2;
}
if (list2 == NULL)
{
return list1;
}
ListNode* newhead, * newtail;
newhead = newtail = (ListNode*)malloc(sizeof(ListNode));
ListNode* l1 = list1;
ListNode* l2 = list2;
while (l1 && l2)
{
if (l1->val < l2->val)
{
newtail->next = l1;
newtail = newtail->next;
l1 = l1->next;
}
else
{
newtail->next = l2;
newtail = newtail->next;
l2 = l2->next;
}
}
if (l1)
{
newtail->next = l1;
}
if (l2)
{
newtail->next = l2;
}
ListNode* ret = newhead->next;
free(newhead);
newhead = NULL;
return ret;
}
5.链表分割
链表分割_牛客题霸_牛客网
该题的思路是:创建两个链表,遍历所链表,将值小于x的节点放到链表LessHead中,大的放到链表GeaterHead中。
typedef struct ListNode ListNode;
ListNode* partition(ListNode* pHead, int x)
{
ListNode* LessHead, * LessTail;
LessHead = LessTail = (ListNode*)malloc(sizeof(ListNode));
ListNode* GeaterHead, * GeaterTail;
GeaterHead = GeaterTail = (ListNode*)malloc(sizeof(ListNode));
ListNode* pcur = pHead;
while (pcur)
{
if (pcur->val < x)
{
LessTail->next = pcur;
LessTail = LessTail->next;
pcur = pcur->next;
}
else
{
GeaterTail->next = pcur;
GeaterTail = GeaterTail->next;
pcur = pcur->next;
}
}
LessTail->next = GeaterHead->next;
ListNode* ret = LessHead->next;
GeaterTail->next = NULL;
free(LessHead);
LessHead = NULL;
free(GeaterHead);
GeaterHead = NULL;
return ret;
}
6.链表的回文结构
链表的回文结构_牛客题霸_牛客网
做该题的一个思路是:先使用快慢指针找到所给链表的中间元素,然后以中间元素为头节点定义三个指针反转链表,最后左右同时开始遍历判断链表是否为回文结构。
ListNode* findMiddle(ListNode* phead)
{
ListNode* slow, * fast;
slow = phead;
fast = phead;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* reverseListNode(ListNode* phead)
{
ListNode* n1, * n2, * n3;
n1 = NULL;
n2 = phead;
n3 = n2->next;
while (n2)
{
n2->next = n1;
n1 = n2;
n2 = n3;
if (n3)
n3 = n3->next;
}
return n1;
}
bool chkPalindrome(ListNode* A)
{
//1.找中间节点
ListNode* mid = findMiddle(A);
//2.反转链表
ListNode* right = reverseListNode(mid);
ListNode* left = A;
while (right)
{
if (left->val != right->val)
{
return false;
}
left = left->next;
right = right->next;
}
return true;
}
7.相交链表
. - 力扣(LeetCode)
该题的一个方法是:首先定义两个指针分别遍历给出的两个链表,并计算这两个链表的大小,如果这两个链表的最后一个节点相同,那么这两个链表一定相交,不相等则不相交,返回空,如果相交,则定义一个指向长链表的指针,一个指向短链表的指针,并通过代码中的方法确定那个是长链表哪个是短链表,并使长链表提前走几步(长短链表长度之间的差值),使两个指针遍历的长度相同,最后找到两个链表开始相交的节点。
typedef struct ListNode ListNode;
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
ListNode* pheadA = headA;
ListNode* pheadB = headB;
int sizeA = 0;
int sizeB = 0;
while (pheadA->next)
{
pheadA = pheadA->next;
sizeA++;
}
while (pheadB->next)
{
pheadB = pheadB->next;
sizeB++;
}
if (pheadA != pheadB)
{
return NULL;
}
else
{
int gap = abs(sizeA - sizeB);
ListNode* longList = headA;
ListNode* shortList = headB;
if (sizeA < sizeB)
{
longList = headB;
shortList = headA;
}
while (gap--)
{
longList = longList->next;
}
while (longList != shortList)
{
longList = longList->next;
shortList = shortList->next;
}
return longList;
}
}
8.环形链表1
. - 力扣(LeetCode)
该题的方法是:定义快慢指针,遍历链表,如果该链表是环形链表,则快指针会追赶上慢指针,不是环形链表,快指针会指向空,值得一提的是,如果莲表是环形链表,无论快指针每次走两步还是三步都一定能与慢指针相等,用数学方法可以证明,感兴趣的朋友可以尝试证明一下。
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode* head) {
ListNode* slow, * fast;;
slow = fast = head;
while (fast && fast->next)
{
slow = slow->next;
int n = 3;
while (n--)
{
if (fast->next)
{
fast = fast->next;
}
else
{
return false;
}
}
if (slow == fast)
{
return true;
}
}
return false;
}
9.环形链表2
. - 力扣(LeetCode)
该题先通过快慢指针找到相遇点,再定义⼀个指针从链表起始位置运行,⼀个指针从相遇点位置绕环,每次都走⼀步,两个指针最终会在入口点的位置相遇 。该结论也是可以通过数学方法证明两个指针一定会在入口点的位置相遇。
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head)
{
ListNode* slow, * fast;
slow = fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast)
{
ListNode* pcur = head;
while (pcur != slow)
{
slow = slow->next;
pcur = pcur->next;
}
return slow;
}
}
return NULL;
}
更多链表算法刷题链接:
牛客网:https://www.nowcoder.com/exam/oj
LeetCode:https://leetcode.cn/problems/copy-list-with-random-pointer/description/