单链表经典算法

一,移除链表元素

思路一

遍历数组,如果遇到链表中的元素等于val的节点就执行删除操作

typedef struct ListNode ListNode;
 
 struct ListNode* removeElements(struct ListNode* head, int val) {
  
    if(head==NULL)
    {
        return NULL;
    } 
     ListNode*pnewhead=(ListNode*)malloc(sizeof(ListNode));
    pnewhead->next=head;
    ListNode*pcur=head;
    ListNode*prev=pnewhead;
    while(pcur)
    {
        if(pcur->val==val)
        {
            prev->next=pcur->next;
            free(pcur);
            pcur=NULL;
            pcur=prev->next;
        }
        else
        {
            prev=pcur;
            pcur=pcur->next;
            
        }
    }
    return pnewhead->next;
 }

思路二

建立一个新的链表,遇到的节点里面存的数据如果不等于val的值的话就将这个节点尾插到建立的新的链表的后面

typedef struct ListNode ListNode;
 struct ListNode*removeElement(struct ListNode*head,int val)
 {
    ListNode*pcur=head;
    ListNode*newhead,*newtail;//建立新的链表
    newhead=newtail=NULL;
    while(pcur)//遍历链表
    {
        if(pcur->val!=val)
        {
            if(newhead==NULL)
            {
                newhead=newtail=pcur;//新链表为空,将当前的节点赋值给新的链表
            }else
            {
                newtail->next=pcur;//不为空,插到新链表的后面
                newtail=newtail->next;
            }
        }
        pcur=pcur->next;
    }
    if(newtail)//如果链表的不为空的话,就让尾节点的后面为空
    {
        newtail->next=NULL;
    }
    return newhead;
 }
 

代码说明

 if(newtail)//如果链表的不为空的话,就让尾节点的后面为空
 {
     newtail->next=NULL;
 }

因为链表在插入的时候,当前节点的后面还有节点,插入的时候会后面的节点会跟在后面,所以要将原链表的尾节点的后面的节点置为空,比如下面的

当要将上面的链表中的4处于的节点的位置插入到下面0位于的节点的后面,插入的不是4,而是4和4链接的5位于的节点,插入的是后面的一串,这和顺序表不一样 

二.反转链表

思路

这里可以定义三个指针,一个指向当前节点,一个指向当前节点的上一个节点,一个指向当前节点的下一个节点,直接让当前节点指向上一个节点

typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
    ListNode*prev=NULL;//前驱节点
    ListNode*pcur=head;//当前节点
    ListNode*back=NULL;//当前节点的下一个节点
     if(head==NULL)
    {
        return NULL;
    }
   
    if(head->next==NULL)
    {
        return head;
    }
    while(pcur)
    {
        pcur->next=prev;
         prev=pcur;
        pcur=back;
        if(back!=NULL){
        back=back->next;
        }
    }
    return prev;
}

三.将两个升序链表合并成为一个新的升序链表

思路:定义一个新的无效链表头(里面没有数据,在后续的程序中不需要判断该链表是否是空链表,能够对代码进行优化,有返回值的时候直接返回这个链表头的下一个节点就可以了),以及两个指针,分别指向两个升序链表,比较两个链表里面的值,谁小就把谁放到新链表的后面,然后这个指针指向下一个节点就行了

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    ListNode*pcur1=list1;//定义指针1
    ListNode*pcur2=list2;//定义指针2
    if(list1==NULL)
    {
        return list2;//为空链表的话返回另一个链表
    }
    if(list2==NULL)
    {
        return list1;//为空的话返回另一个链表
    }
    ListNode*phead=(ListNode*)malloc(sizeof(ListNode));
    ListNode*pcur3=phead;
    while(pcur1&&pcur2)
    {
        if(pcur1->val<=pcur2->val)
        {
            pcur3->next=pcur1;
            pcur3=pcur3->next;
            pcur1=pcur1->next;
        }
        else
        {
             pcur3->next=pcur2;
            pcur3=pcur3->next;
            pcur2=pcur2->next;
        }
    }
    if(pcur2==NULL)
    {
        pcur3->next=pcur1;//插入的是该节点以及该节点后面的节点,一步到位
    }
    if(pcur1==NULL)
    {
       
       pcur3->next=pcur2;
    }
    return phead->next;

}

四.链表的中间节点

思路:定义两个指针,一个快指针,一个慢指针,满指针一次走一格,快指针一次走两格,最后返回慢指针就行了

typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
    ListNode*fast,*slow;
    fast=head,slow=head;
    if(head->next==NULL)
    {
        return head;
    }
    while(fast&&fast->next)
    {
        slow=slow->next;
        
        fast=fast->next->next;
            
        
    }
    return slow;
    
}

五,分割链表

思路:定义两个新的链表,遍历原链表,原链表里面的节点里面的值和x比较,小的尾插在第一个链表后面,大于等于的尾插在第二个链表的后面,最后连接两个链表

typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
  ListNode*pcur=head;
  ListNode*pcur1=(ListNode*)malloc(sizeof(ListNode));
  ListNode*pcur2=(ListNode*)malloc(sizeof(ListNode));
  ListNode*head1=pcur1;
  ListNode*head2=pcur2;
  if(head==NULL)
  {
    return NULL;
  }
  if(head->next==NULL)
  {
    return head;
  }

  while(pcur)
  {
    if(pcur->val<x)
    {
        pcur1->next=pcur;
        pcur1=pcur1->next;
    }
    else
    {
        pcur2->next=pcur;
        pcur2=pcur2->next;
    }
    pcur=pcur->next;
  }
  pcur2->next=NULL;
  pcur1->next=head2->next;
 return head1->next;
}

 注意:上面的代码为什么要有下面的这句话嘞?

pcur2->next=NULL;

 原因是插入链表节点的时候,后面带着的节点是跟在本节点的后面的,插的时候就顺带着了,本节点后面不置为空就会造成死循环

六.环形链表的约瑟夫问题 

什么是环形链表的约瑟夫问题,先看一看下面的故事:

著名的Josephus问题 据说著名犹太 历史学家 Josephus有过以下的故事:在罗⻢⼈占领乔塔帕特后,39个犹太⼈与 Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被⼈抓到,于是决定了⼀个⾃杀 ⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀ 个重新报数,直到所有⼈都⾃杀⾝亡为⽌。 然⽽Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在 第16个与第31个位置,于是逃过了这场死亡游戏。

第一步:创建一个环形链表

第二部:定义一个变量,初值为1,如果该变量不等于m,该变量加1,继续遍历链表,等于,删除该节点,变量重新等于1

typedef struct ListNode ListNode;
//产生一个节点
 ListNode*buynode(int x)
 {
    ListNode*newnode=(ListNode*)malloc(sizeof(ListNode));
    newnode->val=x;
    newnode->next=NULL;
    return newnode;
 }
//建立一个环形链表
 ListNode*creatcircle(int n)
 {
    ListNode*phead=buynode(1);
    ListNode*pcur=phead;
    phead->val=1;
    for(int i=2;i<=n;i++)
    {
        pcur->next=buynode(i);
        pcur=pcur->next;
        
    }
   pcur->next=phead;
   return phead;
 }
int ysf(int n, int m )
 {
   
    ListNode*phead=creatcircle(n);
    ListNode*pcur=phead;
    ListNode*prev=NULL;
    int count=1;
    while(pcur->next!=pcur)
    {
        if(count==m)
        {
            prev->next=pcur->next;
            free(pcur);
            pcur=NULL;
            count=1;
            pcur=prev->next;
        }
        else 
        {
            prev=pcur;
            pcur=pcur->next;
            count++;
        }
        
    }
    return pcur->val;
}

七.结语

在链表中,最值得注意的是,尾插链表,插入的是该节点和该节点后面的节点,也就是一串节点

在插入链表的时候,记得要将该节点后面置为空

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值