Algorithm Questions - C++ (4) Evil Linked List

Although the linked list is good, it is easy to break and mess like a rope...
The array is also good, but the movement efficiency is low...

141. Circular Linked List

Determine if there is a cycle in a linked list

Fast and slow pointer: the step size of the fast pointer is 1 more than the slow pointer (Vs-Vf=1)

S+Vs-(F+Vf) = N-1

Vs-Vf = 1

Let S be the slow pointer step size F be the fast pointer step size

Difference: SF=N

S+1-(F+2)=N-1

After N times: SF=0

class Solution {
    
    
public:
    bool hasCycle(ListNode* head) {
    
    
        if (head == nullptr || head->next == nullptr) {
    
    
            return false;
        }
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (slow != fast) {
    
    
            if (fast == nullptr || fast->next == nullptr) {
    
    
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

876. Intermediate Node of Linked List

1. Violent traversal O(N) O(N)

Traversing a linked list with an array

end get length len

Then directly get the midpoint of the linked list l [len//2]

// vector::back() 返回vector容器的最后一个元素...
// vector::push_back(Q) 向vector容器里加入一个元素Q
// vector::pop_back() 删除vector最后一个元素
class Solution {
    
    
public:
    ListNode* middleNode(ListNode* head) {
    
    
        vector<ListNode*> A = {
    
    head};
        while(A.back()->next!=NULL)
        {
    
    
            A.push_back(A.back()->next);
        }
        return A[A.size()/2];
    }
};

2. Single pointer O(N) O(1)

The first traversal gets the length of the linked list N

The second traversal to the position of N//2 is the midpoint of the linked list

class Solution {
    
    
public:
    ListNode* middleNode(ListNode* head) {
    
    
        int len = 0;
        ListNode* t = head,*tt = head;
        while(t!=NULL)
        {
    
    
            t = t->next;
            len+=1;
        }
        for(int i=0;i<len/2;i++)
            tt = tt->next;
        return tt;
    }
};

3. Fast and slow pointers (it's you again...) O(N) O(1)

Define the fast and slow pointers at the head node position

The fast pointer is one step faster than the slow pointer, the fast pointer is 2 steps long at a time, and the slow pointer is 1 step long at a time

The termination condition is that the fast pointer is empty or the next node of the fast pointer is empty (because it returns the second node in the middle when it is even)

Practice here: After the experiment, I found that it is really clever, and other step sizes cannot satisfy...

A. When the length of the linked list is even, it is assumed that it is 4. The fast pointer must point to NULL, and the slow pointer is right at the second position in the middle.

slow------slow-----------slow

fast---------------------fast----------------------fast

1 ->       2    ->        3    ->        4       ->NULL

B. When the length of the linked list is odd, it is assumed that it is 5. The fast pointer must point to the end node, and the slow pointer is right at the middle node.

slow----------slow-----------slow

fast-------------------------fast--------------------------fast

1 ->          2     ->        3      ->      4       ->      5     ->    NULL
class Solution {
    
    
public:
    ListNode* middleNode(ListNode* head) {
    
    
        ListNode *fast=head,*slow=head;
        while(fast != NULL && fast->next != NULL)
        {
    
    
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
};

Sword refers to Offer 22. The k-th last node in the linked list

Set the fast and slow pointers to point to the head node.

Let the fast pointer go k steps first, and then set the fast and slow pointers to take one step. When the fast pointer traverses to the tail node, the slow pointer points to the kth node from the bottom.

how?

1->2->3->4->5->NULL, k=2 ====> [4->5]

faster: go k steps first, pointing to 3

slow: point to head 1

Then the next two pointers move 1 step at the same time, until the fast pointer reaches the end -> NULL

faster: oriented NULL

slow: oriented 4

class Solution {
    
    
public:
    ListNode* getKthFromEnd(ListNode* head, int k) {
    
    
        ListNode *faster = head, *slow = head;
        int cnt = 0;
        while(1){
    
    
                faster = faster->next;
                cnt += 1;
                if(cnt == k) break;
            }
        while(faster!=NULL)
        {
    
    
            slow = slow->next;
            faster = faster->next;
        }
        return slow;
    }
};

206. Reverse Linked List

It's hard to be a chicken, what kind of monster is 0-0!!!

The head insertion method inserts the last node to the beginning. Note that the meanings of head and head->next are not the same...

class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        ListNode *newHead = nullptr;
        while (head != nullptr) {
    
    
            //使用了一个 newHead 指针来记录新链表的头部,以及一个 head 指针来遍历原始链表
            ListNode *next = head->next;
            head->next = newHead;
            newHead = head;
            head = next;
        }
        return newHead;
    }
};
//newHead -> nullptr
//head -> A -> B -> C -> nullptr

//newHead -> A -> nullptr
//head -> B -> C -> nullptr

//newHead -> B -> A -> nullptr
//head -> C -> nullptr

//newHead -> C -> B -> A -> nullptr
//head -> nullptr

Tail insertion doesn't work for reversed lists because it just makes a copy

double pointer

// 注意啊 这里的什么*都是指针,并不是节点啊!没有节点需要被操作,我们操作的是他们之间的指针指向
class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        // 双指针辅助移动 pre->指向头节点 cur指向空
        ListNode* pre = head, *cur = NULL;
        while(pre!=NULL)
        {
    
    
           //需要存储一下pre指向下一节点的指针 不然没法移动
           ListNode* temp = pre->next;
           pre->next = cur; //让pre指向cur
           cur = pre; //让cur变成pre实现向右/左移动
           pre = temp;//这里不能写pre->next 因为这时候pre->next=cur...这里就要用temp
        }
        return cur;
    }
};

evil recursion

Traverse to the end, and then return to modify the pointing of the next node of the current node to the current node each time, and reset the pointing of the next pointing node to empty

class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        //  使用递归 出口是到尽头NULL
        // 遍历到最后一个节点 然后它将会变成头节点(因为是反转)
        // 让当前节点的下一个节点指向当前节点???4->5->NULL 5->4->NULL
        // 让当前节点next指向NULL
        if(head == NULL || head->next==NULL)
        {
    
    
            return head;
        }
        ListNode* ret = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return ret;
    }
};

demonized double pointer

///
class Solution {
    
    
public:
    ListNode* reverseList(ListNode* head) {
    
    
        if(head == NULL) return NULL;
        ListNode* cur = head;
        while(head->next!=NULL)
        {
    
    
            // head->next->next => head 先把head->next->next存起来 是新节点t
            // cur = head->next 移动下一个位置
            // 此时head->next要指向下一个新节点 t
            ListNode* t = head->next->next;//存好之后不需要担心之后找不到新节点的问题
            head->next->next = cur;
            cur = head->next;
            head->next = t;
        }
        return cur;
    }
};

92. Reverse Linked List II

Reverse the node order of the fixed lr... as an advanced version of the reverse linked list

The main reason is that if you need to embed the original linked list after reversing the linked list segment, you need to adjust the order of the pointers at the head and tail

Head insertion method:

class Solution {
    
    
    public ListNode reverseBetween(ListNode head, int left, int right) {
    
    
        // 定义一个dummyHead, 方便处理
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;    
	// 初始化指针
        ListNode g = dummyHead;
        ListNode p = dummyHead.next;

    // 将指针移到相应的位置
    for(int step = 0; step < left - 1; step++) {
    
    
        g = g.next; p = p.next;
    }

    // 头插法插入节点
    for (int i = 0; i < right - left; i++) {
    
    
        ListNode removed = p.next;
        p.next = p.next.next;
        removed.next = g.next;
        g.next = removed;
    }
    return dummyHead.next;
}
    

directly reversed...

Double pointer:

You also need to insert a sentinel node because when left==1, direct inversion will go wrong...

pre points to the traversal node cur points to the initialization node NULL

p0 is the previous node position of the inverted node...from dummy...

Linked list: d->1->2->3->4->5->NULL

Reverse [3,4] p0 = d; the number of traversals is (3-1) twice, p0 ->1->2 is exactly to the previous node

If you write 3, it is a node that is exactly reversed. In this way, the position of the node before the reversal cannot be saved, and then the linked list of the previous node cannot be connected...

The previous linked list that does not need to be reversed is d->1->2 p0 saves the subscript of the end position of this path

insert image description here

//自己实现的
class Solution {
    
    
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
    
    
     	ListNode *dummy = new ListNode(0);
        dummy->next = head;
    	ListNode *p0 = dummy;
        for(int i=0;i<left-1;i++)
        //遍历到left-1的位置 因为我需要把p0控制在反转节点的前一个节点的位置
            p0 = p0->next;
        //反转次数 right - left + 1 
        // cur 指向当前的被反转的元素 
        // pre 指向上一个元素
        ListNode *cur = p0->next, *pre = NULL;
        for(int i=0;i<right-left+1;i++)
        {
    
    
            ListNode *nxt = cur->next;//指向cur的下一个节点
            cur->next = pre;//修改cur的指向
            pre = cur; // 移动pre到已经反转的节点
            cur = nxt;// 移动cur到下一个节点
        }
        // 还没完 我们只是单纯反转了其中的[l,r]的方向
        // 拼接链表 
        // 此时pre = 已经反转的最后一个节点 是头节点
        // 此时cur = 已经反转节点的下一个节点 未反转节点的第一个
        // 以下的顺序不能改 不然无法修改p0->next->next的指向
        p0->next->next = cur;
        // p0->next 反转节点的第一个节点 再指向未反转链表的头节点cur
        p0->next = pre;
		// p0指向下一个节点是已经反转的最后一个节点
        return dummy->next;//head
    }
};

143. Rearrange Linked List

Investigate three questions: (Poison woman! Pure embarrassment)

  • 1. Fast and slow pointers to find intermediate nodes
  • 2. Reverse the second half of the linked list
  • 3. Merge the front and back linked lists

1->2->3->4->5->6

->Truncate 1->2->3 || 4->5->6

->Reverse the second half 1->2->3 || 6->5->4

->Interleaved merge6->1->5->2->4->3

pseudocode:

A

// 快慢指针找到中间节点...但是这里求的是中间节点的第一个
// 所以终止条件要模拟改变一下...
// 我们希望在链表长度是偶数的时候,slow落在中间部分的左侧
假设len = 4 以下类型是我们想要的 可以发现此时fast没有到末尾并且下一个节点也不是NULL,但是它下一个节点的下一个指向是NULL;
slow----slow
fast-----------fast
  1       2      3      4      NULL
假设len = 5 以下类型是我们想要的:此时fast只到末尾节点
slow----slow---slow
fast-----------fast----------fast
  1       2      3      4      5    NULL 
总结得到只要满足: fast->next!=NULL and fast->next->next!=NULL 就继续走
fast,slow = head,head
while fast->next!=NULL and faste->next->next!=NULL:
	fast = fast->next->next
    slow = slow->next
return slow
temp = slow

B

反转链表的操作...在A之后已经获得切割出来的头节点temp
由于是单链表后半段,故末尾一定指向NULL
head = temp
pre = NULL
while(head!=NULL)
{
    
    
    t = head->next;
    head->next = pre;
    pre = head;
    head = t;
}
return pre //头节点

C

ListNode *i = head1 , *j = head2

while i!=NULL and j!=NULL:
	t1 = i->next
	t2 = j->next
    
	i->next = j
	j->next = t
	
    i = t1
	j = t2

return head1

my code:

First implement 3 functions corresponding to the task objectives

1. Find the midpoint of the linked list

2. Reverse linked list

3. Merge linked list

class Solution {
    
    
public:
    void reorderList(ListNode* head) {
    
    
        // 特判
        if(head == NULL) return;
        // 找中间节点
        ListNode *mid = FindMidNode(head);
        // 这里注意是中间节点的第一个 所以反转的后半段是第二个中间节点开头的
        ListNode *reverse_right = reverseList(mid->next);
        // 断链之后要把中点指向的改写NULL
        mid->next = NULL;
        merge_List(head,reverse_right);
    }
    //找截断中点
    ListNode* FindMidNode(ListNode* head)
    {
    
    
        ListNode *fast = head, *slow = head;
        //这里注意和找中点的那个题不一样,偶数的长度要截断的是中点左侧的那个点
        while(fast->next != NULL && fast->next->next != NULL)
        {
    
    
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
    // 反转链表 这里因为是直接给出头节点所以直接遍历就可以
    // 如果没有找中点的话,就要根据left,right写一个反转链表2的函数...
    ListNode* reverseList(ListNode* head)
    {
    
    
        ListNode* pre = NULL;
        while(head!=NULL)
        {
    
    
            ListNode *t = head->next;
            head->next = pre;
            pre = head;
            head = t;
        }
        return pre;
    }
    void merge_List(ListNode *i, ListNode *j)
    {
    
    
        // 依次接起来 i  ->  j   ->  i->next  ->  j->next  -> ...
        while(i!=NULL && j!= NULL)
        {
    
    
            ListNode *ti = i->next;
            ListNode *tj = j->next;
            
            i->next = j;
            j->next = ti;
            
            i = ti;
            j = tj;
		}
    }
};

148. Sorting Linked List
Merge Sort C++ Merge Sort Board Questions Remember? ? ?

//先写一遍归并排序的板子
#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int tmp[N];
int a[N];
void merge_sort(int q[],int l,int r)
{
    
    
    //特判
    if(l>=r) return;
    //先划分中点
    int mid = l+r>>1;
    int i = l, j =mid+1, k = 0;;
    //快乐递归...
    merge_sort(q,i,mid);
    merge_sort(q,j,r);
    while(i<=mid && j<=r)
    {
    
    
        if(q[i]<=q[j]) tmp[k++] = q[i++];//i++ 先返回i 再++
        else tmp[k++] = q[j++];
    }
    // 长的一方并到tmp里
    while(i<=mid) tmp[k++] = q[i++];
   	while(j<=r) tmp[k++] = q[j++];
    for(int i=l,j=0;i<=r;i++,j++)
        q[i] = tmp[j];
}
int main()
{
    
    
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    merge_sort(a,0,n-1);
    for(int i=0;i<n;i++) cout<<a[i]<<" ";
    cout<<endl;
    return 0;
}

Leetcode code:

recursion

1. Find the midpoint in sections

2. Sort the two sub-lists separately

3. Merge sub-lists

//新建一个头节点用于存储合并结果
newnode = new ListNode(0)
*p = newnode
//传入 head1 head2 链表
//同时遍历 head1 head2 的节点 并比较当前节点值大小
while head1!=NULL and head2!=NULL:
	if head1->val < head2->val:
		p->next = head1
        head1 = head1->next
    else:
		p->next = head2
        head2 = head2->next
//合并长的链表
if(head1!=NULL) p->next = head1
if(head2!=NULL) p->next = head2
return newnode

Official code:

class Solution {
    
    
public:
    ListNode* sortList(ListNode* head) {
    
    
        if (head == nullptr || head->next == nullptr) return head;

        ListNode* head1 = head;
        ListNode* head2 = split_(head);

        head1 = sortList(head1);        //一条链表分成两段分别递归排序
        head2 = sortList(head2);

        return merge(head1, head2);     //返回合并后结果
    }

    //双指针找单链表中点模板 中点左侧
    ListNode* split(ListNode* head)     
    {
    
    
        ListNode *slow = head, *fast = head;

        while (fast->next->next != nullptr && fast->next != nullptr)
        {
    
    
            slow = slow->next;
            fast = fast->next->next;
        }
			// 这里必须要处理把链表断开,不然指针乱指
        ListNode* mid = slow->next;
        slow->next = nullptr;           //断尾
        return mid;
    }
    
    //双指针找单链表中点模板
    ListNode* split_(ListNode* head)     
    {
    
    
        ListNode *slow = head, *fast = head;
        //中点右侧
        ListNode *p = NULL;
        while(fast!= nullptr && fast->next != nullptr)
        {
    
    
            p = slow;
            slow = slow->next;
            fast= fast->next->next;
        }
        p->next = NULL;
        return slow;
    }

    //合并两个排序链表模板
    ListNode* merge(ListNode* head1, ListNode* head2)
    {
    
    
        ListNode *dummy = new ListNode(0), *p = dummy;

        while (head1 != nullptr && head2 != nullptr)
        {
    
    
            if (head1->val < head2->val)
            {
    
    
                p = p->next = head1;
                head1 = head1->next;
            }
            else
            {
    
    
                p = p->next = head2;
                head2 = head2->next;
            }
        }
        if (head1 != nullptr) p->next = head1;
        if (head2 != nullptr) p->next = head2;

        return dummy->next;
    }
};

Guess you like

Origin blog.csdn.net/daxuanzi515/article/details/130140260