链表算法问题总结

链表算法问题笔记

反转链表1

题目:

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

限制:

0 <= 节点个数 <= 5000

代码:

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null) return head;
        //定义一个新的辅助头节点
        ListNode newHead = new ListNode(0);
        //temp指针:指向头节点
        ListNode temp = head;
        while(temp != null) {
            //获取当前节点的next域
            ListNode next = temp.next;
            temp.next = newHead.next;
            newHead.next = temp;
            temp = next;
        }
        return newHead.next;

    }
}

反转链表2

题目:

给你单链表的头指针 head 和两个整数 leftright ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]

示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]

提示:

  • 链表中节点数目为 n
  • 1 <= n <= 500
  • -500 <= Node.val <= 500
  • 1 <= left <= right <= n

题解:

在这里插入图片描述
在这里插入图片描述

代码:

class Solution {
    //优化代码
    public ListNode reverseBetween(ListNode head, int m, int n) {
        if (head == null || head.next == null) {
            return head;
        }
        if (m == n) {
            return head;
        }
        ListNode newHead = new ListNode(0);
        //定义一个辅助指针变量,指向newHead节点
        ListNode p = newHead;
        //定义temp指针指向原头节点
        ListNode temp = head;
        //定义开始进行反转的节点,即反转刚结束的链表最后一个节点
        ListNode last = null;
        //定义一个计数器
        int count = 1;
        while (temp != null) {
            //记录当前指针的next域
            ListNode next = temp.next;
            //将temp的next域指向p指针的next域
            temp.next = p.next;
            //p的next域指向temp
            p.next = temp;
            if (count < m || count > n) {
                //如果不在反转位置区间内,则直接将temp指向next域
                temp = next;
                //然后将p指向p的next域
                p = p.next;
            }else if (count == m) {
                //如果进入开始反转位置
                //需要记录last:当前的节点
                last = temp;
                //然后不移动p指针变量,只将temp指针指向它的next域
                temp = next;
            }else if (count == n) {
                //如果进入反转的最后位置
                //则反转以后,将p指针指向之前记录的开始反转节点
                p = last;
                //将temp指针指向它的next域
                temp = next;
            }else {
                //否则,只需要将temp指针指向它的next域
                temp = next;
            }
            //记得将计数器加1
            count++;
        }
        
        //最后返回newHead的next指针,即为反转m~n位置以后的头结点
        return newHead.next;
    }
}

倒数第k个节点问题

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

代码:

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //我们定义两个指针变量p1和p2指向链表的头节点head
        ListNode p1 = head;
        ListNode p2 = head;
        //首先让p1指针走k步
        for(int i = 0; i < k; i++) {
            p1 = p1.next;
        }
        //然后将p1,p2同时向前移动,到p1指针指向null停止
        while(p1 != null) {
            p1 = p1.next;
            p2 = p2.next;
        }
		//此时p2指向的即为倒数第k个节点
        return p2;
    }
}

删除排序链表的重复项1

题目:

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次

返回同样按升序排列的结果链表。

示例 1:

在这里插入图片描述

输入:head = [1,1,2]
输出:[1,2]

示例 2:

在这里插入图片描述

输入:head = [1,1,2,3,3]
输出:[1,2,3]

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

代码:

class Solution {
   public ListNode deleteDuplicates(ListNode head) {
        //如果head为null或者head的next域为null[只有一个节点]
        if (head == null || head.next == null)
            //直接返回head节点:一定为不重复的
            return head;
        //定义一个辅助指针变量temp,指向head节点
        ListNode temp = head;
        //while循环条件:temp不为null
        while (temp != null) {
            //保存当前节点cur
            ListNode cur = temp;
            //如果temp的next域不为null,并且temp的val值和temp的next域的val值相同
            //指向跳过该next节点
            while (temp.next != null && temp.val == temp.next.val) {
                temp = temp.next;
            }
            //退出while循环时,此时temp指向最后一个相同的节点
            //将cur节点的next节点之前temp的next域,同时将temp指向下一个不同的val值的第一个节点,即cur节点的next节点
            cur.next = temp.next;
            temp = cur.next;
        }
        //最后返回头结点
        return head;
    }
}

删除排序链表的重复项2:

题目:

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

返回同样按升序排列的结果链表。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:

在这里插入图片描述

输入:head = [1,1,1,2,3]
输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

代码:

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        //没有节点或者只存在一个节点的时候,直接返回原head节点即可
        if (head == null || head.next == null) {
            return head;
        }
        //定义辅助指针变量head,以及新的头结点newHead
        ListNode temp = head;
        //定义一个新的辅助头节点
        ListNode newHead = new ListNode(0);
        //同时定义一个辅助变量pNode指向newHead
        ListNode pNode = newHead;
        while (temp != null) {
            //获取当前节点
            ListNode cur = temp;
            //跳过相同节点
            while (temp.next != null && temp.val == temp.next.val) {
                temp = temp.next;
            }
            //此时表示temp节点是唯一的,不重复的,我们将其添加到pNode后面
            if (cur == temp) {
                //首先获取next节点
                ListNode next = temp.next;
                //然后将temp节点的next域指向pNode的next域,即指向null
                temp.next = pNode.next;
                //接着将pNode的next域指向temp
                pNode.next = temp;
                //最后分别将temp,pNode指向向后移动一位
                temp = next;
                pNode = pNode.next;
            }else {
                //否则我们说此时的temp存在重复值,此时temp指向最后一个重复值,我们直接跳过该重复项
                temp = temp.next;
            }
        }
        //最后返回newHead的next域,即为删除重复项的新的链表的头节点
        return newHead.next;

    }
}

合并两个链表:

问题:

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

限制:

0 <= 链表长度 <= 1000

代码:

class Solution {
   //利用递归回溯法进行问题的求解
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //传入两个链表的两个头结点l1和l2  
        if (l1 == null) {
            return l2;
        }else if (l2 == null) {
            return l1;
        }else if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else  {//if (l1.val >= l2.val)
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }

合并多个链表:

问题:

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

在这里插入图片描述

代码:

//使用归并算法求解
public ListNode mergeKLists(ListNode[] lists) {
    //获取链表集合的头部和尾部
    int start = 0;
    int end = lists.length - 1;
    return merge(lists,start,end);
}

private ListNode merge(ListNode[] lists, int start, int end) {
    if (start == end) {
        return lists[start];
    }
    if (start > end) {
        return null;
    }
    //获取中值
    int mid = (start + end) >> 1;
    return mergeTwoList(merge(lists,start,mid),merge(lists,mid + 1,end));
}

private ListNode mergeTwoList(ListNode a, ListNode b) {
    if (a == null || b == null) {
        return a != null ? a : b;
    }
    //定义两个辅助变量aNode,bNode
    ListNode aNode = a;
    ListNode bNode = b;
    //定义一个头节点head以及辅助变量temp指向head
    ListNode head = new ListNode(0);
    ListNode temp = head;
    while (aNode != null && bNode != null) {
        if(aNode.val < bNode.val) {
            temp.next = aNode;
            aNode = aNode.next;
        }else {
            temp.next = bNode;
            bNode = bNode.next;
        }
        temp = temp.next;
    }
    temp.next = (aNode != null ? aNode : bNode);
    return head.next;
}

环形链表问题:

问题描述:

这道题有两个问题,一个是判断一个链表是否存在环?还有一个是要求找到链表中环的入口节点?

我们分别来看一下这两个问题:

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

**说明:**不允许修改给定的链表。

进阶:

  • 你是否可以使用 O(1) 空间解决此题?

示例 1:

在这里插入图片描述

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

在这里插入图片描述

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

在这里插入图片描述

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

问题分析:

上面的题目可以分支为两个问题:

第一个问题:是否存在环?

思路1是利用哈希表进行存储求解,但是速度较慢;思路2使用快慢指针进行求解;

第二个问题:找到该环形链表的入口节点?

思路1仍然是哈希表进行存储求解;思路2使用快慢指针所走步数之间的关系来求解该问题,我们重点来看一下第二种思路的思路分析:

在这里插入图片描述

问题1的java代码实现:
/*
    * 思路1:使用HashSet的唯一性:将每一个节点的next域添加到HashSet中,若存在相同的next域,则说明存在环
    * */
public boolean hasCycleByHash(ListNode head) {
    if (head == null || head.next == null) {
        //即表示该链表不存在节点或者只存在一个节点
        return false;
    }
    HashSet<ListNode> set = new HashSet<>();
    ListNode temp = head;
    while (temp != null) {
        if (!set.contains(temp)) {
            set.add(temp);
            temp = temp.next;
        }else {
            return true;
        }
    }
    //说明遍历到链表尾部,仍未发现环,则返回false
    return false;
}

/*
    * 思路2:使用快慢指针来完成环形链表的是否有环判断问题
    * 即定义一个指针first和指针second同时指向链表的头节点
    * 将first指向下一个节点,second指向下下个节点,若second能赶上first,则表示存在环
    * */
public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) {
        //即表示该链表不存在节点或者只存在一个节点
        return false;
    }
    //定义两个指针slow和fast:进行追赶
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {//只要first不等于second,就一直进行追赶
        if (fast == null || fast.next == null) {//若first为null或者first的下一个节点为null,说明不存在环形链表,则直接返回false
            return false;
        }
        //将first指向它的下一个节点,second指向它的下下个节点
        slow = slow.next;
        fast = fast.next.next;

    }
    //退出while循环,说明second追赶上了first
    return true;
}
问题2的java代码实现:
//思路1:hashset法:击败24%的用户
public ListNode detectCycle01(ListNode head) {
    //如果头节点为空或者只有一个节点:则表示无环
    if(head == null || head.next == null) {
        return null;
    }

    //定义一个HashSet
    HashSet<ListNode> set = new HashSet<>();
    ListNode temp = head;
    while (temp != null) {
        if (set.contains(temp)) {
            return temp;
        }else {
            set.add(temp);
            temp = temp.next;
        }
    }
    //while循环遍历结束,仍未发现环,则返回null
    return null;
}

//思路2:快慢指针法:快指针追赶上慢指针一定是在环的内部,并且两者的步数存在一定的关系:击败100%的用户
public ListNode detectCycle(ListNode head) {
    if (head == null || head.next == null) {
        return null;
    }
    //定义两个指针:slow和fast,以及一个存储最后返回的ListNode变量
    ListNode slow = head;
    ListNode fast = head;
    ListNode temp = head;
    while (true) {
        if (fast == null || fast.next == null) {//当遍历到最后链表的最后一个节点:即无环,返回null
            return null;
        }
        //将快指针指向next.next域,慢指针指向next域
        fast = fast.next.next;
        slow = slow.next;
        //若快慢指针相遇了,则直接break返回,此时slow和fast指针指向的都是相遇时的位置
        if (fast == slow) {
            //当两者相遇了
            break;
        }
    }
    //将temp指针和fast指针一起同步移动,再次相遇则是环的起始位置
    while (temp != fast) {
        temp = temp.next;
        fast = fast.next;
    }
    return temp;
}
JavaScript代码实现:

问题一:是否存在环

//方法一:使用map存储    节点:next节点   
//如果map中已经存在,说名存在环
var hasCycle = function(head) {
    let p = head;
    let map = new Map();
    while(p != null){
        if(!map.has(p)){
            map.set(p, p.next)
        }else{
            return true;
        }  
        p = p.next; 
    }
    return false;
}

//方法二:使用快慢指针
//如果存在环,快慢指针最终会相遇
var hasCycle = function(head) {
    if(!head || !head.next) return false;
//定义两个指针,分别指向头结点和头结点的下一个节点
//慢指针平移一步,快指针平移两步,如果存在环形链表,则一定会在某个时间点,慢指针和快指针在环内相遇
    let first = head;
    let second = head;
    while(first != second){
        //若一方的下一个节点为null,就不存在环形,直接返回false
        if( second == null || second.next == null){
            return false;
        }
        first = first.next;
        second = second.next.next;
    } 
    return true;
};

问题二:环的入口节点

在这里插入图片描述

方法一:使用map
var detectCycle = function(head) {
    let p = head;
    let map = new Map();
    while(p != null){
        if(!map.has(p)){
            map.set(p, p.next)
        }else{
            return p;
        }  
        p = p.next; 
    }
    return null;
}
方法二: 使用快慢指针
var detectCycle = function(head) {
     if(!head || !head.next) return null;
     if(head.next ==  head) return head;
//定义两个指针,分别指向头结点和头结点的下一个节点
//慢指针平移一步,快指针平移两步,如果存在环形链表,则一定会在某个时间点,慢指针和快指针在环内相遇
    let first = head;
    let second = head;
    let temp = head;
    while(true){
        //若一方的下一个节点为null,就不存在环形,直接返回false
        if(second == null || second.next == null){
            return null;
        }
        first = first.next;
        second = second.next.next;
        if(first == second) break;
    }
    //此时first和second是相同的位置
    while(first != temp){
        first = first.next;
        temp = temp.next;
    }
    return temp;   
};

逆序打印链表问题:

问题:

逆序打印一个单向链表

代码:

class Solution {
    public int[] reversePrint(ListNode head) {
        if (head == null) {
            //返回一个空的int数组
            return new int[0];
        }
        ListNode temp = head;
        Stack<Integer> stack = new Stack<>();
        while (true) {
            //将当前链表结点的val值压栈
            stack.push(temp.val);
            //判断链表是否遍历结束
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //while循环结束,则链表的结点val值按照链表的顺序依次压入栈
        //创建一个和栈的size相同大小的int数组:用来存储最后的从尾部到头部打印的结果
        int[] res = new int[stack.size()];
        int index = 0;
        while (stack.size() > 0) {
            res[index++] = stack.pop();
        }
        //最后将int数组返回
        return res;
    }
}

重排链表问题:

问题:

给定一个单链表 LL0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→LnL1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

给定链表 1->2->3->4, 重新排列为 1->4->2->3.

示例 2:

给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

代码:

class Solution {
    public void reorderList(ListNode head) {
        //如果头结点为null或者只有一个结点,直接返回
        if (head == null || head.next == null) return;
        //定义一个LinkedList:保存链表的所有结点
        LinkedList<ListNode> list = new LinkedList<>();
        //定义一个辅助指针变量temp指向头结点
        ListNode temp = head;
        //将链表的所有结点加入到LinkedList中
        while (true) {
            list.add(temp);
            if (temp.next == null) {
                break;
            }
            temp = temp.next;
        }
        //定义左指针left:指向头结点
        //定义右指针right:获取list的最后一个元素(即获取尾结点)
        ListNode left = head;
        ListNode right = list.removeLast();
        while (true) {
            //1.当遍历到left == right(结点的个数为奇数个时:直接将右结点的next置为null即可)
            //2.当遍历到left.next == right(结点的个数为偶数个时:直接将右结点的next域置为null即可)
            if (left == right || left.next == right) {
                right.next = null;
                break;
            }
            //记录左结点的值
            ListNode next = left.next;
            right.next = next;
            left.next = right;
            //更新left和right指针的值
            left = next;
            right = list.removeLast();
        }
        return;
    }
}

旋转链表问题:

问题:

给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]

示例 2:

在这里插入图片描述

输入:head = [0,1,2], k = 4
输出:[2,0,1]

提示:

  • 链表中节点的数目在范围 [0, 500]
  • -100 <= Node.val <= 100
  • 0 <= k <= 2 * 109

代码:

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        //如果该链表为空
        if(head == null) return head;
        //获取链表的长度和原始的尾节点temp
        int size = 1;
        ListNode temp = head;
        while(temp.next != null) {
            size++;
            temp = temp.next;
        } 
        //获取实际向后移动的步数
        int steps = k % size;
        //如果 steps = 0,说明没有平移
        if(steps == 0) return head;
        //否则我们的策略是先找到平移以后的链表的尾部节点和头部节点
        ListNode last = head;
        int time = size - steps - 1;
        while(time > 0) {
            last = last.next;
            time--;
        }
        //此时last指针指向旋转后的尾部节点
        //则旋转后的头部节点即为last的next域
        ListNode newHead = last.next;   
        //我们已经知道找到原始链表的尾部节点temp,让temp.next = head;last.next = null;最后返回newHead即可
        temp.next = head;
        last.next = null;
        return newHead;
    }
}

复制带有随机指针的链表问题:

问题:

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

示例 1:

在这里插入图片描述

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

在这里插入图片描述

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

在这里插入图片描述

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:

  • -10000 <= Node.val <= 10000
  • Node.random 为空(null)或指向链表中的节点。
  • 节点数目不超过 1000 。

问题分析:

使用HashMap法进行解决:

因为我们需要返回一个新的链表:其中的每一个节点都不是之前原链表的引用,而是一个新的节点地址;

所以我们定义一个HashMap :其中的key(键)为原链表的旧节点,value(值)为以旧节点的val值创建的新节点;

遍历原链表,完成新链表每个节点的创建

然后再遍历原链表,完成新链表的新节点的next域和random域的复制

其中:

新节点的next域:即为map中对应key(旧节点的next域)对应的value值

新节点的random域:即为map中对应key(旧节点的random域)对应的value值

最后,返回新的链表的头结点: return map.get(head);

在这里插入图片描述

java代码实现:
public Node copyRandomList(Node head) {
    //如果该链表为空,则直接返回本身即可
    if (head == null) return head;
    //定义一个Node作为key,Node作为value的map
    Map<Node,Node> map = new HashMap<>();
    //定义一个辅助变量head指向head节点
    Node temp = head;
    //(1)我们首先遍历原链表,将当前节点作为key,以当前节点的val创建的新节点作为value进行存储
    while (temp != null) {
        map.put(temp,new Node(temp.val));
        temp =temp.next;
    }
    //(2)然后再遍历原链表,更新新链表的节点(即上面的value)的next域和random域
    temp = head;
    while (temp != null) {
        map.get(temp).next = map.get(temp.next);
        map.get(temp).random = map.get(temp.random);
        temp = temp.next;
    }
    //(3)最后返回新的链表的头结点即可
    return map.get(head);
}
JavaScript代码实现:
//map存储 原节点:拷贝节点
var copyRandomList = function(head) {
    if(head == null || head.length == 0){
        return null;
    }
    let map = new Map();//存储原节点 : 当前节点
    let p = head;//指向原节点
    let newHead = new Node(0);
    let newP = newHead;//指向当前节点
    //设置next
    while(p){
        let tempNode = new Node(p.val);
        map.set(p, tempNode);
        newP.next = tempNode;
        p = p.next;
        newP = newP.next;
    }  
    //设置random
    p = head;
    newP = newHead.next;
    while(p){
        let random = map.get(p.random);
        newP.random = random;
        p = p.next;
        newP = newP.next;
    }
    return newHead.next;
};

回文链表问题:

问题:

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

代码:

class Solution {
     //双指针法
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null)
            return true;
        List<Integer> list = new ArrayList<>();
        ListNode temp = head;
        while (temp != null) {
            list.add(temp.val);
            temp = temp.next;
        }
        int left = 0;
        int right = list.size() - 1;
        while (left < right) {
            if (list.get(left) == list.get(right)) {
                left++;
                right--;
            }else {
                return false;
            }
        }
        return true;
    }
}

分隔链表问题:

问题:

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例 1:

在这里插入图片描述

输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200]
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

代码:

class Solution {
    public ListNode partition(ListNode head, int x) {
        if(head == null || head.next == null) return head;
        ListNode beforeHead = new ListNode(0);
        ListNode before = beforeHead;
        ListNode afterHead = new ListNode(0);
        ListNode after = afterHead;
        ListNode temp = head;
        while(temp != null) {
            if(temp.val < x) {
                before.next = temp;
                before = before.next;
            }else {
                after.next = temp;
                after = after.next;
            }
            temp = temp.next;
        }
        //此时我们将before和after链表进行连接
        after.next = null;
        before.next = afterHead.next;
        return beforeHead.next;
    }
}

两两交换链表中的节点问题

问题:

题目链接

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1] 

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

**进阶:**你能在不修改链表节点值的情况下解决这个问题吗?(也就是说,仅修改节点本身。)

代码:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if (head == null || head.next == null) return head;
        //定义一个前置节点
        ListNode pre = new ListNode(0);
        //将该前置节点的next域指向head节点
        pre.next = head;
        //定义一个临时辅助节点temp,指向pre
        ListNode temp = pre;
        //进行while循环:temp的next域不为null并且temp的next的next域不为null,表示temp后面至少存在两个节点
        while (temp.next != null && temp.next.next != null) {
            //首先获取temp的next域为当前节点cur
            ListNode cur = temp.next;
            //然后获取cur的下一个节点next
            ListNode next = temp.next.next;
            //1.将temp的next域指向next节点
            temp.next = next;
            //2.cur的next域指向next的next域
            cur.next = next.next;
            //3.next的next域指向cur当前节点
            next.next = cur;
            //4.修改temp节点的值为当前节点cur
            temp = cur;
        }
        //返回pre的next
        return pre.next;
    }
}

K 个一组翻转链表问题

问题:

题目链接

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

  • 你可以设计一个只使用常数额外空间的算法来解决此问题吗?
  • 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

在这里插入图片描述

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

在这里插入图片描述

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

示例 3:

输入:head = [1,2,3,4,5], k = 1
输出:[1,2,3,4,5]

示例 4:

输入:head = [1], k = 1
输出:[1]

提示:

  • 列表中节点的数量在范围 sz
  • 1 <= sz <= 5000
  • 0 <= Node.val <= 1000
  • 1 <= k <= sz
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        //定义一个新的头结点
        ListNode newHead = new ListNode(0);
        newHead.next = head;
        ListNode pre = newHead;
        
        while (head != null) {
            //定义一个辅助指针指向pre节点
            ListNode tail = pre;
            //我们查看剩余部分的长度是否大于等于k
            for (int i = 0; i < k; i++) {
                tail = tail.next;
                if (tail == null) {
                    //说明剩余长度小于k,我们选择直接返回
                    return newHead.next;
                }
            }
            //如果存在k个节点,可以进行翻转
            ListNode next = tail.next; //指向翻转以后的下一个节点
            //我们翻转此时的k个节点[head,tail]
            //返回的结果为翻转以后的第一个头结点为尾节点
            ListNode[] reverse = reverse(head,tail);
            //获取此时翻转以后的头节点和尾节点
            head = reverse[0];
            tail = reverse[1];
            //把子链表重新接回原链表
            pre.next = head;
            tail.next = next;
            //更新此时的pre节点和head节点
            pre = tail;
            head = next;
        }
        return newHead.next;
    }

    //知道链表的头结点和尾结点的反转链表方法
    private ListNode[] reverse(ListNode head, ListNode tail) {
        ListNode prev = tail.next;
        ListNode temp = head;
        while (prev != tail) {
            //获取next节点
            ListNode next = temp.next;
            temp.next = prev;
            prev = temp;
            temp = next;
        }
        return new ListNode[]{tail,head};
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值