链表—中等难度(java实现)
链表中环的入口节点
题目描述
对于一个给定的链表,返回环的入口节点,如果没有环,返回null
拓展:
你能给出不利用额外空间的解法么?
(图源牛客LPL006题解)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
//设在进入环前路程为a,二指针在距环起点为b位置相遇,环总长(b+c)
//即慢指针行 a+b , 快指针行a+b+n(b+c)
//快指针速度为慢指针2倍,则 2*(a+b)=a+b+n*(b+c)
//推出 a = (n-1)(b+c) + c
//此时将速度相同的两个指针分别放在开头和环相遇位置,二者相遇在环起点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
ListNode tmp = head;
while(tmp != slow){
tmp = tmp.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
删除链表的倒数第n个节点
题目描述
给定一个链表,删除链表的倒数第n个节点并返回链表的头指针
例如,
给出的链表为:1->2->3->4->5, n= 2.
删除了链表的倒数第n个节点之后,链表变为1->2->3->5.
备注:
题目保证n一定是有效的
请给出请给出时间复杂度为\ O(n) O(n)的算法
题目已知给出的n一定是有效的,所以无需再考虑n小于0或者大于链表长度的情况
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @param n int整型
* @return ListNode类
*/
public ListNode removeNthFromEnd (ListNode head, int n) {
// write code here
ListNode fast = head;
ListNode slow = head;
for(int i = 0; i < n; i++)
fast = fast.next;
//若n等于链表的长度,则直接返回去掉头结点的链表
if(fast == null)
return head.next;
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
//1 2 3 4 5 6 7
slow.next = slow.next.next;
return head;
}
}
删除有序链表中重复出现的元素
题目描述
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
例如:
给出的链表为1→2→3→3→4→4→5, 返回1→2→5.
给出的链表为1→1→1→2→3, 返回2→3.
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode deleteDuplicates (ListNode head) {
// write code here
ListNode res = new ListNode(0);
res.next = head;
ListNode pre = res;
ListNode cur = head;
while(cur != null && cur.next != null){
if(cur.val != cur.next.val)
pre = cur;
else{
while(cur.next != null && cur.val == cur.next.val)
cur = cur.next;
pre.next = cur.next;
}
cur = cur.next;
}
return res.next;
}
}
链表的奇偶重排
题目描述
给定一个单链表,请设定一个函数,讲链表的奇数位节点和偶数位节点分别放在一起,重排后输出。
注意是节点的编号而非节点的数值。
示例1
输入:{1,2,3,4,5,6}
返回值:{1,3,5,2,4,6}
示例2
输入:{1,4,6,3,7}
返回值:{1,6,7,4,3}
说明:奇数节点有1,6,7,偶数节点有4,3。重排后为1,6,7,4,3
备注:链表长度不大于200000。每个数范围均在int内。
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* @param head ListNode类
* @return ListNode类
*/
public ListNode oddEvenList (ListNode head) {
// write code here
if(head == null || head.next == null)
return head;
ListNode evenhead = head.next;
ListNode odd = head;
ListNode even = head.next;
int i = 1;
while(even != null && even.next != null){
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
//奇数链与偶数链相连
odd.next = evenhead;
return head;
}
}
划分链表
题目描述
给出一个链表和一个值 ,以 为参照将链表划分成两部分,使所有小于 的节点都位于大于或等于 的节点之前。
两个部分之内的节点之间要保持的原始相对顺序。
例如:
给出1→4→3→2→5→2 和x=3,
返回 1→2→2→4→3→5.
示例1
输入:{1,4,3,2,5,2},3
返回值:{1,2,2,4,3,5}
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @param x int整型
* @return ListNode类
*/
public ListNode partition (ListNode head, int x) {
// write code here
if(head == null)
return null;
ListNode res1 = new ListNode(0);
ListNode res2 = new ListNode(0);
ListNode left = res1;
ListNode right = res2;
while(head != null){
if(head.val < x){
left.next = head;
left = left.next;
}
else{
right.next = head;
right = right.next;
}
head =head.next;
}
right.next = null;
left.next = res2.next;
return res1.next;
}
}
重排链表
题目描述
将给定的单链表 L: L0→L1→…→Ln-1→Ln
重新排序为:L0→Ln →L1→Ln-1→L2→Ln-2→…
要求使用原地算法,不能改变节点内部的值,需要对实际的节点进行交换。
例如:
对于给定的单链表{10,20,30,40},将其重新排序为{10,40,20,30}.
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public void reorderList(ListNode head) {
//要求使用原地算法,不能改变节点内部的值
//反转后半部分链表
if(head == null || head.next == null)
return;
//1、快慢指针找中点
ListNode fast = head;
ListNode slow = head;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode cur = slow.next;
slow.next = null; //拆分链表
ListNode pre = null; //后半部分链表的尾
while(cur != null){
ListNode nex = cur.next;
cur.next = pre;
pre = cur;
cur = nex;
}
cur = pre; //注意:在走完循环之后pre指向头结点,cur指向null
//合并两个链表
ListNode res = head;
while(res != null && cur != null){
ListNode ftmp = res.next;
ListNode ctmp = cur.next;
//1 2 3 4 5
//9 8 7 6
res.next = cur;
res = ftmp;
cur.next = res;
cur = ctmp;
}
}
}
两个链表生成相加链表
题目描述
假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
示例1
输入:[9,3,7],[6,3]
返回值:{1,0,0,0}
备注:
1 ≤ n,m ≤ 10^6
0 ≤ ai,bi ≤ 9
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head1 ListNode类
* @param head2 ListNode类
* @return ListNode类
*/
public ListNode addInList (ListNode head1, ListNode head2) {
// write code here
if(head1 == null) return head2;
if(head2 == null) return head1;
//方法一利用栈
//方法二反转链表
Stack<Integer> s1 = new Stack<>();
Stack<Integer> s2 = new Stack<>();
while(head1 != null){
s1.push(head1.val);
head1 = head1.next;
}
while(head2 != null){
s2.push(head2.val);
head2 = head2.next;
}
ListNode pre = null;
ListNode cur = null;
int n1 = 0;
int n2 = 0;
int cu = 0; //本位
int cp = 0; //进位
while(!s1.isEmpty() || !s2.isEmpty()){
n1 = s1.isEmpty() ? 0 : s1.pop();
n2 = s2.isEmpty() ? 0 : s2.pop();
cu = (cp + n1 + n2) % 10;
cp = (cp + n1 + n2) / 10;
pre = cur;
cur = new ListNode(cu);
cur.next = pre; //注意最后链表的顺序
}
if(cp != 0){ //两表遍历完,还有进位
pre = cur;
cur = new ListNode(cp);
cur.next = pre;
}
return cur;
}
}
链表指定区间反转
题目描述
将一个链表 m 位置到 n 位置之间的区间反转,要求时间复杂度 O(n) ,空间复杂度 O(1) 。
例如:
给出的链表为1→2→3→4→5→NULL, m = 2,n = 4,
返回1→4→3→2→5→NULL.
注意:
给出的 满足以下条件:
链表长度1≤m≤n≤链表长度
示例1
输入:{1,2,3,4,5},2,4
返回值:{1,4,3,2,5}
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* }
*/
public class Solution {
/**
*
* @param head ListNode类
* @param m int整型
* @param n int整型
* @return ListNode类
*/
public ListNode reverseBetween (ListNode head, int m, int n) {
// write code here
ListNode res = new ListNode(0);
res.next = head;
ListNode cur = head;
ListNode pre = res;
for(int i = 1; i < m; i++){
pre = cur;
cur = cur.next;
}
//1 2 3 4 5 6 7
//1 3 2 4 5 6 7
//1 4 3 2 5 6 7
/*
不妨拿出四本书,摞成一摞(自上而下为 A B C D),要让这四本书的位置完全颠倒过来(即自上而下为 D C B A):
盯住书A,每次操作把A下面的那本书放到最上面
初始位置:自上而下为 A B C B
第一次操作后:自上而下为 B A C D
第二次操作后:自上而下为 C B A D
第三次操作后:自上而下为 D C B A
*/
for(int i = 0; i < n - m; i++){
ListNode tmp = cur.next;
cur.next = tmp.next;
tmp.next = pre.next;
pre.next = tmp;
}
return res.next;
}
}