Data Structure Notes - Linked List Classic High Frequency Questions

Table of contents

foreword

1--Reverse one-way linked list

2--Reverse one-way linked list-II

3--Reverse doubly linked list

4--Print the common part of two ordered linked lists

5--Palindrome linked list

6--List adjustment

7-- Copy a linked list containing random pointer nodes

8--Two singly linked list intersection problem


foreword

Face scriptures:

        For linked list questions, you don’t care much about the space complexity of the written test, and focus on the time complexity (if you can pass it, it’s the same for any question type, you can pass the written test); for the interview, the time complexity is still in the first place bit, but strive for the algorithm with the lowest space complexity (highlight the highlights);

        The important skills of linked list questions include: using additional data structure records (such as hash tables, etc.), using the idea of ​​​​fast and slow pointers ;

1--Reverse one-way linked list

Written test solution:

        With the help of the stack first in last out, you can traverse and store the nodes in the stack, and then pop out the stack continuously, so that the order of the nodes is reversed;

        The time complexity is O(n), and the space complexity is O(n);

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        std::stack<ListNode*> st;
        while(head != NULL){
            st.push(head);
            head = head->next;
        }
        ListNode *new_head = new ListNode(0);
        ListNode *tmp = new_head;
        while(!st.empty()){
            tmp->next = st.top();
            st.pop();
            tmp = tmp->next;
        }
        tmp->next = NULL;
        return new_head->next;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

Interview solution:

        Optimize the space complexity to O(1) through iteration without stack or recursion;

        Use an additional precursor node pre to store the previous node of the current node cur, and keep updating pre and cur;

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur != NULL){
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

2--Reverse one-way linked list-II

Main idea:

        Use three pointers, the pointer pre points to the first node outside the reversed area, which is 1 in the above figure; the pointer cur points to the current pointer, and the pointer next points to the next pointer of cur;

        Traversing the linked list, inserting the next pointer each time, the specific process can refer to the official solution ;

#include <iostream>

struct ListNode {
    int val;
    ListNode *next;
    ListNode() : val(0), next(nullptr) {}
    ListNode(int x) : val(x), next(nullptr) {}
    ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        ListNode *dummyNode = new ListNode(-1);
        dummyNode->next = head;
        ListNode *pre = dummyNode;
        ListNode *cur;
        ListNode *next;
        // 经过循环之后,pre指向反转区域前的第一个节点
        for(int i = 0; i < left - 1; i++){
            pre = pre->next;
        }
        cur = pre->next; // cur指向反转区域的第一个节点
        for(int i = 0; i < right - left; i++){
            next = cur->next;
            cur->next = next->next; // cur指向next的下一个节点,因为next节点要头插到pre节点后面
            next->next = pre->next; // next节点头插,指向原来的第一个节点
            pre->next = next; // next节点头插到pre节点后面
        }
        return dummyNode->next;
    }
};

int main(){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Solution S1;
    int left = 2, right = 4;
    ListNode *res = S1.reverseBetween(Node1, left, right);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

3--Reverse doubly linked list

Main idea:

        Similar to the reverse one-way linked list, use pre, cur and next to point to the previous node, the current node and the next node, and continuously traverse and update the nodes pointed by the three pointers, and modify the corresponding predecessor pointer and rear driver pointer;

#include <iostream>
#include <stack>

struct ListNode {
     int val;
     ListNode *pre;
     ListNode *next;
     ListNode() : val(0), pre(nullptr), next(nullptr) {}
     ListNode(int x) : val(x), pre(nullptr), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), pre(nullptr), next(next) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL) return head;
        ListNode *pre = NULL;
        ListNode *cur = head;
        while(cur != NULL){
            ListNode* next = cur->next;
            cur->next = pre;
            cur->pre = next;
            pre = cur;
            cur = next;
        }
        return pre;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(4);
    ListNode *Node5 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Node2->pre = Node1;
    Node3->pre = Node2;
    Node4->pre = Node3;
    Node5->pre = Node4;

    Solution S1;
    ListNode *res = S1.reverseList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        if(res->pre != NULL) std::cout << res->pre->val;
        std::cout << std::endl;
        res = res->next;
    }

    return 0;
}

4--Print the common part of two ordered linked lists

        Given the head pointers head1 and head2 of two ordered linked lists, print the common part of the two linked lists; the required time complexity is O(n), and the additional space complexity requirement is O(1);

Main idea:

        Similar to merge sort, since the two linked lists are ordered, two pointers i and j can be used to point to the two linked lists respectively;

        For small linked list nodes, the pointer moves backward;

        When it is compared that the two pointers are equal, the value of the node is printed, and the two pointers i and j are moved backward at the same time;

#include <iostream>
#include <vector>

struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    std::vector<ListNode*> printlist(ListNode* head1, ListNode* head2) {
        std::vector<ListNode*> res;
        if(head1 == NULL || head2 == NULL) return res;
        ListNode *i = head1;
        ListNode *j = head2;
        while(i != NULL && j != NULL){
            // 小的后移
            if(i->val < j->val) i = i->next;
            else if(i->val > j->val) j = j->next;
            else{ // 相等同时后移
                res.push_back(i);
                i = i->next;
                j = j->next;
            } 
        }
        return res;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(5);

    ListNode *Node4 = new ListNode(0);
    ListNode *Node5 = new ListNode(2);
    ListNode *Node6 = new ListNode(3);
    ListNode *Node7 = new ListNode(5);

    Node1->next = Node2;
    Node2->next = Node3;


    Node4->next = Node5;
    Node5->next = Node6;
    Node6->next = Node7;

    Solution S1;
    std::vector<ListNode *> res = S1.printlist(Node1, Node4);
    for(ListNode * node : res) std::cout << node->val << " ";
    return 0;
}

5--Palindrome linked list

Main idea:

        The interview method can refer to reversing the one-way linked list, reversing the linked list, and comparing it with the nodes of the original linked list. When the reversed linked list is not equal to the nodes of the original linked list, it indicates that it is not a palindromic linked list;

        Space complexity O(n), time complexity O(n);

#include <iostream>
#include <stack>

struct ListNode {
     int val;
    ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL) return true;
        std::stack<ListNode*> st;
        ListNode *tmp = head;
        while(tmp != NULL){
            st.push(tmp);
            tmp = tmp->next;
        }
        while(!st.empty()){
            if(head->val != st.top()->val) return false;
            head = head->next;
            st.pop();
        }
        return true;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(2);
    ListNode *Node4 = new ListNode(1);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;

    Solution S1;
    bool res = S1.isPalindrome(Node1);
    if(res) std::cout << "true" << std::endl;
    else std::cout << "false" << std::endl;
    return 0;
}

Main idea:

        Written test solution: the space complexity of the above solution is O(n), use the fast and slow pointer to optimize the space complexity to O(1);

        The main principle is to construct the linked list from 1→2→1→2→1 into the form of 1→2→1←2←1, and traverse from both ends for comparison;

#include <iostream>

struct ListNode {
     int val;
    ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        if(head == NULL) return true;
        ListNode *i = head;
        ListNode *j = head;
        while(j->next != NULL && j->next->next != NULL){
            i = i -> next;
            j = j -> next -> next;
        }
        j = i->next; // right part first node
        i->next = NULL;
        ListNode *tmp = NULL;
        while(j != NULL){
            tmp = j->next;
            j->next = i;
            i = j;
            j = tmp;
        }
        j = i; // 最后一个结点
        i = head;
        while(i != NULL && j != NULL){
            if(i->val != j ->val) return false;
            i = i->next;
            j = j->next;
        }
        return true;
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(1);
    ListNode *Node2 = new ListNode(2);
    ListNode *Node3 = new ListNode(2);
    ListNode *Node4 = new ListNode(1);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;

    Solution S1;
    bool res = S1.isPalindrome(Node1);
    if(res) std::cout << "true" << std::endl;
    else std::cout << "false" << std::endl;
    return 0;
}

6--List adjustment

        Divide the one-way linked list into a form of small left, equal in the middle, and large on the right according to a certain value;

        Topic: Given a head node head of a singly linked list, the value type of the node is an integer, and then given an integer pivot, implement a function to adjust the linked list, and adjust the linked list so that the left part of the linked list is all nodes whose value is less than the pivot point, the middle part is a node whose value is equal to pivot, and the right part is a node whose value is greater than pivot;

        Requirements: After adjustment, the relative order of all nodes less than, equal to or greater than pivot is the same as before adjustment, the time complexity is O(n), and the space complexity is O(1);

#include <iostream>

struct ListNode {
     int val;
    ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

class Solution {
public:
    ListNode* change(ListNode* head, int pivot) {
        if(head == NULL) return head;
        ListNode* SH = NULL; // small head
        ListNode* ST = NULL; // small tail
        ListNode* EH = NULL; // equal head
        ListNode* ET = NULL; // equal tail
        ListNode* LH = NULL; // large head
        ListNode* LT = NULL; // large tail

        ListNode* tmp;
        while(head != NULL){
            tmp = head->next; // 下一个结点
            head->next = NULL;
            // 抽每一个结点出来进行比较
            if(head->val < pivot){
                if(SH == NULL && ST == NULL){
                    SH = head;
                    ST = head;
                }
                else{
                    ST->next = head;
                    ST = ST->next;
                }
            }
            else if(head->val == pivot){
                if(EH == NULL && ET == NULL){
                    EH = head;
                    ET = head;
                }
                else{
                    ET->next = head;
                    ET = ET->next;
                }
            }
            else{
                if(LH == NULL && LT == NULL){
                    LH = head;
                    LT = head;
                }
                else{
                    LT->next = head;
                    LT = LT->next;
                }
            }
            head = tmp; // 比较下一个结点
        }
        // 首尾相连
        if(ST != NULL){// 有小于区域
            ST->next = EH;
            ET = ET == NULL ? ST : ET; // 没有等于区域,ET变成ST
        } 
        if(ET != NULL) ET->next = LH;
        return SH != NULL ? SH : (EH != NULL ? EH : LH);
    }
};

int main(int argc, char *argv[]){
    ListNode *Node1 = new ListNode(4);
    ListNode *Node2 = new ListNode(6);
    ListNode *Node3 = new ListNode(3);
    ListNode *Node4 = new ListNode(5);
    ListNode *Node5 = new ListNode(8);
    ListNode *Node6 = new ListNode(5);
    ListNode *Node7 = new ListNode(2);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;
    Node5->next = Node6;
    Node6->next = Node7;

    Solution S1;
    int pivot = 5;
    ListNode* res = S1.change(Node1, pivot);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }

    return 0;
}

7-- Copy a linked list containing random pointer nodes

Main idea:

        The written test solution uses a hash table to store nodes, that is, the key represents the original node, and the value represents the copied node;

        After the storage is completed, traverse the node and set the next pointer and value pointer of the copied node;

#include <iostream>
#include <unordered_map>

class Node {
public:
    int val;
    Node* next;
    Node* random;
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};

class Solution {
public:
    Node* copyRandomList(Node* head) {
        std::unordered_map<Node*, Node*> hash;
        Node *tmp = head;
        while(tmp != NULL){
            hash[tmp] = new Node(tmp->val);
            tmp = tmp->next;
        }
        tmp = head;
        while(tmp != NULL){
            hash[tmp]->next = hash[tmp->next];
            hash[tmp]->random = hash[tmp->random];
            tmp = tmp->next;
        }
        return hash[head];
    }
};

int main(int argc, char *argv[]){
    Node* Node1 = new Node(7);
    Node* Node2 = new Node(13);
    Node* Node3 = new Node(11);
    Node* Node4 = new Node(10);
    Node* Node5 = new Node(1);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Node1->random = NULL;
    Node2->random = Node1;
    Node3->random = Node5;
    Node4->random = Node3;
    Node4->random = Node1;

    Solution S1;
    Node* res = S1.copyRandomList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }
    return 0;
}

Main idea:

        Interview solution, optimize the space complexity to O(1);

        Jang Yuan Table Ori_Node1 → Ori_Node2 → Ori_Node3 Structure Ori_Node1 → New_Node1 → Ori_Node2 → New_Node2 → Ori_Node3 → New_Node3;

        Then traverse the linked list one by one to build a random pointer;

        Finally, separate the old and new linked lists and build the next pointer;

#include <iostream>

class Node {
public:
    int val;
    Node* next;
    Node* random;
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if (head == NULL) return NULL;
        Node* tmp = head;
        Node* next = NULL;
        while(tmp != NULL){
            next = tmp->next; // 原链表下一个结点
            tmp->next = new Node(tmp->val); // 创建新结点
            tmp->next->next = next; // 新结点指向原链表地下一个结点
            tmp = next; // 更新tmp
        }

        tmp = head; // 遍历构建random指针
        while(tmp != NULL){
            next= tmp->next->next; // 一对一对遍历
            tmp->next->random = tmp->random != NULL ? tmp->random->next : NULL;
            tmp = next;
        }

        // 分离链表并构建next指针
        tmp = head;
        Node *res = head->next;
        Node *copy;
        while(tmp != NULL){
            copy = tmp->next;
            next = tmp->next->next; // 一对一对分离
            tmp->next= next;
            copy->next = next != NULL ? next->next : NULL;
            tmp = next;
        }
        return res;
    }
};

int main(int argc, char *argv[]){
    Node* Node1 = new Node(7);
    Node* Node2 = new Node(13);
    Node* Node3 = new Node(11);
    Node* Node4 = new Node(10);
    Node* Node5 = new Node(1);

    Node1->next = Node2;
    Node2->next = Node3;
    Node3->next = Node4;
    Node4->next = Node5;

    Node1->random = NULL;
    Node2->random = Node1;
    Node3->random = Node5;
    Node4->random = Node3;
    Node4->random = Node1;

    Solution S1;
    Node* res = S1.copyRandomList(Node1);
    while(res != NULL){
        std::cout << res->val << " ";
        res = res->next;
    }
    return 0;
}

8--Two singly linked list intersection problem

Guess you like

Origin blog.csdn.net/weixin_43863869/article/details/132198202