一、 移除链表元素
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路:
我们可以直接暴力求解:遍历一遍链表,将不是val的节点存放到新的链表当中,返回新的链表。
struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* cur = head;
struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode* tail=newhead;
while (cur)
{
if (cur->val == val)
{
struct ListNode* next=cur->next;
free(cur);
cur=next;
}
else
{
tail->next=cur;
tail=tail->next;
cur=cur->next;
}
}
tail->next=NULL;
head=newhead->next;
free(newhead);
return head;
}
在LeetCode做这些OJ题malloc一般不会创建失败,可以不进行判断。
二、反转链表
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路:
反转是将节点原本指向下一个节点的指针指向了上一个节点,第一个指向了NULL。 我们可以先画图来理解:
先创建一个空指针(newhead),遍历一遍链表,每次遍历存储下一个节点,然后将当前节点的next指向newhead,再将当前节点赋值给newhead。遍历结束后返回newhead。
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* newhead=NULL;
struct ListNode* cur=head;
while(cur)
{
struct ListNode* next=cur->next;
cur->next=newhead;
newhead=cur;
cur=next;
}
return newhead;
}
三、链表的中间节点
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路:
遍历查找:遍历链表拿到节点的个数n,n/=2,再从头开始遍历。以数组下标为例,头节点下标为0找到下标为n的节点,这个节点就是中间节点,并且它肯定是中间节点的第二个节点。
时间复杂度:O(N)=N+2/N
快慢指针:创建两个指针来遍历(slow和fast),slow一次走一步(slow=slow->next),fast一次走两步(fast=fast->next->next),结束条件是fast为空或fast的下个节点为空。遍历结束slow刚好停在中间节点的第二个节点。
时间复杂度:O(N)=N
快慢指针:
struct ListNode* middleNode(struct ListNode* head) {
struct ListNode* slow=head;
struct ListNode* fast=head;
while(fast&&fast->next)
{
slow=slow->next;
fast=fast->next->next;
}
return slow;
}
四、链表中倒数第k个节点
思路:
暴力求解:遍历得到链表的节点个数n,再重新遍历,循环走n-k次就得到了倒数第k个节点(也可以参考上一题的遍历查找,以下标的角度来遍历)。
时间复杂度:O(N)=2N
快慢指针:fast先提前走k步,然后两个指针在同时遍历,fast为空时停下,slow就是倒数第k个节点。
时间复杂度:O(N)=N
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
struct ListNode* slow=pListHead;
struct ListNode* fast=pListHead;
while(k--)
{
if(fast==NULL)
{
return NULL;
}
fast=fast->next;
}
while(fast)
{
slow=slow->next;
fast=fast->next;
}
return slow;
}
五、合并两个有序链表
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
思路:
首先判断两个链表若一个为空那就直接返回另一个。创建一个新的指针(newlist)来链接list1和list2,循环遍历list1和list2,每次将它们两个中val较小的那个链接到newlist,list1或list2为空时结束循环,然后将其中不空的链接到newlist中。链接时使用cur指针指向newlist的尾节点来控制newlist的链接,循环时要单独判断newlist为空时的情况。
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list1;
struct ListNode* newlist = NULL;
struct ListNode* cur = NULL;
while (list1 && list2)
{
struct ListNode* tmp = NULL;
if (list1->val < list2->val)
{
tmp = list1;
list1 = list1->next;
}
else
{
tmp = list2;
list2 = list2->next;
}
if (cur == NULL)
{
newlist = tmp;
cur = newlist;
}
else
{
cur->next = tmp;
cur = cur->next;
}
}
cur->next = list1 == NULL ? list2 : list1;
return newlist;
}
六、链表分割
思路:
创建两个空指针(head1和head2),遍历pHead将小于x的节点放入head1,大于等于x的节点放入head2,遍历时使用tail1和tail2指向head1和head2的尾节点,head1和head2为空时单独判断。遍历结束后head1和head2一个为空,那就直接返回另一个。如果没有就将tail2的next置为空,再将head2链接给head1(遍历完pHead后tail2可能不是pHead的尾节点,那tail2的next就不为空,链接完后head1就会是循环链表),返回head1。
class Partition {
public:
ListNode* partition(ListNode* pHead, int x) {
ListNode* head1 = NULL;
ListNode* head2 = NULL;
ListNode* cur = pHead, * tail1 = head1, * tail2 = head2;
while (cur)
{
if (cur->val < x)
{
if (tail1 == NULL)
{
head1 = cur;
tail1 = head1;
}
else
{
tail1->next = cur;
tail1 = tail1->next;
}
}
else
{
if (tail2 == NULL)
{
head2 = cur;
tail2 = head2;
}
else
{
tail2->next = cur;
tail2 = tail2->next;
}
}
cur = cur->next;
}
if (tail1 == NULL)
{
return head2;
}
if (tail2 == NULL)
{
return head1;
}
tail2->next = NULL;
tail1->next = head2;
return head1;
}
};
七、链表的回文结构
思路:
这道题主要是将前面的快慢指针和链表反转结合了起来。
例链表为:1->2->3->3->2->1
先找到中间节点:
再从中间节点开始将后面的节点反转:
最后从原链表的尾节点(n1)和头节点(cur)开始遍历判断,n1==cur->next结束循环,返回true,如果循环时两个节点的val不相等则返回false。
class PalindromeList {
public:
bool chkPalindrome(ListNode* A) {
ListNode* slow = A, * fast = A;
ListNode* cur = A;
//找到中间节点
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
}
//将中间节点后面的节点反转
ListNode* n1 = slow, * n2 = slow->next;
while (n2)
{
ListNode* tmp = n2->next;
n2->next = n1;
n1 = n2;
n2 = tmp;
}
//第一个节点和最后一个节点开始相互比较
while (n1 != cur->next)
{
if (cur->val != n1->val)
return false;
cur = cur->next;
n1 = n1->next;
}
return true;
}
};