单链表经典算法题

反转链表,顾名思义即为将链表颠倒,有一个非常容易想到的思路,就是你创建一个新链表,然后逐个遍历原链表的节点,逐个头插进新链表中。但我今天不讲这个方法,我要为大家讲解一种在原链表上原地反转的方法。

其实说插入链表总共要考虑什么呢,新节点的指向,前一个结点的指向;那么说删除节点又要考虑什么呢,其实就是前一个节点的指向;说来容易,那我们这里的反转,无非要考虑的情况也就那么几种,首先我们要找到当前节点,这是肯定的吧,光有当前节点肯定不够,我们还需要当前节点的前一个结点和后一个节点,前一个结点你能理解,因为你要修改当前节点的指向,改成原先的前一个结点,那后一个节点是为啥呢?其实很好想,当你将当前节点的指向修改后你已经无法找到原来的下一个节点了,所以必须提前拿到后手动赋值以免循环无法进行。那么赋值又该怎么赋?无非是整体后移一位,即prev指向上一次的now,now指向next,next指向新的next;那么循环啥时候截止?当now为空时,代表你已经完成任务,链表里已经没有元素了,此时截止即可。但是还有细节不要忘记,就是我们是要改变头节点的朝向的,一定记得最后将prev的值赋给头节点,为啥是prev?还记得我们说now为空时循环终止条件吗,prev自然是第一个元素。照例考虑临界情况,首先链表只有一个元素怎么办?直接返回这个节点即可,那如果链表为空呢?那直接返回NULL就可以了吧。代码实现如下

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode LN;
struct ListNode* reverseList(struct ListNode* head) {
   if(head == NULL)
   {
        return NULL;
   }
    LN* pnow=head->next;
    LN* prev=head;
    head->next=NULL;
    while(pnow)
    {
        LN* next=pnow->next;
        pnow->next=prev;

        prev=pnow;
        pnow=next;
    }
    return prev;
}

接下来第二题

移除链表元素,跟上一篇我讲的移除顺序表元素很像,但其实更加简单了,因为这个无非就是在考你链表指定位置的删除,因为不同于数组,链表删除是十分便利的,根本就涉及不到元素的挪动问题,所以其实这道题没啥好说的,我直接放代码

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode LN;

struct ListNode* removeElements(struct ListNode* head, int val) {
    LN* pnow=head;
    LN* prev=NULL;
    while(pnow)
    {
        LN* next=pnow->next;
        if(pnow->val==val)
        {
            if(pnow==head)
            {
                head=next;
            }
            if(prev)
            {
                 prev->next=next;
            }
           
        }
        else
        {
            prev=pnow;
        }
        pnow=next;
        
    }   
    return head;
}

第三题

中间节点,这里我要给大家介绍一个非常重要的思想,快慢指针,快指针走两步慢指针走一步,最后快指针无法再走时慢指针一定指向中间节点,这就跟物理速度差不多,你速度二倍时间一样,路程可不就是二倍关系吗。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode LN;
struct ListNode* middleNode(struct ListNode* head) {
    LN* fast=head;
    LN* slow=head;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
    }
    return slow;
}

第四题

这道题我选择创建新链表逐个尾插的方式,同样是定义两个指针遍历两个链表,然后定义新的头节点,但是这里注意,创建新链表只是存在于逻辑上,并不是真的开辟了一块空间存储数个节点,而是在原链表上进行操作,只是形如建立新链表的样子,待到一方为空则直接把非空链表的剩余节点尾插即可,代码如下

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode LN;

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    LN* l1=list1;
    LN* l2=list2;
    if((l1==NULL && l2!=NULL)||(l1!=NULL && l2==NULL))
    {
        return l1==NULL?l2:l1;
    }
    LN* newhead=NULL;
    LN* newtail=NULL;
    while(l1&&l2)
    {
        if(l1->val < l2->val)
        {
            if(newhead==newtail)
            {
                if(newhead==NULL)
                {
                    newhead=l1;
                    newtail=newhead;
                }
                else
                {
                    newtail=l1;
                    newhead->next=newtail;
                }
            }
            else
            {
                newtail->next=l1;
                newtail=newtail->next;
            }
            l1=l1->next;

        }
        else
        {
             if(newhead==newtail)
            {
                if(newhead==NULL)
                {
                    newhead=l2;
                    newtail=newhead;
                }
                else
                {
                    newtail=l2;
                    newhead->next=newtail;
                }
            }
            else
            {
                newtail->next=l2;
                newtail=newtail->next;
            }
            l2=l2->next;
        }
    }
    if(l1==NULL)
    {
        while(l2)
        {
            newtail->next=l2;
            newtail=l2;
            l2=l2->next;
        }
    }
    else
    {
        while(l1)
        {
            newtail->next=l1;
            newtail=l1;
            l1=l1->next;
        }
    }
    return newhead;
}

第五题

这应该是最复杂的一道题,但是并不算难,只是步骤比较多,我们捋一下这道题,首先他说围成一圈,其次我们需要不停的去淘汰节点,或者说删除节点,那自然不能使用数组,也不能使用普通的单链表,因为达不到首尾相接的效果,这时候就要想到循环链表,即尾节点指向头节点以达到循环效果,那么解决了选用数据结构的问题,接下来开始正式梳理这道题,看到这道题的淘汰制,一定第一时间想到删除,单链表节点的删除需要三个节点,循环链表其实也是一样,在这里区别不大,因为我们是单向循环,这是底层逻辑,然后针对这道题,我们要考虑报数的机制,从1开始,逐个遍历,遍历一个节点加1,达到目标后停止,并删除相应节点,然后就要接着遍历,从1接着开始,循环往复直到链表中只剩一个节点,这里的删除我要简单说一句,删除节点后,prev是不能动的,你想,你删掉了这个节点,那么下一个节点的上一个节点不久改成prev了吗,其他的部分其实没什么特别需要注意的地方,具体可以参考下面代码

cpp 复制代码
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param n int整型 
 * @param m int整型 
 * @return int整型
 */

typedef struct ListNode LN;
int ysf(int n, int m ) {
    // write code here
    //成环
    LN* newhead=(LN*)malloc(sizeof(LN));
    LN* pcur=newhead;
    LN* ptail=newhead;
    newhead->val=1;
    newhead->next=ptail;
    for(int i=2;i<=n;i++)
    {
        LN* newnode=(LN*)malloc(sizeof(LN));
        newnode->val=i;
        pcur->next=newnode;
        pcur=pcur->next;
    }
    pcur->next=newhead;
    ptail=pcur;

    LN* prev=ptail;
    LN* pnow=newhead;
    LN* next=newhead->next;
    int cnt=0;
    while(pnow!=prev || pnow!=next)
    {
        cnt++;
        if(cnt==m)
        {
            prev->next=next;
            free(pnow);
            pnow=next;
            next=pnow->next;
            cnt=0;
        }
        else 
        {
            prev=pnow;
            pnow=next;
            next=pnow->next;
        }
    }
    return pnow->val;

}
相关推荐
Navigator_Z4 分钟前
LeetCode //C - 1311. Get Watched Videos by Your Friends
c语言·算法·leetcode
云析赢指标公式网419 分钟前
通达信ATAN均线角度公式源码笔记:四周期角度计算与DRAWBAND色带实现
笔记·算法
21岁被迫秃头30 分钟前
SVT-AV1 Mini-Gop决策算法详解+源码分析.md
算法·av1·svt-av1·源码详解·mini-gop决策
Kobebryant-Manba31 分钟前
IEKT向量矩阵流程&评估
人工智能·算法·矩阵
Js.Tx1 小时前
【具体数学 Concrete Mathematics】2.1.7 无限和式
算法·具体数学
FPGA信号处理1 小时前
【 凸优化】第六讲:求解器与支持向量机
算法·机器学习·支持向量机
专注仿真1 小时前
支持向量机(SVM)算法详解与代码实例
算法·机器学习·支持向量机
Pniubi1 小时前
力扣42接雨水(动态规划法)
算法·leetcode·职场和发展
CQU_JIAKE1 小时前
10.31[A]
算法·深度优先
zwc32104320551 小时前
Day 5:threshold与inRange阈值分割
c++·图像处理·人工智能·opencv·算法·计算机视觉