链表中的常见oj问题解析

常见的链表oj题:

目录

常见的链表oj题:

1.删除链表中等于给定值 val 的所有结点。

2.反转一个单链表

 3.寻找链表的中间节点

4.链表中倒数第k个结点

 5.合并两个有序链表

6.链表分割问题

 7.链表回文

8.相交节点

9.环形链表

最后,以上就是全部内容了,希望不足的地方和方法有更好的地方可以在评论区一起讨论哦,欢迎大佬们进行讨论。 

1.删除链表中等于给定值 val 的所有结点。

 移除链表元素

 我们先进行思路分析,这道题我们可以先遍历,然后寻找链表中的数据域等于val的节点,然后对这个节点进行删除,也就是将上一个节点的指向下一个节点的的下一个节点。同时我们也要注意头节点这一个情况。

struct ListNode* removeElements(struct ListNode* head, int val)
{
    //用两个节点
    //当cur遇到val,prev直接跳到下一个
    struct ListNode*prev=NULL,*cur=head;//prev表示的是cur节点的上一个节点
    while(cur)
    {
        if(cur->val==val)
        {
            if(cur==head)//为头节点这一情况            
            {
                head=cur->next;//将头节点移向下一个节点
                free(cur);//删除当前节点
                cur=head;//重新将cur节点置成头节点
            }
            else
            {
                prev->next=cur->next;//将prev节点指向cur的下一个节点,完成删除操作
                free(cur);
                cur=prev->next;
            }
        }
        else
        {
            prev=cur;
            cur=cur->next;//遍历整个链表
        }
    }
    return head;
}

2.反转一个单链表

反转链表

我们也进行简单的思路分析:我们可以建立一个新链表,将后面的节点不断移到新链表前面。

 如果只定义两个节点,当n2指向n1时,n2的下一个结点就找不到了。因此要定义3个节点n1和n2用于翻转,n3用于迭代:n1指向NULL,n2指向n1,n3用于记录n2的下一个结点

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode*n1,*n2,*n3;
    n1=NULL;
    n2=head;
    if(n2)
    {
        n3=n2->next;
    }
    while(n2)
    {
        n2->next=n1;//将后面的节点移向前面
        n1=n2;
        n2=n3;//翻转
        if(n3)
    {
        n3=n3->next;
    }
    }
    
      return n1;
}

我们观察,我们就发现了一个规律,他们是反着的,然后刚好满足头插法的效果。所以这道题我们可以用头插法。

 

struct ListNode* reverseList(struct ListNode* head)
{

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


 3.寻找链表的中间节点

链表的中间结点

 思路分析:

这题我们可采用快慢节点的来进行解题,两者速度相差为1,到最后就相差一半,慢的刚好到中间位置

struct ListNode* middleNode(struct ListNode* head)
{
   struct ListNode*fast=head,*slow=head;
     while(fast&&fast->next)//两个错开,不同
     {
         slow=slow->next;
         fast=fast->next->next;
     }
     return slow;
}

4.链表中倒数第k个结点

链表中倒数第k个结点_牛客题霸_牛客网 (nowcoder.com)

思路分析:

我们也可以同样设置两个快慢节点,一个节点先移动到第k个,然后两个节点同时移动,直到当fast下一个指向空时,slow就刚好指向倒数第k个节点。因为一开始slow和fast刚好相差k个,当fast下一个指向空时,slow就刚好指向倒数第k个节点

 


 

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    
    //利用距离相差原理
    struct ListNode*fast=pListHead,*slow=pListHead;
    while(k--)//fast先走k个距离
    {
        if(fast==NULL)//fast大于k值
        {
            return NULL;
        }
        fast=fast->next;
    }
    while(fast)
    {  
        fast=fast->next;
        slow=slow->next;
       
    }
    return slow;
}
 

 5.合并两个有序链表

合并两个有序链表

思路分析

我们先建立一个新链表,再将两个链表分别进行比较,小的直接插入到新链表的后面。同时我们还要考虑一些问题其中一个为空时,比较完时,剩余部分的处理


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1==NULL)//处理如果一个为空时
    {
        return list2;
    }
    if(list2==NULL)
    {
        return list1;
    }
    struct ListNode *head=NULL,*tail=NULL;//创建一个新链表
    while(list1&&list2)//比较两者大小,插入到新链表后面。
    {
        if(list1->val < list2->val)
        {
            if(tail==NULL)
            {
                head=tail=list1;
            }
           else
           {
               tail->next=list1;
                tail=tail->next;
           }
           list1=list1->next;
        }
        else
        {
            if(tail==NULL)
            {
                head=tail=list2;
            }
           else
           {
               tail->next=list2;
               tail=tail->next;
           }
           list2=list2->next;
        }
    }
    if(list1)//剩余部分的处理
    tail->next=list1;
    if(list2)
    {
        tail->next=list2;
    }
   return head;

}

6.链表分割问题

链表分割_牛客题霸_牛客网 (nowcoder.com)

思路分析

我们先建立两个链表,一个比x大的链表,一个小于x的链表。我们需要不断进行比较,分别将数据保存在这两个链表中。最后链接这两个链表成一个链表也就是前面是小于x的链表后面是大于x的链表,返回这个链表的头节点。

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode*ghead,*gtail,*Lhead,*Ltail;
        ghead=gtail=(struct ListNode*)malloc(sizeof(struct ListNode));//大于x的链表
        Lhead=Ltail=(struct ListNode*)malloc(sizeof(struct ListNode));//小于x的链表
         struct ListNode*cur=pHead;
         //利用两个链表分别记录,在同时连接
         while(cur)
         {
            if(cur->val<x)//分别保存
            {
                Ltail->next=cur;
                Ltail=Ltail->next;
            }
            else 
            {
            gtail->next=cur;
            gtail=gtail->next;
            }
            cur=cur->next;
         }
         Ltail->next=ghead->next;//进行链接
         gtail->next=NULL;//不空,出现损坏
           struct ListNode*head=Lhead->next;
           free(Lhead);//分别释放不然会内存泄漏
           free(ghead);
           return head;//返回连接后新头节点
    }
};

 7.链表回文

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)

思路分析

我们先观察回文特点,我们不免发现回文中,后一半节点都是前一半的反转。所以我们应该先找到这这个链表中间节点。然后反转中间节点后一半的链表。与前一半链表进行比较,相同就是回文。

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
struct ListNode* middleNode(struct ListNode* head)//中间节点函数
{
   struct ListNode*fast=head,*slow=head;
     while(fast&&fast->next)//两个错开,不同
     {
         slow=slow->next;
         fast=fast->next->next;
     }
     return slow;
}
struct ListNode* reverseList(struct ListNode* head)//反转链表函数
{
    struct ListNode*n1,*n2,*n3;
    n1=NULL;
    n2=head;
    if(n2)
    {
        n3=n2->next;
    }
    while(n2)
    {
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3)
    {
        n3=n3->next;
    }
    }
    
      return n1;
}
    bool chkPalindrome(ListNode* A) {
        //后半段逆置
          struct ListNode *mid=middleNode(A);
          struct ListNode*rmid=reverseList(mid);
          while(rmid&&A)
          {
            if(rmid->val!=A->val)//判断两个对应是否相等
            {
                return false;
            }
              rmid=rmid->next;
              A=A->next;
          }
          return true;

    }
};

8.相交节点

相交链表

 思路分析

这道题我们可以用一下前面我们用到距离相差(快慢的方法,先让他们能够在同一长度后同时比较,这样就可以知道他的每一个节点是否有相同的地方

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    //计算a,b的长度
    //长的链表走差距步abs(lenA-lenB)
    //时间复杂度O(n)
   struct ListNode *curA=headA,*curB=headB;
   int lenA=1;
   int lenB=1;
   while(curA->next)
   {
       ++lenA;
       curA=curA->next;
   }
   while(curB->next)
   {
       ++lenB;
       curB=curB->next;
   }
   //不相交
   if(curA!=curB)
   {
        return NULL;
   }
   int gap=abs(lenA-lenB);
   struct ListNode *longlist=headA,*shortlist=headB;
  if(lenA<lenB)
  {
     longlist=headB;
     shortlist=headA;
  }
   //长的走差距步
   while(gap--)
   {
       longlist=longlist->next;
   }
   //一起走
   while(longlist!=shortlist)
   {
       longlist=longlist->next;
       shortlist=shortlist->next;
   }
   return longlist;

}

9.环形链表

环形链表

 思路分析:

我们还是可以设置两个快慢节点,他们相差为1,如果在环中他们必定会相遇,相遇了就是环形链表。

bool hasCycle(struct ListNode *head) {
    //理解为追及问题
      struct ListNode*fast=head,*slow=head;
      while(fast&&fast->next)
      {
          fast=fast->next->next;
          slow=slow->next;
          if(fast==slow)
          {
              return true;
          }

      }
      return false;
}

最后,以上就是全部内容了,希望不足的地方和方法有更好的地方可以在评论区一起讨论哦,欢迎大佬们进行讨论。 

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值