Linked list reversal, specified interval reversal, k group reversal --- detailed explanation

There are three questions about inverting linked lists on Niuke, an introductory question, and there are many ways to invert linked lists. When I was doing the first question, I wrote a random one, and then I found out the method I used later, and I was doing the second question The third question was a bit cumbersome, so I considered the three questions together and chose a relatively clearer way of thinking. The three questions are solved using the same idea.

reverse linked list

topic

Reverse Linked List-Title link
Given a single linked list head node pHead (the head node has a value, for example, in the figure below, its val is 1), the length is n, after reversing the linked list, return the new linked list header.

train of thought

The method is to use three pointers to reverse the linked list pointing to the
cur pointer pointing to the current node, pre pointing to the predecessor, and temp pointing to the successor.
The operation is as shown in the figure:
In the initial state, pre is initialized to NULL
insert image description here
. Step 1: The pointer points back.
insert image description here
Step 2: Move the three pointers forward
insert image description here
. Then cycle through these two steps until the end to reverse the entire linked list.

Code

Time complexity: O(n)
Space complexity: O(1)

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
            val(x), next(NULL) {
    }
};*/
class Solution {
    
    
  public:
    ListNode* ReverseList(ListNode* pHead) {
    
    
        struct ListNode* cur =pHead;
		struct ListNode* pre = NULL;
		struct ListNode* temp = cur->next;
		while(cur!=NULL){
    
    
			cur->next = pre;
			pre = cur;
			cur = temp;
			temp = temp->next;
		}
		return pre;
    }
};

Invert the specified interval in the linked list

topic

Specified Interval Reversal - Topic Link

Reversing the interval between the m position and the n position of a linked list whose number of nodes is size requires time complexity O(n) and space complexity O(1).

train of thought

We first find the mth node, and then from m to n, we still use three pointers to invert it according to the idea of ​​the previous question.
But it should be noted that it is not enough for us to just find the mth node, we should also keep the predecessor node of m, so that it is convenient to link back to the main chain after the inversion.
Therefore, when we traverse, we use cur as the traverse pointer and pre as the precursor pointer.
Since the head node has no predecessor, we add a virtual node to make the operation uniform without considering the special situation of the head node separately.
When we traverse to the mth node, pre remains unchanged at this time, and then use the cur pointer, p pointer and t pointer to perform the inversion operation. Reverse all the way to the nth node.
Then link the reversed linked list back to the main linked list.
As shown in the figure, it is the case of table length = 5, m = 2, and n = 4.
insert image description here
Another thing to note: according to this method, we can return to the head at the end. But there is a special case, if m=1, that is to say, the first one starts to reverse, then the node pointed to by head will be the last node after being reversed, so you can’t return head, you should return pre-> next. So this step in the code needs special handling.

the code

Time complexity: O(n)
Space complexity: O(1)

/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 * };
 */

class Solution {
    
    
  public:
    /**
     *
     * @param head ListNode类
     * @param m int整型
     * @param n int整型
     * @return ListNode类
     */
    ListNode* reverseBetween(ListNode* head, int m, int n) {
    
    
        // write code here
        ListNode* pre = new ListNode(-1);
        pre -> next = head;//增加一个前驱节点
        ListNode* cur = head;
        for (int i = 1; i < m; i++) {
    
    
            pre = cur;
            cur = cur->next;
        }
        ListNode* p = NULL;
        ListNode* t = cur->next;
        for (int i = m; i <= n; i++) {
    
    
            cur->next = p;//回指
            p = cur;
            cur = t;
            t = t->next;
        }
        pre->next->next = cur;//链接回主链
        pre->next = p;
        if (m == 1) head = pre->next;
        return head;
    }
};

The nodes in the linked list are reversed every k groups

topic

The nodes in the linked list are reversed every k groups-topic link

Flip the nodes in the given linked list every k groups, and return the flipped linked list
If the number of nodes in the linked list is not a multiple of k, keep the last remaining node as it is
You cannot change the value in the node, only change the node itself.

train of thought

According to the ideas of our previous two questions, we only need to use three conditions to reverse any linked list: the first node of the linked list to be reversed, the predecessor of the first node, and the number of reversals.
As shown in the figure, if we want to reverse this linked list, we only need to know these three conditions.
insert image description here
The code is as follows, which is basically the idea of ​​the previous question:

void reverse(ListNode* pre, ListNode* cur, int k) {
    
    
        ListNode* head = pre;//把头保留下来 反转后链接的时候需要用到
        pre = NULL;//三个指针 开始反转
        ListNode* t = cur->next;
        for (int i = 1; i <= k; i++) {
    
    //限制反转数量为k
            cur->next = pre;
            pre = cur;
            cur = t;
            t = t->next;
        }
        //反转后 链接回主链
        head ->next->next = cur;
        head->next = pre;
    }

Then this operation is realized, and now the topic is simple, every group of k is reversed, we traverse the linked list, use a num to count when num=1,
record the predecessor pre and the current position cur, and then use it as num When it reaches k, it means that this section needs to be reversed.
We have the head node and its predecessor of this section of the linked list, so we can use this subfunction to achieve the reversal.
Suppose: k=4, then as shown in the figure, cur refers to the current node, which has reached the fourth one, cur_t and pre_t are the head and the precursor of the head of the linked list we saved, so we will give it to the
function , reverse, after the reverse is completed, as shown in the figure below.
There is one point to note here: before the reversal, cur points to the fourth node. Since the first four are reversed, after the reversal, cur should point to node 1.
So after inversion, we need to correct the pointing of cur. (Note that only the pointing of cur needs to be corrected, and pre does not need to be corrected, because after the reversal, node 4 has also been traversed, and the pointer will be moved back immediately, pre is directly equal to the corrected cur, and then cur can be moved backward .)
insert image description here
In addition, there is another problem, the same problem as the previous one, that is, the first set of inverted linked lists, the head needs to be reset, otherwise it will be wrong to return to the head.

the code

Time complexity: O(n)
Space complexity: O(1)
(In the worst case of this whole operation, it is equivalent to traversing the linked list twice, which is 2n, and the complexity is still O(n))

/**
 * struct ListNode {
 *  int val;
 *  struct ListNode *next;
 * };
 */

class Solution {
    
    
  public:
    /**
     *
     * @param head ListNode类
     * @param k int整型
     * @return ListNode类
     */
    void reverse(ListNode* pre, ListNode* cur, int k) {
    
    
        ListNode* head = pre;
        pre = NULL;
        ListNode* t = cur->next;
        for (int i = 1; i <= k; i++) {
    
    
            cur->next = pre;
            pre = cur;
            cur = t;
            t = t->next;
        }
        //反转后 连接回主链
        head ->next->next = cur;
        head->next = pre;
    }

    ListNode* reverseKGroup(ListNode* head, int k) {
    
    
        // write code here
        ListNode* pre = new ListNode(-1);
        pre->next = head;  //增加一个新的结点
        ListNode* cur = head;
        ListNode* cur_t = NULL;//保存待反转链表的头和前驱
        ListNode* pre_t = NULL;
        int flag = 1;//标记   第一组反转时 需将head重置
        int num = 1;
        while (cur != NULL) {
    
    
            if (num == 1) {
    
    //保存待反转链表的头和前驱
                cur_t = cur;
                pre_t = pre;
            }
            if (num % k == 0) {
    
    
                if (flag) {
    
     //第一次反转
                    head = cur;
                    flag = 0;
                }
                reverse(pre_t, cur_t, k);
                //反转完成后 纠正cur指针位置
                cur = cur_t;
                num = 0;//计数置0
            }
            pre = cur;
            cur = cur->next;
            num++;
        }
        return head;
    }
};

Guess you like

Origin blog.csdn.net/holly_Z_P_F/article/details/128313956