【数据结构】单链表的应用

1.移除链表元素

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

思路: 创建新链表,找值不为val的节点,尾插到新链表中

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val) {
     ListNode*newHead,*newTail;  //创建新链表的头指针和尾指针
     newHead=newTail=NULL;
     //遍历原链表
     ListNode*pcur=head;
     while(pcur)
     {
        //找值不为val的节点,尾插到新链表中
        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;
}

 2.反转链表

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

 思路:创建三个指针,完成原链表的翻转。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head) {
    //判空
    if(head==NULL)
    {
        return head;
    }
    //创建3个指针
    ListNode*n1,*n2,*n3;
    n1=NULL;
    n2=head;
    n3=n2->next;
    while(n2)
    {
        n2->next=n1;
        n1=n2;
        n2=n3;
        if(n3)//n3可能已经走到空了
        n3=n3->next;
    }
    return n1;
}

3.链表的中间节点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

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

思路:使用快慢指针,慢指针每次走一步,快指针每次走两步。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {
    //创建快慢指针
    ListNode*slow=head;
    ListNode*fast=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    //此时slow刚好指向中间节点
    return slow;
}

while(fast&&fast->next) 语句可以写成 while(fast->next&&fast)吗?

不可以。若链表中有偶数个节点,fast最后会直接走到空,fast->next操作对空指针进行解引用,会报错!

4.合并两个有序链表

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

思路:创建新的空链表,遍历原链表,将节点值小的节点拿到新链表中进行尾插操作。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
   //判空  list1为空  list2为空  list和list2都为空
   if(list1==NULL)
   {
    return list2;
   }
   if(list2==NULL)
   {
    return list1;
   }
    ListNode*l1=list1;
    ListNode*l2=list2;
    //创建的新链表
    ListNode*newHead=NULL;
    ListNode*newTail=NULL;
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            //l1拿下来尾插
            if(newHead==NULL)
            {
                newHead=newTail=l1;
            }
            else
            {
                newTail->next=l1;
                newTail=newTail->next;
            }
            l1=l1->next;
        }
        else
        {
             //l2拿下来尾插
            if(newHead==NULL)
            {
                newHead=newTail=l2;
            }
            else
            {
                newTail->next=l2;
                newTail=newTail->next;
            }
            l2=l2->next;
        }
    }
    //跳出循环有两种情况:一.l1走到空了 二.l2走到空了
    if(l2)
    {
        newTail->next=l2;
    }
    if(l1)
    {
        newTail->next=l1;
    }
      return newHead;
}

仔细观察,上面代码存在重复,重复操作判断链表是否为空,怎么解决这个问题呢?

让新链表不为空。使用malloc函数给 newHead 和 newTail 指针申请一块空间,它们不再是空指针,此时链表不为空,头尾指针都指向了一个有效的地址。如下图,newHead为头结点,被称为“哨兵位

代码优化为:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
 struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
   //判空  list1为空  list2为空  list和list2都为空
   if(list1==NULL)
   {
    return list2;
   }
   if(list2==NULL)
   {
    return list1;
   }
    ListNode*l1=list1;
    ListNode*l2=list2;
    //创建的新链表
    ListNode*newHead,*newTail;
    newHead=newTail=(ListNode*)malloc(sizeof(ListNode));    
    while(l1&&l2)
    {
        if(l1->val<l2->val)
        {
            //l1拿下来尾插
             newTail->next=l1;
             newTail=newTail->next;
             l1=l1->next;
        }
        else
        {
             //l2拿下来尾插
             newTail->next=l2;
             newTail=newTail->next;
             l2=l2->next;
        }
    }
    //跳出循环有两种情况:1.l1走到空了 2.l2走到空了
    if(l2)
    {
        newTail->next=l2;
    }
    if(l1)
    {
        newTail->next=l1;
    }
      //动态申请的空间手动释放掉
      ListNode* ret=newHead->next; //newHead的下一个节点保存下来
      free(newHead);
      newHead=NULL;
      return ret;  
}

5.循环链表经典应用

——环形链表的约瑟夫问题

著名的Josephus问题

据说著名犹太 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个人重新报数,直到所有人都自杀身亡为止。 然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己自排在第16个与第31个位置,于是逃过了这场死亡游戏。

题目描述

思路: 

typedef struct ListNode ListNode;
 //创建节点
 ListNode* buyNode(int x)
 {
    ListNode*node=(ListNode*)malloc(sizeof(ListNode));
    if(node==NULL)
    {
        exit(1);
    }
    node->val=x;
    node->next=NULL;
    return node;
 }
 ListNode*createCircle(int n)
 {
    //先创建头节点
   ListNode* phead=buyNode(1);
   ListNode* ptail=phead;
   for (int i=2; i<=n; i++) {
    ptail->next=buyNode(i);
    ptail=ptail->next;
   }
    ptail->next=phead;//首尾相连,链表成环
    return ptail;
 }
 int ysf(int n, int m ) {
    // 根据n创建带环链表
    ListNode*prev=createCircle(n);
    ListNode*pcur=prev->next;
    int count=1; //计数
    while(pcur->next!=pcur)
    {
         if(count==m)
         {
            //销毁pcur节点
            prev->next=pcur->next;
            free(pcur);
            pcur=prev->next;
            count=1;
         }
         else
         {
            prev=pcur;
            pcur=pcur->next;
            count++;
         }
    }
    //此时剩下一个节点,返回的节点里的值
    return pcur->val;
}

6.分割链表

题目描述

 

思路: 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* partition(struct ListNode* head, int x){
    if(head==NULL)
    {
        return head;
    }
     //创建两个带头链表
    ListNode*lessHead,*lessTail;
    ListNode*greaterHead,*greaterTail;
    lessHead=lessTail=(ListNode*)malloc(sizeof(ListNode));  
    greaterHead=greaterTail= (ListNode*)malloc(sizeof(ListNode));
    //遍历原链表,将原链表中的节点尾插到大小链表中
    ListNode*pcur=head;
    while(pcur)
    {
     if(pcur->val<x)
     {
        //尾插到小链表中
        lessTail->next=pcur;
        lessTail=lessTail->next;
     }
     else
     {
        //尾插到大链表中
       greaterTail->next=pcur;
       greaterTail=greaterTail->next;
     }
      pcur=pcur->next;
    }
      //修改大链表的尾节点的next指针的指向
    greaterTail->next=NULL;  
    
    //小链表的尾节点和大链表的第一个有效节点首尾相连
    lessTail->next=greaterHead->next;
    
    ListNode*ret=lessHead->next;
    free(lessHead);
    free(greaterHead);
    lessHead=greaterHead=NULL;
    return ret;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值