一、知识
1.哨兵节点:为了简化处理链表边界条件引入(如果操作可能产生新的头结点)
正常的链表插入操作:需要判断链表为空
正常的链表删除操作:需要判断链表为空;被删除的节点是头结点
注:找到删除节点的前一个节点进行操作比较容易
合理应用哨兵节点,就不再需要单独处理这些特殊的输入,从而杜绝由于忘记处理这些特殊输入而出现Bug的可能性。
2.双指针:
前后双指针(查找链表倒数第k个节点)、
快慢双指针:查找链表中心节点、链表中的环
3.循环链表
给定循环链表的头结点head,则可以通过判断链表中的一个指针p是否等于head来判断是否遍历完链表,防止死循环。
4.将一个节点插入cur与next之间,只需要使用到cur节点即可
insertNode->next = cur->next;
cur->next = insertNode;
二、题目
1.删除倒数第k个节点
思路:前后指针,前一个指针先移动k-1步(倒数第一个节点距离倒数第k个节点有k-1个长度)
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->next = head;
struct ListNode* front = head;
struct ListNode* back = dummy;
for(int i=0;i<n-1;i++)
front = front->next;
while(front->next != NULL)
{
front = front->next;
back = back->next;
}
back->next = back->next->next;
return dummy->next;
}
2.链表中环的入口节点
思路:
1.通过快慢指针找到判断是否有环并找到相遇节点
2.不需要得到环中节点数目,因为若相遇,说明慢指针走k步,快指针走了2k步,快指针多走的(2k-k)= k 步,使其相遇,说明k为环中节点数目的倍数。所以将一个新的指向头结点的指针和相遇节点的指针一起走,再次相遇即为入口节点
3.注意:新的指针在头结点时相当于已经走了一步,因此需要在头节点前插入哨兵节点,从哨兵节点处开始走,保证慢指针能走k步。
struct ListNode *MeetingNode(struct ListNode *head);
struct ListNode *detectCycle(struct ListNode *head) {
if(head == NULL)
return NULL;
struct ListNode *meet = MeetingNode(head);
if(meet == NULL)
return NULL;
struct ListNode *dummy = (struct ListNode *)malloc(sizeof(struct ListNode));
dummy->next = head;
struct ListNode *node = dummy;
while(meet != node)
{
meet = meet->next;
node = node->next;
}
return node;
}
struct ListNode *MeetingNode(struct ListNode *head)
{
if(head == NULL | head->next == NULL)
return NULL;
struct ListNode *slow = head;
struct ListNode *fast = slow->next;
while(slow != NULL && fast != NULL)
{
if(slow == fast)
return slow;
slow = slow->next;
fast = fast->next;
if(fast != NULL)
fast = fast->next;
}
return NULL;
}
3.两个链表的第1个重合节点
方法一:栈(弹栈比较)
方法二:遍历长度lenA和lenB,长的先走abs(lenA-lenB)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
int lenA = 0;
int lenB = 0;
struct ListNode *pA = headA;
struct ListNode *pB = headB;
struct ListNode *longer = (struct ListNode *)malloc(sizeof(struct ListNode *));
struct ListNode *shorter = (struct ListNode *)malloc(sizeof(struct ListNode *));
while(pA != NULL)
{
lenA++;
pA = pA->next;
}
while(pB != NULL)
{
lenB++;
pB = pB->next;
}
int difflen = lenA > lenB ? (lenA-lenB) : (lenB-lenA);
longer = lenA > lenB ? headA : headB;
shorter = lenA > lenB ? headB : headA;
for(int i=0;i<difflen;i++)
longer = longer->next;
while(longer != NULL && shorter != NULL)
{
if(longer == shorter)
return longer;
longer = longer->next;
shorter = shorter->next;
}
return NULL;
}
4.反转链表
思路:三个指针,前两个用来翻转,后一个指针记录节点用来防止链表断开
tips:在循环中声明end比事先声明好end要好,因为若事先声明好end,会有mid为空,mid->next无法访问的情况。
就一开始的情况而言,NULL充当了哨兵节点的作用。
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* front = NULL;
struct ListNode* mid = head;
while(mid != NULL)
{
struct ListNode* end = mid->next;
mid->next = front;
front = mid;
mid = end;
}
return front;
}
5.链表中数字相加
思路:链表翻转再相加
struct ListNode* reverseList(struct ListNode* head);
struct ListNode* addList(struct ListNode* l1,struct ListNode* l2);
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
l1 = reverseList(l1);
l2 = reverseList(l2);
struct ListNode* ret = addList(l1,l2);
return reverseList(ret);
}
struct ListNode* addList(struct ListNode* l1,struct ListNode* l2)
{
struct ListNode* ret = (struct ListNode*)malloc(sizeof(struct ListNode));
ret->val = 0;
struct ListNode* p = ret;
int flag = 0;
while(l1 != NULL || l2 != NULL)
{
int l1_val = l1 != NULL ? l1->val : 0;
int l2_val = l2 != NULL ? l2->val : 0;
int sum = l1_val + l2_val + flag;
if(sum>=10)
{
flag = 1;
sum -= 10;
}
else
flag = 0;
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->val = sum;
p->next = newNode;
p = p->next;
l1 = l1 == NULL ? NULL : l1->next;
l2 = l2 == NULL ? NULL : l2->next;
}
if(flag == 1)
{
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->val = 1;
p->next = newNode;
p = p->next;
}
p->next = NULL;
return ret->next;
}
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* front = NULL;
struct ListNode* mid = head;
while(mid != NULL)
{
struct ListNode* end = mid->next;
mid->next = front;
front = mid;
mid = end;
}
return front;
}
6.重排链表
思路:
1.找到中间节点
2.翻转后半段链表
3.合并
struct ListNode* reverseList(struct ListNode* head);
struct ListNode* middleNode(struct ListNode* head);
void mergeList(struct ListNode* l1, struct ListNode* l2);
void reorderList(struct ListNode* head){
struct ListNode* mid = middleNode(head);
struct ListNode* reverse = reverseList(mid->next);
mid->next = NULL;
struct ListNode* p1 = head;
struct ListNode* p2 = reverse;
mergeList(p1,p2);
}
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* front = NULL;
struct ListNode* mid = head;
while(mid != NULL)
{
struct ListNode* end = mid->next;
mid->next = front;
front = mid;
mid = end;
}
return front;
}
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* slow = head;
struct ListNode* fast = head;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
void mergeList(struct ListNode* l1, struct ListNode* l2)
{
struct ListNode* temp_l1 = l1;
struct ListNode* temp_l2 = l2;
while(l1 != NULL && l2 != NULL)
{
temp_l1 = l1->next;
temp_l2 = l2->next;
l1->next = l2;
l2->next = temp_l1;
l1 = temp_l1;
l2 = temp_l2;
}
}
作者:wu-ming-8e
链接:https://leetcode-cn.com/problems/LGjMqU/solution/jian-zhi-offer-ii-026-zhong-pai-lian-bia-uku9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
7.回文链表
思路:
1.找到中间节点
2.翻转后半段链表
3.比较
struct ListNode* middleNode(struct ListNode* head);
struct ListNode* reverseList(struct ListNode* head);
bool equalsList(struct ListNode* p1,struct ListNode* p2);
bool isPalindrome(struct ListNode* head){
struct ListNode* mid = middleNode(head);
struct ListNode* p1 = head;
struct ListNode* p2 = mid->next;
mid->next = NULL;
p2 = reverseList(p2);
return equalsList(p1,p2);
}
struct ListNode* middleNode(struct ListNode* head)
{
struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->next = head;
struct ListNode* fast = dummy;
struct ListNode* slow = dummy;
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
struct ListNode* reverseList(struct ListNode* head)
{
struct ListNode* front = NULL;
struct ListNode* mid = head;
while(mid != NULL)
{
struct ListNode* end = mid->next;
mid->next = front;
front = mid;
mid = end;
}
return front;
}
bool equalsList(struct ListNode* p1,struct ListNode* p2)
{
while(p1 != NULL && p2 != NULL)
{
if(p1->val != p2->val)
return false;
p1 = p1->next;
p2 = p2->next;
}
return true;
}
8.展平多级双向链表
思路:递归
class Solution {
public Node flatten(Node head) {
flattenGetTail(head);
return head;
}
private Node flattenGetTail(Node head){
Node node = head;
Node tail = null;
while(node != null){
Node next = node.next;
if(node.child != null){
Node child = node.child;
Node childTail = flattenGetTail(node.child);
node.child = null;
node.next = child;
child.prev = node;
childTail.next = next;
if(next != null){
next.prev = childTail;
}
tail = childTail;
}else{
tail = node;
}
node = next;
}
return tail;
}
}
9.排序的循环链表
思路:
1.在链表中找到相邻的两个节点cur与next,如果插入节点的值在二者之间,则插入;否则若找不到合适的节点,则将新节点插入到最大与最小节点之间(注意最小节点不一定是head,head并不一定是最小的,只是链表中的一个节点),即最大节点的下一个。
2.特殊情况:原链表没有节点和只有一个节点
void insertCore(struct Node* head, struct Node* insertNode);
struct Node* insert(struct Node* head, int insertVal) {
struct Node* insertNode = (struct Node*)malloc(sizeof(struct Node));
insertNode->val = insertVal;
if(head == NULL)
{
head = insertNode;
head->next = insertNode;
}
else if(head == head->next)
{
head->next = insertNode;
insertNode->next = head;
}
else
insertCore(head,insertNode);
return head;
}
void insertCore(struct Node* head, struct Node* insertNode)
{
struct Node* cur = head;
struct Node* next = head->next;
struct Node* biggest = head;
while(!(cur->val <= insertNode->val && insertNode->val <= next->val) && next != head)
{
if(next->val >= biggest->val)
biggest = next;
cur = next;
next = next->next;
}
if(cur->val <= insertNode->val && insertNode->val <= next->val)
{
insertNode->next = cur->next;
cur->next = insertNode;
}
else
{
insertNode->next = biggest->next;
biggest->next = insertNode;
}
}