链表一些oj题

本文介绍了如何移除链表中特定值的节点,如`removeElements`函数;如何反转链表,包括`reverseList`和`middleNode`;以及合并有序链表和寻找链表相交点的方法。通过实例演示了链表操作技巧,适用于面试准备和技术提升。
摘要由CSDN通过智能技术生成

1.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

例子:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
//移除链表元素
struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode*node=(struct ListNode*)malloc(sizeof(struct ListNode));//创造哑结点
    struct ListNode*prev=NULL,*cur=node;
    
    node->next=head;

    while(cur)
    {
        if(cur->val==val)//删除和链接
        {
            prev->next=cur->next;
            free(cur);
            cur=prev->next;
        }
    
         else//迭代
        {
        prev=cur;
        cur=cur->next;
        }
    }
    return node->next;//head可能被删
}

思路:

1.创造哑结点,避免讨论单节点和多节点问题

链接:

力扣https://leetcode-cn.com/problems/remove-linked-list-elements/submissions/

2. 反转一个单链表。

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:


输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

//反转单链表
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode*newnode=NULL;
    struct ListNode*cur=head;
    struct ListNode*next=NULL;

    while(cur)
    {
        next=cur->next;//保存下一个节点地址
        cur->next=newnode;//第一个节点指向newnode
        cur=next;//从下一个节点出发
    }
    return cur;
}

链接:力扣力扣https://leetcode-cn.com/problems/reverse-linked-list/description/力扣

 3.链表的中间节点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

//链表的中间节点
struct ListNode* middleNode(struct ListNode* head){
    struct ListNode*slow,*fast;
    slow=fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}

 思路:

采用快慢指针法:

1.快指针比慢指针多走一步

2.当快指针走完后,慢指针刚好到了中间那个点

3.快指针的结束条件收到节点数奇偶的影响

        偶数时,fast==NULL;

        奇数时,fast->next==NULL;

链接

力扣https://leetcode-cn.com/problems/middle-of-the-linked-list/submissions/

4.输入一个链表,输出该链表中倒数第k个结点 

描述

输入一个链表,输出该链表中倒数第k个结点。

示例1

输入:

1,{1,2,3,4,5}

返回值:

{5}

求链表中倒数第k个结点
 ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    ListNode*slow,*fast;
        slow=fast=pListHead;
        while(k--)//不能--k 注意
        {
            if(fast==NULL)//fast已经为空,在走就错误
            {
                return NULL;
            }
            fast=fast->next;
        }
        while(fast)
        {
            slow=slow->next;
            fast=fast->next;
        }
        return slow;
    }

思路:

1.先让fast走k步

2.slow和fast一起走,当fast为NULL时,slow就是倒数第k个

链接:

链表中倒数第k个结点_牛客题霸_牛客网

5.合并两个有序链表
将两个升序链表合并为一个新的  升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

合并两个有序链表
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2){
//如果一个为空 ,则返回另一个
    if(l1==NULL)
    {
        return l2;
    }
    if(l2==NULL)
    {
        return l1;
    }
创造哨兵位的头结点-->不用讨论头结点为空的问题
    struct ListNode*head=NULL,*tail=NULL;
    head=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            tail->next=l1;
            tail=l1;
            l1=l1->next;
        }
        else
        {
            tail->next=l2;
            tail=l2;
            l2=l2->next;
        }
    }
//谁还有节点,就连谁
    if(l1)
    {
        tail->next=l1;
    }
    if(l2)
    {
        tail->next=l2;
    }
//返回
    return head->next;

}

思路:

        1.要讨论l1,l2是否为空链表

        2.创造哨兵卫,避免头结点为空指针

        3.l1或l2有一个可能先比较完,剩下的链接起来.  

6.相交链表

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

//相交链表
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* tailA=headA;
        struct ListNode* tailB=headB;
        //得lenA和lenB
        int lenA=1;
        while(tailA->next)
        {
            lenA++;
            tailA=tailA->next;
        }
         int lenB=1;
        while(tailB->next)
        {
            lenB++;
            tailB=tailB->next;
        }

        //不相交
        if(tailA!=tailB)
            return NULL;

        //长的先走差距步,再同时一起走
        int gap=abs(lenA-lenB);
        struct ListNode*longlist=headA;
        struct ListNode*shortlist=headB;
        if(lenA<lenB)
        {
            longlist=headB;
            shortlist=headA;
        }
        while(gap--)
        {
            longlist=longlist->next;
        }

        //找到共同点
        while(longlist!=shortlist)
        {
            longlist=longlist->next;
            shortlist=shortlist->next;
        }
        return shortlist;
}

 思路:

1.尾结点相同就是相交

2.长链表先走,然后长短链表一起走,走到同一个节点就是交点

链接:

力扣https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

7. 链表的分割

描述

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

//链表分割
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode*lesshead,*lesstail;
        struct ListNode*greaterhead,*greatertail;
        
        //创造哨兵位,方便尾插
        lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lesstail->next=NULL;
        greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
        greatertail->next=NULL;
        
        //比较迭代 链接
        struct ListNode*cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                lesstail->next=cur;
                lesstail=cur;
            }
            else
            {
                greatertail->next=cur;
                greatertail=cur;
            }
            cur=cur->next;
        }
        lesstail->next=greaterhead->next;
        greatertail->next=NULL;
        
        struct ListNode*newhead=lesshead->next;
        free(lesshead);
        free(greaterhead);
        return newhead;
        
    }
};

链接:链表分割_牛客题霸_牛客网

8.链表的回文结构

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

测试样例:

1->2->2->1
返回:true
//回文结构
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        //先找到中间节点
         struct ListNode*slow,*fast;
         slow=fast=A;
         while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
        
        
         struct ListNode*newnode=NULL;
         struct ListNode*cur=slow;
         struct ListNode*next=NULL;
        //逆置中间节点后部分
    while(cur)
    {
        next=cur->next;//保存下一个节点地址
        cur->next=newnode;//第一个节点指向newnode
        newnode=cur;
        cur=next;//从下一个节点出发
    }
       //比较
        while (A && newnode)
		{
			if (A->val != newnode->val)
				return false;
			A = A->next;
			newnode = newnode->next;
		}
		return true;
    }
};

思路:

1.寻找链表的中间节点

2.逆置中间节点后面部分

3.进行比较

链接:

链表的回文结构_牛客题霸_牛客网【牛客题霸】收集各企业高频校招笔面试题目,配有官方题解,在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking

   9.环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false 。

//环形链表
bool hasCycle(struct ListNode *head) {
          struct ListNode *slow,*fast;
          slow=fast=head;
          while(fast&&fast->next)//fast->next针对奇的情况
          {
              slow=slow->next;
              fast=fast->next->next;
              if(slow==fast)
              {
                  return true;
              }
          }
          return false;
}

 思路:

快慢指针法:

1.快指针走2步,慢指针走1步.

2.二者最后会在环内相遇.

10.环形链表2

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

//环形链表2
struct ListNode *detectCycle(struct ListNode *head) {
       struct ListNode *slow,*fast;
          slow=fast=head;
          while(fast&&fast->next)//fast->next针对奇的情况
          {
              slow=slow->next;
              fast=fast->next->next;
              if(slow==fast)
              {
                  struct ListNode*meet=slow;
                  while(meet!=head)
                  {
                      meet=meet->next;
                      head=head->next;
                  }
                  return  meet;

              }
            }
    return NULL;
}

 思路:

公式证明,后面补充

11.复制带随机指针的链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

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

    //1.拷贝节点插入原来节点的后面
         copy->next=cur->next;
         cur->next=copy;

         cur=copy->next;
    }
    //2.根据原节点,处理copy节点的random
    
    cur=head;
    while(cur)
    {
        struct Node*copy=cur->next;
        if(cur->random==NULL)
            copy->random=NULL;
        else
            copy->random=cur->random->next;
        cur=copy->next;
    }
    //3.把拷贝节点解下来,链接成新链表,同时恢复原链表
    struct Node*copyHead=NULL,*copyTail=NULL;
    cur=head;
    while(cur)
    {
        struct Node*copy=cur->next;
        struct Node*next=copy->next;
         if(copyTail==NULL)
        {
            copyHead=copyTail=copy;
        }
        else
        {
             copyTail->next=copy;
             copyTail=copy;
        }
        cur->next=next;
        cur=next;
    }
   
    return copyHead;

}

 思路:

1.拷贝节点插入原来节点的后面

2.根据原节点,处理copy节点的random

3.把拷贝节点解下来,链接成新链表,同时恢复原链表

链接:力扣icon-default.png?t=LA92https://leetcode-cn.com/problems/copy-list-with-random-pointer/submissions/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值