【算法模板】链表篇—(附牛客习题)

【算法模板】链表篇—(附牛客习题)

前言

好久没有更新这个模块文章了呀。也有许多粉丝跑过来催更嘿嘿,所以在百忙之中来给大家续写 算法模板 这一系列的文章。

本篇文章所用的是 牛客 在线编程的算法习题。在这里博主要感谢 csdn和牛客 两位大大,一个提供笔记平台,一个提供习题练习平台。得所以让我进步!非常感谢!

练习题地址: 牛客算法习题地址

在这里插入图片描述

那好话不多说,我们启航!!!

在这里插入图片描述

什么是链表???

简介:

由百度百科解释: 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域另一个是存储下一个结点地址的指针域

在这里插入图片描述

链表的类型:

常见的链表有:

  • 单链表

    是最常见的一种链表,也就是上图所示。

  • 双链表

    每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

    在这里插入图片描述

  • 循环链表

    顾名思义,循环链表就是一个死循环的链表。如下图所示:

    在这里插入图片描述

当然我们也会遇见相对应的算法习题哦!


链表节点的构建(Java和Python版本)

链表节点得构建就像盖房子的打地基一样,就算你上面弄得在天花乱坠。如果你的地基不稳,你可能就没有机会来实现你天花乱坠的想象。

在面试过程中链表有的时候也是需要我们自己来一步步的进行构建,所以今天也就给大家带来相对应的JavaPython两种版本的构建。

在这里插入图片描述

Java链节点表构建

  public class ListNode {
    
    
    int val;//根据情况来对val定义属性。
    ListNode next = null;//一般节点没有赋值就为空。
 }

Python节点构建

 class ListNode:
     def __init__(self, x):
         self.val = x
         self.next = None

注意: 一般在核心代码模块的时候是不需要我们来构建链表的节点的,但是如果是在ACM模式则就需要我们会构建相对应的节点

所以我们需要牢固掌握节点的构建方法!


链表的基本操作

和数组一样,链表也是有自己的增删改查的方法。那么今天我就给大家带来相对应的链表基本操作嘿嘿嘿。

在这里插入图片描述

添加链表节点

添加链表我们可以分为以下两种添加方式:

  • 插入式添加

  • 链表末尾添加

首先我们来看末尾式添加:

在这里插入图片描述

可以直接让尾部节点指向新增节点。

实现代码:

#为了方便讲解我就给大家使用python作为讲解对象啦。
class Solution:
    def add(self , A: ListNode, C) -> ListNode:
        a = A
        #循环到最后一个节点的位置
        while a.next:
            a = a.next
        #让最后一个节点指向添加节点
        a.next = C
        #返回A节点即可
        return A
            

插入式添加

来看下图所示:

在这里插入图片描述

我们能看到,如果插入添加节点的话,是会让B的指针指向C,在让C节点指向D。则完成一个新节点的插入。

代码实现:

class Solution:
    def add(self , A: ListNode, C, k:int) -> ListNode:
        #k表示插入位置
        a = A
        b = a.next
        for i in range(k):
            a = a.next
            b = b.next
        #确保不是插入末尾
        if b:
            a.next = C
            C.next = b
        else:
            a.next = C
        return A
        

以上就是链表的两种增添方式了啦!

删除链表节点

说到删除节点,其实和上面的插入节点的方法其实是一样的,只不过两者是反过来进行操作的啦。我们可以从图中来看看哦。

在这里插入图片描述

代码实现:

class Solution:
    def delete(self , A: ListNode,  k:int) -> ListNode:
        #k表示删除位置
        a = A
        for i in range(k):
            a = a.next
        #确保不是插入末尾
        if b:
            a.next = C.next
        else:
            a.next = None
        return A

反转链表节点

反转链表可是我们在面试中刷题遇见的最多的一道题嘿嘿嘿。在牛客排行榜中公认的第一名

在这里插入图片描述

首先来给大家看看反转链表的具体图:

在这里插入图片描述

我们先不在这里讲具体的代码实现嘿嘿,我们放到下面来做经典的反转链表。我们就能感受到他的魅力了哈哈哈。

在这里插入图片描述

习题练习(附练习题)

首先来一道经典的反转链表嘿嘿嘿

反转链表

题目:

在这里插入图片描述

思路:

(1)定义两个指针: pre 和 cur ;pre 在前 cur 在后。

(2)每次让 pre 的 next 指向 cur ,实现一次局部反转

(3)局部反转完成之后, pre 和 cur 同时往前移动一个位置

(4)循环上述过程,直至 pre 到达链表尾部

图解:

在这里插入图片描述

代码的实现:

Java:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    
    
    public ListNode ReverseList(ListNode head) {
    
    
        //pre指针:用来指向反转后的节点,初始化为null
        ListNode pre = null;
         //当前节点指针
        ListNode cur = head;
        //循环迭代
        while(cur!=null){
    
    
            //Cur_next 节点,永远指向当前节点cur的下一个节点
            ListNode Cur_next = cur.next;
            //反转的关键:当前的节点指向其前一个节点(注意这不是双向链表,没有前驱指针)
            cur.next = pre;
            //更新pre
            pre = cur;
            //更新当前节点指针
            cur = Cur_next ;
        }
        //为什么返回pre?因为pre是反转之后的头节点
        return pre;
    }
}

Python:

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        # write code here
        # 空链表或只有一个节点得链表
        if not pHead&nbs***bsp;not pHead.next:
            return pHead
        cur = pHead
        pre = None
        # 循环移动curent和pre,移动过程中反转 curent.next=pre
        while cur:
            next_node = cur.next
            cur.next = pre
            pre = cur
            cur = next_node
        return pre

其他思路:

递归:

  • 使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 ans
  • 此后,每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。
  • 同时让当前结点的 next 指针指向NULL ,从而实现从链表尾部开始的局部反转
  • 当递归函数全部出栈后,链表反转完成。

栈:

可以先把列表中的所有数据放入到栈中,然后在逐一出栈即可成功!

在这里插入图片描述

链表内指定区间反转

题目:

在这里插入图片描述

思路:

三指针:

  • 判断参数head是否为空,若空则直接返回;判断m是否等于n,相等则无需进行操作,直接返回;

  • 创建一个头结点head_node,以便对整个链表的操作实现统一;

  • 指针begin初始化为头节点head_node,begin将作为待反转链表第一个结点的前驱指针。然后通过变量i(初始化为0)记录当前遍历的结点个数,顺序遍历链表的前m-1个结点时,begin不断向前移动,最终指向第m-1个结点;

  • 指针start指向待反转链表的第一个结点,初始化为begin->next;指针finish指向待反转链表的最后一个结点,初始化为begin。然后在顺序遍历到第n个结点的过程中,finish不断向前移动,直到指向第n个结点。

  • 指针end指向待反转链表的最后一个结点的下一个位置;

  • 采用三指针法反转链表:

    • 初始时,指针p指向start,指针q指向start->next;
    • 反转过程: tmp = q->next; q->next = p;p = q;q = tmp
    • 结束条件:q == finish
  • 最后一步:将反转后的链表与其余begin之前的链表和end之后的链表进行连接:

begin->next = finish; start->next = end;

图解:

代码实现:

Java:

ListNode* reverseBetween(ListNode* head, int m, int n) 
{
    
    
        if(!head || m == n)
            return head;
        ListNode *head_node = new ListNode(-1);
        head_node->next = head;         // 创建链表的头结点
        
        ListNode *begin = head_node;    // begin指向待反转链表的前一个结点
        int i = 0;
        while( i <= m -2) {
    
                 // 循环结束, begin指向第m-1个结点
            begin = begin->next;
            ++i;
        }
        ListNode *start = begin->next;
        ListNode *finish = begin;
        while( i < n) {
    
                    // 循环结束, finish指向第n个结点
            finish = finish->next;
            ++i;
        }
        ListNode *end = finish->next;  // end指向第待反转链表的下一个结点
        
        ListNode *p = start;
        ListNode *q = start->next;
        begin->next = nullptr;
        finish->next = nullptr;
        
        while(p != finish) {
    
    
            ListNode *tmp = q->next;
            q->next = p;
            p = q;
            q = tmp;
        }
        
        begin->next = finish;
        start->next = end;
        
        head = head_node->next;
        delete head_node;
        return head;
}

Python:

# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
#
# 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
#
# 
# @param head ListNode类 
# @param m int整型 
# @param n int整型 
# @return ListNode类
#
class Solution:
    def reverseBetween(self , head: ListNode, m: int, n: int) -> ListNode:
        res = ListNode(-1)
        res.next = head
        pre = res
        cur = head
        for i in range(1, m):
            pre = cur
            cur = cur.next
        for j in range(m, n):
            tmp = cur.next
            cur.next = tmp.next
            tmp.next = pre.next
            pre.next = tmp
        return res.next

链表中的节点每k个一组翻转

题目:

在这里插入图片描述

思路:

本题虽然是链表习题,但是也结合了其他的数据结构。在本题就可以使用的方法来进行一个每K个节点的反转。

  • 1、首先遍历链表k个结点入栈,若k大于链表的长度则直接返回链表不翻转
  • 2、栈内结点出栈(翻转)
  • 3、判断剩下的链表个数够不够k个(少于k个不需要反转,大于k个重复 1、2步骤)
  • 4、将已翻转的部分与剩下的链表连接起来

代码实现:

Python:

class Solution:
    def reverseKGroup(self , head , k ):
        # write code here
        # 用于链表头元素
        Phead = ListNode(None)
        p = Phead
        while True:
            count = k
            stack = []
            tmp = head
            # 进栈
            while count and tmp:
                stack.append(tmp)
                tmp = tmp.next
                count -= 1
            # 跳出上面循环,tmp是第k+1的元素
            # 如果循环结束,count不为0,则代表不足k个元素
            if count:
                p.next = head
                break
            # 对k个元素进行反转
            # 出栈
            while stack:
                p.next = stack.pop()
                p = p.next
            # 与剩下链表链接起来
            p.next = tmp
            head = tmp
        return Phead.next

由于时间原因本题没有Java版本的题解,但是后期博主也是会慢慢的补上来的啦。接下来就是最激动的环节啦!!!

习题的系统刷题:

题目
合并两个排序的链表
合并k个已排序的链表
判断链表中是否有环
链表中环的入口结点
链表中倒数最后k个结点
删除链表的倒数第n个节点
两个链表的第一个公共结点
链表相加(二)
链表的奇偶重排

给他干!!!

在这里插入图片描述

其他算法模板文章推荐

【算法模板】BFS秒杀模板—附练习题(开启大海贼时代)
【算法模板】DFS秒杀模板—附练习题(阳光号启航)
【算法模板】动态规划(基础DP篇)
【算法模板】动态规划(基础背包篇)—附习题

好了那今天的文章就到这里了,现在是半夜1.30。我得赶紧睡了(狗命要紧),溜了溜了。

猜你喜欢

转载自blog.csdn.net/m0_54355125/article/details/125796026