原题链接:
移除链表元素
第一种方法: 直接删除法
这种方法需要特殊考虑头结点的删除情况
首先考虑一般情况 ,删除的不是头结点,定义两个指针 prev , cur 一前一后,逐步删除移动
我们由图中可以看到,如果删除的是头结点,prev->next = cur->next 该步就会出错,因为 prev 此时是空指针
给出代码 :
struct ListNode* removeElements(struct ListNode* head, int val)
{
if(head == NULL)
{
return NULL;
}
struct ListNode* prev = NULL,* cur = head;
while(cur != NULL)
{
if(cur->val == val)
{
// 删除头结点的情况
if(cur == head)
{
cur = cur->next;
free(head);
head = cur;
}
// 删除非头结点的情况
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}
第二种方法 : 创建虚拟头结点
该方法可以将删除头结点和其他结点的情况进行统一
我们创建一个新节点来作为整个链表的头结点,该节点中的值没有意义,只是用该节点来方便我们的操作。如果用DummyNode->next=head; 此时 我们操作DummyNode的话就把第一个结点当做了一个普通节点来操作,此时头结点就可以被删除了。最后返回DummyNode->next就满足条件了。正是由于有个无意义节点作为头结点会统一操作(把头结点当做普通节点)所以实际链表设计过程中都是有个无意义头结点的,遇到第一个结点不好解决的问题,大家可以设一个节点试试。
给出代码 :
typedef struct ListNode ListNode;
// 创建虚拟头结点
ListNode* CreateListNode()
{
ListNode* DummyNode = (ListNode*)malloc(sizeof(ListNode));
if(DummyNode == NULL)
{
exit(-1);
}
else
{
DummyNode->next = NULL;
}
return DummyNode;
}
struct ListNode* removeElements(struct ListNode* head, int val)
{
ListNode* DummyNode = CreateListNode();
ListNode* cur = head,* prev = DummyNode;
DummyNode->next = head;
head = DummyNode;
// 执行删除操作
while(cur != NULL)
{
if(cur->val == val)
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
else
{
prev = cur;
cur = cur->next;
}
}
return head->next;
}
方法三 : 递归
递归思路:
struct ListNode* removeElements(struct ListNode* head, int val)
{
if(head == NULL)
{
return NULL;
}
head->next = removeElements(head->next,val);
return (head->val == val) ? head->next : head;
}
原题链接 :
链表的中间结点
第一种方法 :
快慢指针
(1) 链表结点个数为奇数个时 :
(2) 链表结点个数为偶数个时 :
给出代码 :
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* fast = head,* slow = head;
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
原题链接 :
链表中倒数第k个结点
方法: 快慢指针
先让快指针走 k 步,接着快指针和慢指针一起走,当快指针走到 NULL 时,慢指针走到倒数第 k 个结点
struct ListNode* getKthFromEnd(struct ListNode* head, int k)
{
if(head == NULL)
{
return NULL;
}
struct ListNode* fast = head,* slow = head;
// fast指针先走 k 步
while(k--)
{
// 判断 k 是否满足条件
if(fast != NULL)
{
fast = fast->next;
}
else
{
return NULL;
}
}
// 快慢指针同时移动
while(fast != NULL)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
原题链接 :
合并两个有序链表
第一种方法 : 尾插迭代法
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2)
{
if(l1 == NULL || l2 == NULL)
{
return (l1 == NULL) ? l2 : l1;
}
ListNode* head = NULL,* tail = NULL;
head = tail = (ListNode*)malloc(sizeof(ListNode));
while(l1 != NULL && l2 != NULL)
{
if(l1->val <= l2->val)
{
tail->next = l1;
tail = tail->next;
l1 = l1->next;
}
else
{
tail->next = l2;
tail = tail->next;
l2 = l2->next;
}
}
if(l1 == NULL)
{
tail->next = l2;
}
else
{
tail->next = l1;
}
return head->next;
}
第二种方法 : 递归
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
if(l1==NULL)
return l2;
if(l2==NULL)
return l1;
if(l1->val <= l2->val)
{
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}else
{
l2->next = mergeTwoLists(l1,l2->next);
return l2;
}
}
原题链接 :
分割链表
给出代码 :
class Partition {
public:
ListNode* partition(ListNode* pHead, int x)
{
ListNode* LessHead = (ListNode*)malloc(sizeof(ListNode));
ListNode* GreaterHead = (ListNode*)malloc(sizeof(ListNode));
LessHead->next = GreaterHead->next = NULL;
ListNode* LessHeadTail = LessHead;
ListNode* GreaterHeadTail = GreaterHead;
ListNode* cur = pHead;
while (cur != NULL)
{
if (cur->val < x)
{
LessHeadTail->next = cur;
LessHeadTail = LessHeadTail->next;
}
else
{
GreaterHeadTail->next = cur;
GreaterHeadTail = GreaterHeadTail->next;
}
cur = cur->next;
}
GreaterHeadTail->next = NULL;
LessHeadTail->next = GreaterHead->next;
ListNode* List = LessHead->next;
free(LessHead);
free(GreaterHead);
return List;
}
};
原题链接 :
反转链表
第一种方法 : 双指针
给出代码 :
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
ListNode* n1 = NULL;
ListNode* n2 = head;
while(n2 != NULL)
{
ListNode* n3 = n2->next;
n2->next = n1;
n1 = n2;
n2 = n3;
}
return n1;
}
第二种方法 : 头插法
设置一个新节点 newNode , 采取单链表中讲解到的头插的方法 ,即可达到逆序链表的目的
给出代码 :
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
// 设置新节点
ListNode* newNode = NULL,* cur = head;
// 进行头插操作,因为每次改变了 cur->next , 所以用 next 保存 cur 的下一个结点
while(cur != NULL)
{
ListNode* next = cur->next;
cur->next = newNode;
newNode = cur;
cur = next;
}
return newNode;
}
原题链接 :
链表的回文结构
方法 : 先找到链表的中间结点 , 具体方法可参考第二题 , 接着逆序从中间结点起往后的部分(逆序操作可看第六题),判断逆序后和前半部分结点是否相等
给出代码 :
typedef struct ListNode ListNode;
// 逆序链表操作
ListNode* reverseList(ListNode* head)
{
ListNode* n1 = NULL;
ListNode* n2 = head;
while(n2 != NULL)
{
ListNode* n3 = n2->next;
n2->next = n1;
n1 = n2;
n2 = n3;
}
return n1;
}
class PalindromeList {
public:
bool chkPalindrome(ListNode* A)
{
ListNode* fast = A;
ListNode* slow = A;
ListNode* prev = NULL;
// 找到链表中间结点
while(fast != NULL && fast->next != NULL)
{
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
slow = reverseList(slow);
// 判断前半部分结点和后半部分结点是否相等
while(A != NULL && slow != NULL)
{
if(A->val != slow->val)
{
return false;
}
else
{
A = A->next;
slow = slow->next;
}
}
return true;
}
};
原题链接 :
相交链表
方法 :
(1). 分别遍历两个链表,求出两个链表的长度 la , lb
(2).让指向较长链表的指针移动 abs(lb - la)步
(3).两个指针同时移动,若存在两个指针相等的情况,则返回该结点,若不存在,则返回NULL
给出代码 :
typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
if(headA == NULL || headB == NULL)
{
return NULL;
}
ListNode* curA = headA,* curB = headB;
int la = 0,lb = 0;
// 求链表 A 的长度
while(curA !=NULL)
{
la++;
curA = curA->next;
}
// 求链表 B 的长度
while(curB != NULL)
{
lb++;
curB = curB->next;
}
curA = headA,curB = headB;
int count = (lb > la) ? (lb - la) : (la - lb);
int i = 0;
// 让指向较长链表的指针移动 abs(lb - la)步
for(i = 0;i < count;i++)
{
if(lb > la)
{
curB = curB->next;
}
else
{
curA = curA->next;
}
}
// 两个指针同时移动
while(curA && curB)
{
if(curA != curB)
{
curA = curA->next;
curB = curB->next;
}
else
{
return curA;
}
}
return NULL;
}
原题链接 :
环形链表
当一个链表有环时,快慢指针都会陷入环中进行无限次移动,然后变成了追及问题。想象一下在操场跑步的场景,只要一直跑下去,快的总会追上慢的。当两个指针都进入环后,每轮移动使得慢指针到快指针的距离增加一,同时快指针到慢指针的距离也减少一,只要一直移动下去,快指针总会追上慢指针。
给出代码 :
typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head)
{
if(head == NULL)
{
return false;
}
ListNode* fast = head,* slow = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
{
return true;
}
}
return false;
}