11道链表oj题

简介: 11道链表oj题

反转链表

解题思路

指针反转法

这里我们直接把节点的指针进行反转就行,反转的时候要注意保存好下一个节点的地址和上一个节点的地址。

n1表示当前节点的上一个节点的地址

n2表示当前节点

n3表示当期节点的下一个节点的地址

节点插入法

我们创建新的头节点的地址,原链表中的每个节点一个一个进行头插

代码

指针反转法

struct ListNode* reverseList(struct ListNode* head){
    if(head==NULL)
    return head;
    struct ListNode* n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=head->next;
    while(n3)
    {
        n2->next=n1;

        n1=n2;
        n2=n3;
        n3=n2->next;
    }
    n2->next=n1;
    return n2;
}

节点插入法

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* newhead=NULL;
    
    while(head)
    {
        struct ListNode* cur=head;
        head=head->next;
        
        cur->next=newhead;
        newhead=cur;
       
    }
    return newhead;
}

链表的中间结点

解题思路

快慢指针法,慢指针走一步,快指针走两步。当快指针指向为空的时候。慢指针指向的节点即为中间的节点。

代码

struct ListNode* middleNode(struct ListNode* head){
    struct ListNode *low,*quick;
    low=head;
    quick=head;
    while(quick->next)
    {
        low=low->next;
        quick=quick->next;
        if(quick==NULL)
        break;
        quick=quick->next;
         if(quick==NULL)
        break;
    }
    return  low;
}

移除链表元素

解题思路

方法1:遍历链表,遇到val,则跳过该节点。

代码

方法1:

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode* cur=head;
    struct ListNode* curpro=head;
     while(head&&head->val==val)
        {
            head=head->next;
            free(cur); 
            cur=head;
        }
    while(cur)
    {
       
        if(cur->val==val)
        {
            curpro->next=cur->next;
            free(cur);
            cur=curpro->next;
        }
       else{
           curpro=cur;
        cur=curpro->next;
       }
        
    }
    return head;
}

链表中倒数第k个结点

解题思路

快慢指针法,先让快指针走k步,然后快慢指针一起走。直到快指针为空的时候,慢指针指向的节点就是倒数第k个节点。

注意k的值大于节点个数的时候,返回的为空。


代码

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode *slow,*quick;
    slow=NULL;
    quick=pListHead;
    int i=0;
    while(i<k&&quick)
    {
        quick=quick->next;
        i++;
    }
    if(i==k)
    {
        slow=pListHead;
        while(quick)
    {
        slow=slow->next;
        quick=quick->next;
    }
    }
    return slow;
}

合并两个有序链表

解题思路

开辟一个头节点用于当中新的链表,里面不存有效值,对应给的两个链表,从首个节点开始进行val值的比较,小的那个值的节点插入到新的节点的后面,一直到某个链表节点的结尾。把剩下一个链表的全部插入到新链表的后面。最后不要忘记释放头节点。

代码

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* newhead=(struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* cur=newhead;
    while(list1&&list2)
    {
        if(list1->val<list2->val)
        {
            cur->next=list1;
            list1=list1->next;
            cur=cur->next;
        }
        else
        {
            cur->next=list2;
            list2=list2->next;
            cur=cur->next;
        }
    }
    if(list1==NULL)
    {
        cur->next=list2;
    }
    else{
        cur->next=list1;
    }
    //销毁
    cur=newhead->next;
    free(newhead);
    newhead=NULL;
    return cur;
}

链表分割

题目的意思是这样的,小于x的节点在前面,大于等于x的节点排在后面,其相对位置不变。看下面的更能更好的理解。

解题思路

我们把原链表分成2个链表,第一个链表为小于x的链表,第二个链表为大于等于x的链表。最后第二个链表连接到第一个链表的后面。返回第一个链表的地址。

形成两个链表的过程中,遍历原链表。两个链表都是带头节点的。

代码

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode *list1=(struct ListNode *)malloc(sizeof(struct ListNode));
        struct ListNode *list2=(struct ListNode *)malloc(sizeof(struct ListNode));
        struct ListNode *head1,*head2,*tail1,*tail2;
        list1->next=NULL;
        list2->next=NULL;
        head1=tail1=list1;
        head2=tail2=list2;
        while(pHead)
        {
            if(pHead->val<x)
            {
                tail1->next=pHead;
                pHead=pHead->next;
                tail1=tail1->next;
            }
            else
            {
                tail2->next=pHead;
                pHead=pHead->next;
                tail2=tail2->next;
            }
        }
        tail2->next=NULL;
        tail1->next=head2->next;
        
        struct ListNode *head=head1->next;
        free(list1);
        free(list2);
        return head;
    }
};

另一种写法

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode *head1,*head2,*tail1,*tail2;
        head1=tail1=head2=tail2=NULL;
        while(pHead)
        {
            if(pHead->val<x)
            {
                if(tail1==NULL)
                {
                    head1=tail1=pHead;
                }
                else
                {
                tail1->next=pHead;

                tail1=tail1->next;
                }
            }
            else
            {
                 if(tail2==NULL)
                {
                    head2=tail2=pHead;
                }
                else
                {
                tail2->next=pHead;
                tail2=tail2->next;
                }
            }
        pHead=pHead->next;
        }
        if (head1 == NULL)
        return head2;
      if (head2 == NULL)
        return head1;
        
        tail2->next=NULL;
        tail1->next=head2;
        struct ListNode *head=head1;
        return head;
    }
};

链表的回文结构

解题思路

1.找到中间节点,然后把中间节点后面的节点进行逆序

2.没有逆序的部分与逆序的部分进行比较,相同即为回文,否则不是回文。

3.是回文结束的标准为节点为空。

4.恢复破坏的链表结构

代码

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode *slow,*quick;
    slow=quick=head;
    while(head&&head->next)
    {
        slow=slow->next;
        head=head->next->next;
    }
    return slow;
}

struct ListNode* reverseList(struct ListNode* head)
{
    if(head==NULL)
    return NULL;
    struct ListNode *n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=head->next;
    while(n2)
    {
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3)
        n3=n3->next;
    }
    struct ListNode* newhead=n1;
    return newhead;
}

bool isPalindrome(struct ListNode* head){
    struct ListNode* mid= middleNode(head);
    mid=reverseList(mid);
    struct ListNode* temp=mid;
    struct ListNode* cur=head;
    while(cur&&mid)
    {
        if(cur->val!=mid->val)
        {
            reverseList(temp);
            return false;
        }
        
        else
        {
            cur=cur->next;
            mid=mid->next;
        }
    }
    reverseList(temp);
    return true;
}

相交链表

解题思路

1.先求出两个链表的长度,求出它们的差值。

2.让长的链表先走差值步。

3.然后两人链表同时走,发现有地址相同的时候就返回该地址的节点。没有就返回空。

代码

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *curA=headA,*curB=headB;
    int lenA=0,lenB=0;
    while(curA)
    {
        curA=curA->next;
        lenA++;
    }

    while(curB)
    {
        curB=curB->next;
        lenB++;
    }
    struct ListNode *shorts=headA,*longs=headB;
    if(lenA>lenB)
    {
        shorts=headB;
        longs=headA;
    }
    int k=lenA>lenB?lenA-lenB:lenB-lenA;
    while(k--)
    {
        longs=longs->next;
    }
    while(shorts&&longs)
    {
        if(shorts==longs)
        return shorts;
        else
        {
            shorts=shorts->next;
            longs=longs->next;
        }
    }
    return NULL;
}

环形链表1

解题思路

本题用快慢指针进行解决,快指针走2步,慢指针走一步。当两个指针相等时即存在环。

代码

bool hasCycle(struct ListNode *head) {
    struct ListNode *slow,*quick;
    slow=quick=head;
    while(quick&&quick->next)
    {
        slow=slow->next;
        quick=quick->next->next;
        if(slow==quick)
        return true;
    }
    return false;
}

环形链表2

解题思路

思路1:

先用快慢指针找到他俩相遇的节点,然后一个从头开始走,一个从相遇点开始走,当它们相遇的时候即为环的第一个节点。

证明

假设不是环的长度为L,环的长度为C。这里L表达节点的个数。

快指针一次走两步,慢指针一次走一步,当慢指针开始进入环的时候,快指针走了L+N*C+Y(N表示整数,Y表示不足1圈的步数),慢指针走了L。

这时,慢指针和快指针开始了追击。当快,慢指针相遇的时候。假设慢指针在环中走了X步,快指针走了C-Y+X步。

慢指针:L+X

快指针:L+(N+1) * C+X

由快慢指针的关系:

2(L+X)=L+(N+1) * C+X =>L=(N+1)*C-X=> L=N * C+(C-X)

L=N * C+(C-X)

从这个表达式中可以看出来一个指针从头开始走,另一个从相遇点开始走,相遇时就是环的第一个节点。

思路2:

同样需要快慢指针,当快慢指针相遇的时候,把该节点制成空。这时题目就变成找两个链表的相交节点。注意: 最后要把破坏的链表恢复到原来的模样。

代码

思路1:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode  *slow,*quick;
    slow=quick=head;
    while(quick&&quick->next)
    {
        slow=slow->next;
        quick=quick->next->next;
        if(slow==quick)
        {
            struct ListNode* cur=head;
            while(cur!=slow)
            {
                cur=cur->next;
                slow=slow->next;
            }
             return cur;
        }
    }
    return NULL;
}

思路2:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *slow,*quick;
    int L1,L2;
    L1=L2=0;
    slow=quick=head;
    while(quick&&quick->next)
    {
        slow=slow->next;
        quick=quick->next->next;

        if(slow==quick)
        {
            struct ListNode*cur=head;
            struct ListNode* newhead=slow->next;
            slow=slow->next;
            quick->next=NULL;
            while(cur)
            {
                cur=cur->next;
                L1++;
            }
            while(slow)
            {
                slow=slow->next;
                L2++;
            }
            int divalue=L1>L2?L1-L2:L2-L1;
            struct ListNode *shorts=head,*longs=newhead;
            if(L1>L2)
            {
                shorts=newhead;
                longs=head;
            }
            while(divalue--)
            longs=longs->next;
            while(shorts!=longs)
            {
                shorts=shorts->next;
                longs=longs->next;
            }
            quick->next=newhead;
            return shorts;
        }
    }
    return NULL;
}

复制带随机指针的链表

解题思路

1.原链表每个节点后面插入一个和它一样的节点。

新的链表就是插入的节点(还没有链接在一起)

2.复制节点中random指向的节点 == 原节点中random指向的节点的后一个节点。从上图中可以看出来。

3.恢复原链表,剪除新链表。

先让原链表中的节点指向新链表节点的下一个节点,原链表节点移动一位。

然后新链表中的节点指向原链表节点的下一个节点。一次遍历就行。


代码

struct Node* copyRandomList(struct Node* head) {
  struct Node* cur=head;
    while(cur)
    {
        struct Node *newnode=(struct Node*)malloc(sizeof(struct Node));
        newnode->val=cur->val;

        //插进去
        newnode->next=cur->next;
        cur->next=newnode;

        cur=newnode->next;
    }
    //random 指向恢复
    cur=head;
    while(cur)
    {
        struct Node* curnext=cur->next;
        if(cur->random==NULL)
        curnext->random=NULL;
        else
        curnext->random=cur->random->next;

        cur=curnext->next;
    }
    //恢复原链表,新成新链表
    cur=head;
    struct Node *newhead=NULL,*newtail=NULL;
    while(cur)
    {
        if(newtail==NULL)
        newhead=newtail=cur->next;
        cur->next=newtail->next;
        cur=cur->next;
        if(cur)
        newtail->next=cur->next;

        newtail=newtail->next;
    }
    return newhead;
}
相关文章
|
1月前
数据结构——链表OJ题
数据结构——链表OJ题
|
1月前
|
存储
2023 8 -14链表OJ(上)
2023 8 -14链表OJ
39 0
|
1月前
链表OJ详解
链表OJ详解
32 0
|
2天前
链表OJ题
链表OJ题
22 8
|
1月前
|
索引
链表相关部分OJ题
链表相关部分OJ题
|
1月前
|
算法 搜索推荐 索引
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题(下)
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题
26 0
|
1月前
|
算法 程序员 索引
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题(中)
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题
29 0
|
1月前
|
算法 C语言 C++
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题(上)
数据结构与算法⑥(第二章OJ题,下)后八道链表面试题
17 0
|
1月前
|
算法 测试技术
数据结构与算法⑤(第二章OJ题,上)前五道链表面试题(下)
数据结构与算法⑤(第二章OJ题,上)前五道链表面试题
25 0
|
1月前
|
算法
数据结构与算法⑤(第二章OJ题,上)前五道链表面试题(上)
数据结构与算法⑤(第二章OJ题,上)前五道链表面试题
16 0