【Java】数据结构刷题笔记——链表(更新中)

牛客网面试必刷top101习题笔记——链表部分

目录

牛客网面试必刷top101习题笔记——链表部分

一、反转链表

1.题目描述

2.暴力解法 

(1).解题思路

(2).代码实现

​3、使用栈

(1).解题思路

(2).代码实现

4、递归

(1).解题思路

 (2).代码实现



第一题:反转链表

题目描述

给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1,长度为n,反转该链表后,返回新链表的表头。

数据范围:0≤n≤1000

要求:空间复杂度O(1) ,时间复杂度O(n) 。

如当输入链表{1,2,3}时,

经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。

以上转换过程如下图所示:

【Java】数据结构刷题笔记——链表(更新中)_第1张图片

java语言下Node的表示方法: 

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

    ListNode(int val) {
        this.val = val;
    }

1.遍历解法 

(1).解题思路

        从头结点开始对链表的每一个结点依次进行处理,将当前所处理结点currentNode的指针域指向其前驱结点preNode。同时由于改变currentNode的指针域会导致其丢失后继结点地址,因而需要在处理过程中创建中介结点tempNode对currentNode的后继结点进行记录。

        具体流程如下:

        1.明确之前提到的各个变量的内容,将头结点作为currentNode,记录相应的preNode和tempNode的位置。     

          

        2.将currentNode的next指向preNode,并相应地后移变量所指向的结点,令preNode=currentNode,并令currentNode=tempNode。        

        

         3.以此类推,处理至最后一个结点,最终状态如下。     

        

(2).代码实现


public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode preNode = null;        //当前所处理结点的前驱结点
        ListNode currentNode = head;    //当前所处理结点
        while(currentNode!=null)        //判定条件:当前节点不为null,避免空链表
        {
            //改变当前节点指针域指向对象
            ListNode tempNode = currentNode.next;
            currentNode.next = preNode;
            //继续处理下一个结点
            preNode = currentNode;      
            currentNode = tempNode;
        };
        //该循环结束判定条件下,结束时preNode即为倒转之后链表的头结点
        return preNode;
    }
}

 运行时间23ms,占用内存10276KB。


​2、使用栈

(1).解题思路

        利用栈后入先出的特性,将整个链表的结点从头压入栈中,再依次取出,拼接为新的链表。

(2).代码实现

import java.util.Stack;
public class Solution {
    public ListNode ReverseList(ListNode head) {
        Stack stack = new Stack<>();
        //将链表中的结点依次压入栈中
        while(head!=null)
        {
            stack.push(head);
            head = head.next;               //后移指针
        }
        if(stack.isEmpty())                 //判定空链表
            return null;
        //确定新链表的头结点
        ListNode currentNode = stack.pop();     
        ListNode headNode = currentNode;
        //栈中结点依次出栈,并且将结点依次连接
        while(!stack.isEmpty())
        {
            currentNode.next = stack.pop();
            currentNode = currentNode.next;
        }
        currentNode.next = null;            //将尾结点指针域指向null,避免构成环
        return headNode;
    }
}

运行时间31ms,占用内存10144KB。


3、递归

(1).解题思路

        使用递归函数的方法在每一层调用中对相应结点的指针域进行处理,将当前结点head的后继结点postNode的指针域指向head,设定递归终止条件为当前处理结点为尾结点,即可把尾结点作为新链表的头结点在递归调用中不断向上传递。

        设该链表长度为n,从第n次递归调用开始反推流程。

        具体流程如下:

        1.第n次调用时,当前节点为尾结点,触发终止条件,将当前处理结点作为新链表头结点直接向上一层传递。

                

          2.第n-1次调用时,由于递归向上传递,当前处理结点head前移。将postNode的指针域指向当前处理结点head,同时将head的指针域置空,避免形成环。  

        

         3.第n-2次调用时,重复第n-1次调用的工作,改变postNode的指针域指向当前结点head,并置空head指针域。继续向上传递新链表头结点newHead,以此类推。

         

          4.重复上述步骤直至递归至第一次函数调用,改变postNode的指针域使其指向当前节点head,置空当前结点head的指针域,所得新链表如下所示。

        

 (2).代码实现

public class Solution {
    public ListNode ReverseList(ListNode head) {
        //确定链表为空或者当前处理结点为尾结点
        if(head == null || head.next == null)      
            return head;
        ListNode postNode = head.next;             //处理当前结点的后继结点

        ListNode newHead = ReverseList(postNode);  //递归返回尾结点,即新链表的头结点
        postNode.next = head;                      //将后继结点指针域指向当前结点
        head.next = null;                          //当前结点指针域置空,避免成环
        return newHead;
}
}

运行时间23ms,占用内存10284KB。


第二题、链表指定区间内反转

1.题目描述

描述

将一个节点数为 size 链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n),空间复杂度 O(1)。
例如:
给出的链表为 1→2→3→4→5→NULL, m=2,n=4,
返回 1→4→3→2→5→NULL.
 

数据范围: 链表长度 0

要求:时间复杂度 O(n) ,空间复杂度 O(n)

进阶:时间复杂度 O(n),空间复杂度 O(1)

2. 遍历解法

(1).解题思路

        整体思路为将m位置到n位置之间的结点作为子链表一齐取出,将其顺序反转后拼接回整个链表中。

        以题例为例,整体步骤如下:

【Java】数据结构刷题笔记——链表(更新中)_第2张图片

  

        1.切断m位置结点与其前驱结点的联系,同时切断n位置结点与其后继结点的联系,将m位置结点的原前驱结点与n位置结点相连,同时将m位置结点与n位置结点的原后置结点相连。

【Java】数据结构刷题笔记——链表(更新中)_第3张图片 

        2.通过遍历法将m位置到n位置的结点反转,最后连接m结点与n结点原后继结点。

【Java】数据结构刷题笔记——链表(更新中)_第4张图片

(2).代码实现

import java.util.*;
public class Solution {

    public ListNode reverseBetween (ListNode head, int m, int n) {
        //分别找到m位置和n位置的结点,并记录其前驱与后继结点。
        ListNode mNode = head;
        ListNode premNode = null;
        ListNode nNode = null;
        ListNode postnNode = null;
        //找m位置结点,同时记录m位置结点的前驱结点
        for(int i = 0; i < m - 1; i++){
            premNode = mNode;
            mNode = mNode.next;
        }
        nNode = mNode;
        //找n位置结点,同时令
        for(int i = 0; i < n - m; i ++){
            nNode = nNode.next;
        }
        //开始区间内链表的遍历式反转。
        //连接m位置结点的原前驱结点与n位置结点。
        if(m!=1)
            premNode.next = nNode; 
        ListNode postNode = null;
        ListNode preNode = mNode;
        ListNode currentNode = mNode.next;
        //连接m位置结点与n位置的原后继结点。
        mNode.next = nNode.next;
        //开始反转。
        for(int i = 0 ; i < n - m ; i++){
            ListNode tempNode = currentNode;
            currentNode = currentNode.next;
            tempNode.next = preNode;
            preNode = tempNode; 
        }
        //特殊情况,m为1时区间左端结点无前驱结点。
        if(m==1)
            head = nNode;
        return head;
    }
}

运行时间22ms,占用内存10020KB。


第三题、链表中的结点每k个一组翻转

1.题目描述

描述

将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。

数据范围:0≤n≤2000 , 1≤k≤2000 ,链表中每个元素都满足 0≤val≤1000
要求空间复杂度 O(1),时间复杂度 O(n)

例如:

给定的链表是 1→2→3→4→5

对于k=2 , 你应该返回 2→1→4→3→5

对于k=3 , 你应该返回 3→2→1→4→5

2.暴力解法

(1).解题思路

        以k=3为例,处理一个n=8的链表。

         最终所得到的链表结构如下,将每k个结点划为一个区间,则区间最左侧端点与下一区间最右侧端点相连,当剩余结点不足k时则会直接与最右侧端点的原后继结点相连。除此之外均在区间内部进行反转处理即可。

        

(2).代码实现

import java.util.*;
public class Solution {
    public ListNode reverseKGroup (ListNode head, int k) {
        ListNode leftNode = head;
        ListNode rightNode = null;
        ListNode currentNode = leftNode;
        //前一区间的左端点结点需要与后一区间的右端点结点相连
        ListNode preLeftNode = null;
        //先测n
        ListNode countNode = head;
        int n = 0;
        while(countNode!=null){
            countNode = countNode.next;
            n++;
        }
        int epoches = n / k ;
        for (int i = 0; i < epoches;i++){
            //区间内部操作

            // currentNode = leftNode;
            ListNode preNode = null;
            for(int j = 0; j < k;j++){
                ListNode tempNode = currentNode.next;
                currentNode.next = preNode;
                preNode = currentNode;
                currentNode = tempNode;
            }
            //更新端点结点信息
            rightNode = preNode;
            if(preLeftNode != null)
                preLeftNode.next = rightNode;
            preLeftNode = leftNode;
            leftNode = currentNode;
            if(i == 0)
                head = rightNode;
        }
        if (leftNode != null && preLeftNode!=null)
            preLeftNode.next = leftNode;
        return head;
    }
}

 花费时间33ms,占用内存10272KB。

你可能感兴趣的:(数据结构,JAVA,刷题,数据结构,java,链表,学习)