首页 > 其他分享 >链表

链表

时间:2024-11-08 23:18:52浏览次数:4  
标签:head ListNode temp val int next 链表

链表部分

  1. (链表)707. 设计链表(模版,通过了valgrind测试)

实现单向链表,即每个节点仅存储本身的值和后继节点。除此之外,我们还需要一个哨兵(sentinel)节点作为头节点,和一个 size 参数保存有效节点数。如下图所示。

初始化时,只需创建头节点 head 和 size 即可。
实现 get(index) 时,先判断有效性,再通过循环来找到对应的节点的值。如下图所示。

实现 addAtIndex(index, val) 时,如果 index 是有效值,则需要找到原来下标为 index 的节点的前驱节点 pred,并创建新节点 to_add,将to_add 的后继节点设为 pred 的后继节点,将 pred 的后继节点更新为 to_add,这样就将 to_add 插入到了链表中。最后需要更新 size。这样的操作对于 index=0 也成立,如以下两张图所示。

实现 addAtIndex(index, val)时,可以借助 addAtHead(val) 和 addAtTail(val) 来实现。

实现 deleteAtIndex(index),先判断参数有效性。然后找到下标为 index 的节点的前驱节点 pred,通过将 pred 的后继节点更新为 pred 的后继节点的后继节点,来达到删除节点的效果。同时也要更新 size。如下图所示。

题解:

struct Node
{
    int val;
    struct Node *next;
};

typedef struct LinkedList{
    struct Node *head;
    struct Node *tail;
    int size;
} MyLinkedList;

MyLinkedList* myLinkedListCreate() 
{
    MyLinkedList* obj=(struct LinkedList*)malloc(sizeof(struct LinkedList));
    obj->head=NULL;
    obj->tail=NULL;
    obj->size=0;
    return obj;
}

struct Node *createNode(int val)
{
    struct Node *node;
    node=(struct Node*) malloc(sizeof(struct LinkedList));
    node->val=val;
    node->next=NULL;
    return node;
}

int myLinkedListGet(MyLinkedList* obj, int index) 
{
    if(index > (obj->size)-1 || index < 0) return -1;
    struct Node *temp=obj->head;
    for(int i=0;i<index;i++)
    {
        temp=temp->next;
    }
    return temp->val;
}

void myLinkedListAddAtHead(MyLinkedList* obj, int val) 
{
    if(obj->head==NULL)
    {
        obj->head=createNode(val);
        obj->tail=obj->head;
    }
    else
    {
        struct Node *temp=createNode(val);
        temp->next=obj->head;
        obj->head=temp;
    }
    obj->size++;
}

void myLinkedListAddAtTail(MyLinkedList* obj, int val) 
{
    if(obj->head==NULL)
    {
        obj->head=createNode(val);
        obj->tail=obj->head;
    }
    else
    {
        struct Node *temp=createNode(val);
        obj->tail->next=temp;
        obj->tail=temp;
    }
    obj->size++;
}

void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) 
{
    if(index > (obj->size) || index < 0) return;
    else if(index == obj->size)
    {
        myLinkedListAddAtTail(obj,val);
        return;
    }
    else if(index == 0)
    {
        myLinkedListAddAtHead(obj,val);
        return;
    }
    struct Node *temp=obj->head;
    struct Node *temp_pre;
    struct Node *addNode=createNode(val);
    for(int i=0;i<index;i++)
    {
        temp_pre=temp;
        temp=temp->next;
    }
    temp_pre->next=addNode;
    addNode->next=temp;
    obj->size++;
}

void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) 
{
    if(index > (obj->size)-1 || index < 0) return;
    
    if(index == 0)
    {
        struct Node *temp=obj->head;
        obj->head=temp->next;
        free(temp);
        temp=NULL;
        obj->size--;
    }
    else
    {
        struct Node *temp=obj->head;
        struct Node *temp_pre;
        for(int i=0;i<index;i++)
        {
            temp_pre=temp;
            temp=temp->next;
        }
        if(index == (obj->size)-1)
        {
            obj->tail=temp_pre;
            free(temp);
            temp=NULL;
            obj->size--;
        }
        else
        {
            temp_pre->next=temp->next;
            free(temp);
            temp=NULL;
            obj->size--;
        }
    }
}

void myLinkedListFree(MyLinkedList* obj) 
{
    if(obj->size == 0)
    {
        obj->head=NULL;
        obj->tail=NULL;
        return;
    }
    else
    {
        struct Node *temp=obj->head;
        struct Node *temp_pre;
        for(int i=0;i<obj->size;i++)
        {
            temp_pre=temp;
            temp=temp->next;
            free(temp_pre);
            temp_pre=NULL;
        }
        obj->size=0;
        obj->head=NULL;
        obj->tail=NULL;
    }
    
}
  1. (链表)203. 移除链表元素
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode *tmp,*tmp_pre;
    if(!head) return NULL;
    while(head && head->val==val)
    {
        tmp=head;
        head=tmp->next;
        tmp_pre=tmp;
        tmp=tmp->next;
        free(tmp_pre);
        tmp_pre=NULL;
    }
    if(head==NULL) return NULL;
    tmp=head->next;
    tmp_pre=head;
    while(tmp!=NULL)
    {
        if(tmp->val==val)
        {
            tmp_pre->next=tmp->next;
            free(tmp);
            tmp=NULL;
            tmp=tmp_pre->next;
        }
        else
        {
            tmp_pre=tmp;
            tmp=tmp->next;
        }
    }
    return head;
}
  1. (链表)237. 删除链表中的节点

直接将下一个节点的值覆盖要删去的该节点的值。然后直接将该节点的next连接到原来next的下一个节点即可。(脑筋急转弯啊)

void deleteNode(struct ListNode* node) {
    node->val=node->next->val;
    node->next=node->next->next;
}
  1. (链表,双指针)19. 删除链表的倒数第 N 个结点

我们也可以在不预处理出链表的长度,以及使用常数空间的前提下解决本题。

由于我们需要找到倒数第 n 个节点,因此我们可以使用两个指针 first 和 second 同时对链表进行遍历,并且 first 比 second 超前 n 个节点。当 first 遍历到链表的末尾时,second 就恰好处于倒数第 n 个节点。

具体地,初始时 first 和 second 均指向头节点。我们首先使用 first 对链表进行遍历,遍历的次数为 n。此时,first 和 second 之间间隔了 n−1 个节点,即 first 比 second 超前了 n 个节点。

在这之后,我们同时使用 first 和 second 对链表进行遍历。当 first 遍历到链表的末尾(即 first 为空指针)时,second 恰好指向倒数第 n 个节点。

根据方法一和方法二,如果我们能够得到的是倒数第 n 个节点的前驱节点而不是倒数第 n 个节点的话,删除操作会更加方便。因此我们可以考虑在初始时将 second 指向哑节点,其余的操作步骤不变。这样一来,当 first 遍历到链表的末尾时,second 的下一个节点就是我们需要删除的节点。

题解:

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    // Fast slow pointer.
    struct ListNode *fast,*slow,*pre;
    slow=(struct ListNode*)malloc(sizeof(struct ListNode));
    slow->val=0;
    slow->next=head;
    pre=slow;
    fast=head;
    for(int i=0;i<n;i++)
        fast=fast->next;
    while(fast)
    {
        fast=fast->next;
        slow=slow->next;
    }
    fast=slow->next;
    slow->next=fast->next;
    free(fast);
    head=pre->next;
    return head;
}
  1. (链表)83. 删除排序链表中的重复元素
struct ListNode* deleteDuplicates(struct ListNode* head) {
    struct ListNode *pre,*now;
    pre=(struct ListNode*)malloc(sizeof(struct ListNode));
    pre->next=head;
    now=head;
    int temp_pre=-1000,temp;
    while(now)
    {
        temp=now->val;
        if(temp==temp_pre)
        {
            pre->next=now->next;
            free(now);
            now=pre->next;
        }
        else
        {
            now=now->next;
            pre=pre->next;
            temp_pre=temp;
        }
    }
    return head;
}
  1. (链表,dfs)430. 扁平化多级双向链表

当我们遍历到某个节点 node 时,如果它的 child 成员不为空,那么我们需要将 child 指向的链表结构进行扁平化,并且插入 node 与 node 的下一个节点之间。

因此,我们在遇到 child 成员不为空的节点时,就要先去处理 child 指向的链表结构,这就是一个「深度优先搜索」的过程。当我们完成了对 child 指向的链表结构的扁平化之后,就可以「回溯」到 node 节点。

为了能够将扁平化的链表插入 node 与 node 的下一个节点之间,我们需要知道扁平化的链表的最后一个节点 last,随后进行如下的三步操作:

$\bullet$ 将 node 与 node 的下一个节点 next 断开:
$\bullet$ 将 node 与 child 相连;
$\bullet$ 将 last 与 next 相连。

这样一来,我们就可以将扁平化的链表成功地插入。

class Solution {
public:
    Node* iter_link(Node* head)
    {
        Node *temp=head;
        Node *temp_next=nullptr; // 原先的next链
        Node *temp_next_prev=nullptr; // next链新的前向链
        if(!temp) return nullptr;
        while(temp->next || temp->child)    // 当next或child不为空
        {
            if(temp->child) // 先遍历孩子节点
            {
                temp_next=temp->next; // 记录下原先的next链
                temp->next=temp->child; // 将child链连接到next链,并且连接前向链
                temp->next->prev=temp;
                temp_next_prev=iter_link(temp->child);// 深度优先搜索直到该child链条结束
                temp->child=nullptr;
            }
            if(temp_next_prev)
            {
                temp_next_prev->next=temp_next;
                if(temp_next)   // 可能没有后继链条
                    temp_next->prev=temp_next_prev; // 将后继链条接到child链条末尾
                temp_next_prev=nullptr; // 清空。
            }
            if(temp->next)
                temp=temp->next;
        }
        return temp;
    }

    Node* flatten(Node* head)
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        Node *temp=head;
        iter_link(temp);
        return temp;
    }

};
  1. (二叉树遍历)114. 二叉树展开为链表

思路

我们在前序遍历过程中,按照root->leftSubTree->rightSubTree进行遍历。因此我们可以按照如下的想法,进行递归原地构造:

  1. 观察是否有左右子树。如果没有,return。
  2. 有左子树,则将右子树记录下来,同时将左子树连接到right节点上,并进入到新的右子树根节点。
  3. 持续处理直到所有的左子树均被处理完毕。
  4. 有右子树,则进入到右子树的根节点中,并重复1,2,3条。
  5. 返回已经处理的树。

题解

void flatten(struct TreeNode* root) {
    if(!root) return;
    else if(!root->left && !root->right) return;

    struct TreeNode* rightSubTree;// 记录右子树的末尾。
    struct TreeNode* temp=NULL;
    if(root->left) // 处理左子树。
    {
        temp=root->right;
        root->right=root->left;
        root->left=NULL;
    }
    if(root->right)
    {
        rightSubTree=root->right;
        flatten(rightSubTree);
    }
    // 将temp接到右子树后面。保证temp仅有右子树。
    flatten(temp);
    while(rightSubTree->right)
    {
        rightSubTree=rightSubTree->right;
    }
    rightSubTree->right=temp;
    while(rightSubTree->right)
    {
        rightSubTree=rightSubTree->right;
    }
}

思路2

注意到前序遍历访问各节点的顺序是根节点、左子树、右子树。如果一个节点的左子节点为空,则该节点不需要进行展开操作。如果一个节点的左子节点不为空,则该节点的左子树中的最后一个节点被访问之后,该节点的右子节点被访问。该节点的左子树中最后一个被访问的节点是左子树中的最右边的节点,也是该节点的前驱节点。因此,问题转化成寻找当前节点的前驱节点。

具体做法是,对于当前节点,如果其左子节点不为空,则在其左子树中找到最右边的节点,作为前驱节点,将当前节点的右子节点赋给前驱节点的右子节点,然后将当前节点的左子节点赋给当前节点的右子节点,并将当前节点的左子节点设为空。对当前节点处理结束后,继续处理链表中的下一个节点,直到所有节点都处理结束。

void flatten(struct TreeNode* root) {
    if(!root) return;
    while(root)
    {
        while(root->left)
        {
            struct TreeNode* leftSubTree=root->left;
            struct TreeNode* leftRight=leftSubTree; // 记录左子树的最右边节点。
            while(leftRight->right)
            {
                leftRight=leftRight->right;
            }
            leftRight->right=root->right;
            root->right=root->left;
            root->left=NULL;
        }
        root=root->right;
    }
}

过程如下图所示

    1
   / \
  2   5
 / \   \
3   4   6

//将 1 的左子树插入到右子树的地方
    1
     \
      2         5
     / \         \
    3   4         6        
//将原来的右子树接到左子树的最右边节点
    1
     \
      2          
     / \          
    3   4  
         \
          5
           \
            6
            
 //将 2 的左子树插入到右子树的地方
    1
     \
      2          
       \          
        3       4  
                 \
                  5
                   \
                    6   
        
 //将原来的右子树接到左子树的最右边节点
    1
     \
      2          
       \          
        3      
         \
          4  
           \
            5
             \
              6         
  
  1. (快慢指针、链表)61.旋转链表

思路

快慢指针处理新的head和tail就可以了。

题解

/**
 * Definition for singly-linked list.
 * 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:
    #pragma g++ optimize(2)
    ListNode* rotateRight(ListNode* head, int k) {
        // fast slow pt.
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        if(!head) return nullptr;
        if(!head->next || k==0) return head;
        ListNode *new_head,*fast,*slow,*temp;
        slow=head,fast=head,temp=head;
        int size=0;
        while(temp)
        {
            temp=temp->next;
            size++;
        }
        int real_k=k%size;
        if(real_k==0) return head;
        for(int i=0;i<real_k;i++)
            fast=fast->next;
        while(fast->next)
        {
            fast=fast->next;
            slow=slow->next;
        }
        new_head=slow->next;
        fast->next=head;
        slow->next=nullptr;
        return new_head;
    }
};
  1. 24.两两交换链表中的节点

思路

双指针即可。只需要交换值。而且注意非法访问问题。

题解

/**
 * Definition for singly-linked list.
 * 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* swapPairs(ListNode* head) {
        ListNode *l1,*l2;
        int temp;
        l1=head;
        if(head)
            l2=head->next;
        else
            l2=nullptr;
        while(l1 && l2)
        {
            temp=l2->val;
            l2->val=l1->val;
            l1->val=temp;
            l1=l2->next;
            if(l1)
                l2=l1->next;
            else
                l2=nullptr;
        }
        return head;
    }
};
  1. 206.反转链表(三指针模版)

思路

三指针即可原地反转。

/**
 * Definition for singly-linked list.
 * 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) {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        if(!head || !head->next) return head;
        // 原地交换。
        ListNode *pre,*cur,*nxt;
        pre=head;
        cur=head->next;
        head->next=nullptr;
        nxt=cur->next;
        while(nxt)
        {
            cur->next=pre;
            pre=cur;
            cur=nxt;
            nxt=cur->next;
        }
        cur->next=pre;
        head=cur;
        return head;
    }
};
  1. 92.反转链表II

思路

穿针引线。需要注意边界处理即可。

题解

/**
 * Definition for singly-linked list.
 * 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) {
        if(!head->next) return head;
        if(left==right) return head;

        ListNode *leftNode,*rightNode,*left_pre;
        leftNode=head,rightNode=head,left_pre=nullptr;
        // Scan whole chain!
        for(int i=0;i<left-1;i++)
        {
            left_pre=leftNode;
            leftNode=leftNode->next;
            rightNode=rightNode->next;
        }
        for(int i=left-1;i<right-1;i++)
            rightNode=rightNode->next;
        // Then modify them.
        // Reverse needed;
        ListNode *pre,*cur,*next;
        pre=leftNode;
        cur=pre->next;
        next=cur->next;
        // reverse.
        while(next!=rightNode->next)
        {
            cur->next=pre;
            pre=cur;
            cur=next;
            next=next->next;
        }
        cur->next=pre;
        // boundary.
        if(left_pre)
            left_pre->next=rightNode;
        else
            head=rightNode;
        leftNode->next=next;
        return head;
    }
};
  1. 25. K 个一组翻转链表

思路

模拟即可。

/**
 * Definition for singly-linked list.
 * 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* reverseKGroup(ListNode* head, int k) {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        if(!head || k==1) return head;
        else if(!head->next) return head;
        ListNode *left,*right,*original_head;
        left=head;right=head;original_head=head;
        int flag=0;
        while(right)
        {
            for(int i=0;i<k-1;i++)
            {
                right=right->next;
                if(!right) break;
            }
            if (!right) break;
            // Reverse.
            ListNode *pre,*cur,*next;
            pre=left;
            cur=pre->next;
            next=cur->next;
            while(next!=right->next)
            {
                cur->next=pre;
                pre=cur;
                cur=next;
                next=next->next;
            }
            cur->next=pre;
            if(!flag)
            {
                head->next=next;
                head=right;
                flag=1;
            }
            else
            {
                original_head->next=cur;
                original_head=left;
            }
            left->next=next;
            // pin to next.
            left=next;
            right=next;
        }
        return head;
    }
};
  1. 2. 两数相加
/**
 * Definition for singly-linked list.
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        ListNode *head=new ListNode;
        ListNode *temp=head;
        ListNode *temp_pre;
        int val;
        int carrier=0;
        while(l1 || l2)
        {
            val=0;
            temp_pre=temp;
            temp->next=new ListNode;
            if(l1)
            {
                val+=l1->val;
                l1=l1->next;
            }
            if(l2)
            {
                val+=l2->val;
                l2=l2->next;
            }
            temp->val=(val+carrier)%10;
            carrier=(val+carrier)/10;
            temp=temp->next;
        }
        if(carrier)
            temp->val=carrier;
        else
            temp_pre->next=nullptr;
        return head;
    }
};
  1. 445. 两数相加II

思路

反转链表后,思路同2.两数相加。再将结果反转即可。

题解

/**
 * Definition for singly-linked list.
 * 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* reverseLists(ListNode* head)
    {
        if(!head->next) return head;
        ListNode *pre,*cur,*next;
        pre=head;
        cur=pre->next;
        next=cur->next;
        head->next=nullptr;
        while(next)
        {
            cur->next=pre;
            pre=cur;
            cur=next;
            next=next->next;
        }
        cur->next=pre;
        head=cur;
        return head;
    }
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        l1=reverseLists(l1);
        l2=reverseLists(l2);
        
        ListNode *l3=new ListNode;
        ListNode *temp=l3,*temp_pre;
        int val;
        int carrier=0;
        while(l1 || l2)
        {
            val=0;
            temp_pre=temp;
            temp->next=new ListNode;
            if(l1)
            {
                val+=l1->val;
                l1=l1->next;
            }
            if(l2)
            {
                val+=l2->val;
                l2=l2->next;
            }
            temp->val=(val+carrier)%10;
            carrier=(val+carrier)/10;
            temp=temp->next;
        }
        if(carrier)
            temp->val=carrier;
        else
        {
            delete temp;
            temp_pre->next=nullptr;
        }
        l3=reverseLists(l3);
        return l3;
    }
};
  1. 21. 合并两个有序链表

题解1

/**
 * Definition for singly-linked list.
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(!list1 && !list2) return nullptr;
        if(!list1 && list2) return list2;
        if(!list2 && list1) return list1;
        ListNode *head=new ListNode,*temp=head,*temp_pre=new ListNode;
        temp_pre->next=head;
        while(list1 && list2)
        {
            temp->next=new ListNode;
            if(list1->val<list2->val)
            {
                temp->val=list1->val;
                list1=list1->next;
                temp=temp->next;
                temp_pre=temp_pre->next;
            }
            else if(list1->val>list2->val)
            {
                temp->val=list2->val;
                list2=list2->next;
                temp=temp->next;
                temp_pre=temp_pre->next;
            }
            else
            {
                temp->val=list1->val;
                temp=temp->next;
                temp_pre=temp_pre->next;
                list1=list1->next;
                
                temp->next=new ListNode;
                temp->val=list2->val;
                temp=temp->next;
                temp_pre=temp_pre->next;
                list2=list2->next;
            }
        }
        if(list1)
            temp_pre->next=list1;
        else if(list2)
            temp_pre->next=list2;
        else
        {
            delete temp_pre->next;
            temp_pre->next=nullptr;
        }
        return head;
    }
};

思路二

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

prehead->1->4->5->null

         1->2->3->6->null
			 
prehead->1  4->5->null
         |
         1->2->3->6->null

			 
prehead->1     4->5->null
         |     |
         1->2->3  6->null

			 
prehead->1     4->5
         |     |  |
         1->2->3  6->null
/**
 * Definition for singly-linked list.
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(!list1 && !list2) return nullptr;
        if(!list1 && list2) return list2;
        if(!list2 && list1) return list1;
        ListNode *prehead=new ListNode;
        ListNode *prev=prehead;
        while(list1 && list2)
        {
            if(list1->val<=list2->val)
            {
                prev->next=list1;
                list1=list1->next;
                prev=prev->next;
            }
            else
            {
                 prev->next=list2;
                list2=list2->next;
                prev=prev->next;
            }
        }
        prev->next=list1==nullptr?list2:list1;
        return prehead->next;
    }
};
  1. 23. 合并K个有序链表(分治法)

思路

分治法+合并两个链表。

题解

/**
 * Definition for singly-linked list.
 * 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* merge2Lists(ListNode *l1, ListNode *l2)
    {
        if(!l1 && !l2) return nullptr;
        if(l1 && !l2) return l1;
        if(!l1 && l2) return l2;
        ListNode *prehead=new ListNode;
        ListNode *prev=prehead;
        while(l1 && l2)
        {
            if(l1->val < l2->val)
            {
                prev->next=l1;
                prev=prev->next;
                l1=l1->next;
            }
            else
            {
                prev->next=l2;
                prev=prev->next;
                l2=l2->next;
            }
        }
        prev->next=l1==nullptr?l2:l1;
        return prehead->next;
    }
    ListNode* merge(vector<ListNode*>& lists,int left,int right)
    {
        if(left==right) return lists[left];
        if(left > right) return nullptr;
        int mid=(right+left) >> 1;
        return merge2Lists(merge(lists,left,mid),merge(lists,mid+1,right));
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        int n=lists.size();
        ListNode *head=merge(lists,0,n-1);
        return head;
    }
};
  1. 86.分隔链表

思路

开两个链表,按原链表顺序,一个记录小于pivot链表的数值,一个记录大于等于pivot链表的数值,并将其相加。

题解

/**
 * Definition for singly-linked list.
 * 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* partition(ListNode* head, int x) {
        ListNode *small=new ListNode;
        ListNode *smallHead=small;
        ListNode *big=new ListNode;
        ListNode *bigHead=big;
        while(head)
        {
            if(head->val < x)
            {
                small->next=head;
                small=small->next;
            }
            else
            {
                big->next=head;
                big=big->next;
            }
            head=head->next;
        }
        big->next=nullptr;
        small->next=bigHead->next;
        return smallHead->next;
    }
    
};
  1. 141.环形链表

思路1 哈希表(待补充)

思路2 快慢指针

具体地,我们定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

细节

为什么我们要规定初始时慢指针在位置 head,快指针在位置 head.next,而不是两个指针都在位置 head?
$\bullet$ 采用while循环时,先行判断条件。因此开头重合时,就不会进入循环。
$\bullet$ 可以采用do-while循环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(!head || !head->next) return false;
        ListNode *slow=head,*fast=head->next;
        while(slow!=fast)
        {
            if(!slow->next) return false;
            if(!fast->next || !fast->next->next) return false;
            slow=slow->next;
            fast=fast->next->next;
        }
        return true;
    }
};
  1. 142.环形链表II

思路1 哈希表

思路2 快慢指针

  1. 将快慢指针指在head表头;
  2. 假设链表入表长度为a。进入链表后,在b处相遇。此时,我们很明显知道,快指针已经走过了$(a+b)$的距离,而快指针走过了$(a+n(b+c)+b)$的距离。
  3. 为什么慢指针第一圈内就会与快指针相遇?设环周长$x$。慢指针速度为$v$。快指针速度则为$2v$。因此最多只需要$t=\dfrac{x}{v}$就会追上(追击距离最远为$x$)。因此一圈内必然能够相遇。
  4. 这样我们就有:$(n-1)(b+c)+c=a$。也就是相当于慢指针从相遇点到入环点的所需时间和慢指针从表头到入环点的时间相同。因此只需要在增加一个新的指针即可。

题解

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(!head || !head->next) return nullptr;
        ListNode *slow=head,*fast=head;
        while(1)
        {
            if(!slow->next)
                return nullptr;
            if(!fast->next || !fast->next->next)
                return nullptr;
            slow=slow->next;
            fast=fast->next->next;
            if(fast==slow)
            {
                ListNode *ptr=head;
                while(slow!=ptr)
                {
                    slow=slow->next;
                    ptr=ptr->next;
                }
                return ptr;
            }
        }
    }
};
  1. 876.链表的中间节点

快慢指针。

/**
 * Definition for singly-linked list.
 * 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* middleNode(ListNode* head) {
        ListNode *slow=head,*fast=head;
        while(fast && fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        return slow;
    }
};
  1. 143.重排链表(408真题,要求O(1)空间复杂度)

思路1

采用线性表进行重排。一定要注意在前后指针相遇后,需要将最后的表尾部指向nullptr。

/**
 * Definition for singly-linked list.
 * 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:
    void reorderList(ListNode* head) {
        if(!head) return;
        vector<ListNode *> linear;
        ListNode *node=head;
        while(node)
        {
            linear.emplace_back(node);
            node=node->next;
        }
        int i=0,j=linear.size()-1;
        while(i!=j)
        {
            linear[i]->next=linear[j];
            i++;
            if(i==j) break;
            linear[j]->next=linear[i];
            j--;
        }
        linear[i]->next=nullptr;
    }
};
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
void reorderList(struct ListNode* head) {
    if(!head) return;
    int bufSize=512;
    struct ListNode **linear=(struct ListNode**) malloc(sizeof(struct ListNode*)*bufSize);
    int num=0;
    struct ListNode *read=head;
    while(read)
    {
        
        linear[num]=read;
        read=read->next;
        num++;
        if(num>=bufSize)
        {
            bufSize*=2;
            linear=(struct ListNode**) realloc(linear,sizeof(struct ListNode*)*bufSize);
        }
    }
    int i=0,j=num-1;
    while(i<j)
    {
        linear[i]->next=linear[j];
        i++;
        if(i==j) break;
        linear[j]->next=linear[i];
        j--;
    }
    linear[i]->next=NULL;
}

思路2

2019年408统考真题第41题。
采用O(1)的空间复杂度。

  1. 寻找链表的中心节点。(快慢指针)
  2. 将链表的后半部分反转。(三指针)
  3. 在依顺序合并链表。(双指针)
1->2->3->4->5
1->2->3  4->5
1->2->3  5->4
1->5->2->4->3
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* find_mid(struct ListNode *head)
{
    struct ListNode *slow=head,*fast=head;
    while(fast->next && fast->next->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}
struct ListNode* reverse_link(struct ListNode* head)
{
    if(!head || !head->next) return head;
    struct ListNode *pre=head,*cur=pre->next,*next=cur->next;
    head->next=NULL;
    while(next)
    {
        cur->next=pre;
        pre=cur;
        cur=next;
        next=next->next;
    }
    cur->next=pre;
    return cur;
}
void merge_link(struct ListNode *l1,struct ListNode *l2)
{
    struct ListNode *temp1,*temp2;
    while(l1 && l2)
    {
        temp1=l1->next;
        temp2=l2->next;

        l1->next=l2;
        l1=temp1;

        l2->next=l1;
        l2=temp2;
    }
}
void reorderList(struct ListNode* head) {
    if(!head) return;
    struct ListNode *mid=find_mid(head); // return the middle point of the list.
    struct ListNode *new_head=reverse_link(mid->next);
    mid->next=NULL;

    struct ListNode *l1=head,*l2=new_head;
    merge_link(l1,l2);
}
  1. 160.相交链表

思路:快慢指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(!headA || !headB) return NULL;
    struct ListNode *first=headA,*second=headB;
    while(first!=second)
    {
        if(!first) first=headB;
        else first=first->next;
        if(!second) second=headA;
        else second=second->next;
    }
    return first;
}

主要知识点:建立链表方法(头插法,尾插法),链表的插入,链表的删除,链表的遍历和寻找。
技巧:
$\bullet$ 双指针(链表中心节点,判断是否成环并返回入环节点,判断是否有公共节点,重排链表)
$\bullet$ 分治法(合并多个有序链表)
$\bullet$ 设置空节点于头节点前(反转链表,两两交换节点)。

标签:head,ListNode,temp,val,int,next,链表
From: https://www.cnblogs.com/mumujun12345/p/18536114

相关文章

  • C语言双向链表
    一、优势 与单链表对比,双向链表的增、删、改无需遍历多次以查找目标节点前一节点与后一节点,效率提高,代码对比*单链表:1.插入:voidinsert(node*head,charName,intphonenumber,inttarget){ node*p=(node*)malloc(sizeof(node));//为新节点分配内存; p->next=sea......
  • 数据结构_链表_双向循环链表 & 栈 的初始化、插入、删除、修改、查询打印(基于C语言实
    一、双向循环链表的原理与应用双向循环链表与双向链表的区别:指的是双向循环链表的首结点中的prev指针成员指向链表的尾结点,并且双向循环链表的尾结点里的next指针成员指向链表的首结点,所以双向循环链表也属于环形结构。由于带头结点更加方便用户进行数据访问,所以本次创建一条带......
  • 数据结构_链表_单向循环链表 & 双向链表的初始化、插入、删除、修改、查询打印(基于C语
    一、单向循环链表的原理与应用思考:对于单向链表而言,想要遍历链表,则必须从链表的首结点开始进行遍历,请问有没有更简单的方案实现链表中的数据的增删改查?回答:是有的,可以使用单向循环的链表进行设计,单向循环的链表的使用规则和普通的单向链表没有较大的区别,需要注意:单向循环链表的......
  • 【LeetCode】返回链表的中间结点、删除链表的倒数第 N 个结点
    主页:HABUO......
  • 链表的插入排序
    #include<stdio.h>#include<stdlib.h>//定义链表节点结构typedefstructNode{intdata;structNode*next;}Node;//创建新节点Node*createNode(intdata){Node*newNode=(Node*)malloc(sizeof(Node));newNode->data=data;newN......
  • 链表(C 语言)
    目录一、链表的概念1.链表的结构2.链表的分类3.链表的优势二、链表的实现1.无头单项非循环链表的实现1.1代码说明2.带头双向循环链表的实现2.1代码说明三、链表和顺序表的区别四、链表总结一、链表的概念链表是一种顺序表,它由一个一个的节点组成,该节点中......
  • 【C语言】实战-力扣题库:回文链表
    题目描述给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。提示:链表中节点数目在范围[1,105] 内0<=Node.val<=9进阶:你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?问题分析O(1)的时间复杂度---跟n......
  • lanqiaoOJ 1110:小王子单链表 ← 数组模拟实现
     【题目来源】https://www.lanqiao.cn/problems/1110/learning/【题目描述】小王子有一天迷上了排队的游戏,桌子上有标号为1-10的10个玩具,现在小王子将他们排成一列,可小王子还是太小了,他不确定他到底想把哪个玩具摆在哪里,直到最后才能排成一条直线,求玩具的编号。已知他排......
  • Python——数据结构与算法-时间复杂度&空间复杂度-链表&树状结构
    1.数据结构和算法简介程序可以理解为:程序=数据结构+算法概述/目的:都可以提高程序的效率(性能)数据结构指的是存储,组织数据的方式.算法指的是为了解决实际业务问题而思考思路和方法,就叫:算法.2.算法的5大特性介绍概述:为了解决实际业务问题,......
  • 【LeetCode】移除链表中等于设定值的元素、反转链表
    主页:HABUO......