如果有错的还请各位大佬指出呀
有些是copy的还望不要介意
本人只做学习记录
题目描述:
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的深拷贝。深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
//记录每个节点对应新创建的节点
Map<Node,Node> map = new HashMap<>();
public Node copyRandomList(Node head) {
if(head==null){
return null;
}
if(!map.containsKey(head)){
//map中没有便新创建
Node newNode = new Node(head.val);
map.put(head,newNode);
newNode.next = copyRandomList(head.next);
newNode.random = copyRandomList(head.random);
}
//因为一个节点可能被多个节点指向,所以为了防止重复拷贝,可以直接从哈希表中取出拷贝后的节点的指针
return map.get(head);
}
}
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null){
return null;
}
//复制节点:A->A1->B->B1
Node cur = head;
while(cur != null){
Node next = cur.next; //B
cur.next = new Node(cur.val); //A1
cur.next.next = next;
cur = next;
}
//复制随机节点
cur = head;
while(cur != null){
Node curNew = cur.next;//A1
A->A1->B->B1->C->C1 比如A.random->c 让A1.random->C.next 也就是C1
curNew.random = cur.random == null ? null : cur.random.next;
cur = cur.next.next;//B
}
//拆分,比如把 A->A1->B->B1拆分成 A->B和A1->B1
Node headNew = head.next;
cur = head;
Node curNew = head.next;
while(cur != null){
cur.next = cur.next.next;
cur = cur.next;
curNew.next = cur == null ? null : cur.next;
curNew = curNew.next;
}
return headNew;
}
}
题目描述:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
哈希表
时间O(n)空间O(n)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while(head != null){
if(!set.add(head)){
return true;
}
head = head.next;
}
return false;
}
}
进阶:你能用 O(1)
(即,常量)内存解决此问题吗?
快慢双指针
时间O(n)空间O(1)
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while(slow != fast){
if(fast == null || fast.next == null){
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
题目描述:
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
输入:head = [4,2,1,3]
输出:[1,2,3,4]
自顶向下归并排序
时间O(nlogn)空间O(logn)
/**
* 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 sortList(ListNode head) {
return sort(head,null);
}
private ListNode sort(ListNode head,ListNode tail){
if(head == null){
return head;
}
//此时链表只有一个节点了
if(head.next == tail){
//
head.next = null;
return head;
}
ListNode slow = head,fast = head;
while(fast != tail){
//当fast到达尾部时,slow为中间节点
slow = slow.next;
fast = fast.next;
if(fast != tail){
fast = fast.next;//fast移动两个位置
}
}
ListNode mid = slow;
ListNode list1 = sort(head,mid);
ListNode list2 = sort(mid,tail);
ListNode sorted = merge(list1,list2);
return sorted;
}
private ListNode merge(ListNode list1,ListNode list2){
ListNode sortHead = new ListNode(0);
ListNode temp = sortHead,temp1 = list1,temp2 = list2;
while(temp1!=null && temp2!=null){
if(temp1.val <= temp2.val){
temp.next = temp1;
temp1 = temp1.next;
}else{
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if(temp1!=null){
temp.next = temp1;
}else if(temp2 != null){
temp.next = temp2;
}
return sortHead.next;
}
}
自底向上归并*
时间O(nlogn)空间O(1)
(这种本人还未搞明白所以是直接复制一位大佬的)
class Solution {
// 自底向上归并排序
public ListNode sortList(ListNode head) {
if(head == null){
return head;
}
// 1. 首先从头向后遍历,统计链表长度
int length = 0; // 用于统计链表长度
ListNode node = head;
while(node != null){
length++;
node = node.next;
}
// 2. 初始化 引入dummynode
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
// 3. 每次将链表拆分成若干个长度为subLen的子链表 , 并按照每两个子链表一组进行合并
for(int subLen = 1;subLen < length;subLen <<= 1){ // subLen每次左移一位(即sublen = sublen*2) PS:位运算对CPU来说效率更高
ListNode prev = dummyHead;
ListNode curr = dummyHead.next; // curr用于记录拆分链表的位置
while(curr != null){ // 如果链表没有被拆完
// 3.1 拆分subLen长度的链表1
ListNode head_1 = curr; // 第一个链表的头 即 curr初始的位置
for(int i = 1; i < subLen && curr != null && curr.next != null; i++){ // 拆分出长度为subLen的链表1
curr = curr.next;
}
// 3.2 拆分subLen长度的链表2
ListNode head_2 = curr.next; // 第二个链表的头 即 链表1尾部的下一个位置
curr.next = null; // 断开第一个链表和第二个链表的链接
curr = head_2; // 第二个链表头 重新赋值给curr
for(int i = 1;i < subLen && curr != null && curr.next != null;i++){ // 再拆分出长度为subLen的链表2
curr = curr.next;
}
// 3.3 再次断开 第二个链表最后的next的链接
ListNode next = null;
if(curr != null){
next = curr.next; // next用于记录 拆分完两个链表的结束位置
curr.next = null; // 断开连接
}
// 3.4 合并两个subLen长度的有序链表
ListNode merged = mergeTwoLists(head_1,head_2);
prev.next = merged; // prev.next 指向排好序链表的头
while(prev.next != null){ // while循环 将prev移动到 subLen*2 的位置后去
prev = prev.next;
}
curr = next; // next用于记录 拆分完两个链表的结束位置
}
}
// 返回新排好序的链表
return dummyHead.next;
}
// 此处是Leetcode21 --> 合并两个有序链表
public ListNode mergeTwoLists(ListNode l1,ListNode l2){
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
while(l1 != null && l2!= null){ // 退出循环的条件是走完了其中一个链表
// 判断l1 和 l2大小
if (l1.val < l2.val){
// l1 小 , curr指向l1
curr.next = l1;
l1 = l1.next; // l1 向后走一位
}else{
// l2 小 , curr指向l2
curr.next = l2;
l2 = l2.next; // l2向后走一位
}
curr = curr.next; // curr后移一位
}
// 退出while循环之后,比较哪个链表剩下长度更长,直接拼接在排序链表末尾
if(l1 == null) curr.next = l2;
if(l2 == null) curr.next = l1;
// 最后返回合并后有序的链表
return dummy.next;
}
}
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。
代码实现
时间O(n)空间O(1)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headA == null){
return null;
}
ListNode listA = headA;
ListNode listB = headB;
while(listA != listB){
//若该链表的节点移动到null时还没找到则指向另一个链表找
listA = listA == null? headB : listA.next;
listB = listB == null? headA : listB.next;
}
return listA;
}
}
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
迭代
时间O(n)空间O(1)
/**
* 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 reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
//将指向下一个节点的指针指向上一个节点
while(cur != null){
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
}
递归
时间O(n)空间O(n)
/**
* 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 reverseList(ListNode head) {
//终止条件
if(head == null || head.next == null){
return head;
}
ListNode cur = reverseList(head.next);
//当前节点的下一个节点指向当前节点
head.next.next = head;
//原本当前节点指向下一节点的指针指向null
//使x->y改为x<-y,将->指向null
head.next = null;
return cur;
}
}
题目描述:
给你一个单链表的头节点 head
,请你判断该链表是否为回文链表。如果是,返回 true
;否则,返回 false
。
示例:
输入:head = [1,2,2,1]
输出:true
数组
时间O(n)空间O(n)
/**
* 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 boolean isPalindrome(ListNode head) {
//将链表的值保存在数组列表中
List<Integer> list = new ArrayList<>();
ListNode cur = head;
while(cur != null){
list.add(cur.val);
cur = cur.next;
}
int left = 0;
int right = list.size()-1;
while(left<right){
if(list.get(left) != list.get(right)){
return false;
}
left++;
right--;
}
return true;
}
}
/**
* 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 boolean isPalindrome(ListNode head) {
ListNode fast = head,slow = head;
//通过快慢指针找到中点
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
//若fast不为null,则为奇数
if(fast != null){
slow = slow.next;
}
//反转后半部分链表
slow = reverse(slow);
//快指针返回到头节点,和慢指针同步移动进行比较
fast = head;
while(slow!=null){
if(fast.val != slow.val){
return false;
}
fast = fast.next;
slow = slow.next;
}
return true;
}
private ListNode reverse(ListNode head){
ListNode prev = null;
while(head!=null){
ListNode next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
}
}
/**
* 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 {
ListNode temp;
public boolean isPalindrome(ListNode head) {
temp = head;
return check(head);
}
public boolean check(ListNode head){
if(head == null){
return true;
}
/**链表的逆序打印
*private void printListNode(ListNode head) {
* if (head == null)
* return;
* printListNode(head.next);
* System.out.println(head.val);
*}
*/
//通过逆序遍历和正序遍历进行比较
boolean res = check(head.next) && (temp.val == head.val);
temp = temp.next;
return res;
}
}
/**
* 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 boolean isPalindrome(ListNode head) {
if(head==null){
return true;
}
ListNode temp = head;
Stack<Integer> stack = new Stack();
int len = 0;
//把链表的值存放在栈中
while(temp != null){
stack.push(temp.val);
temp = temp.next;
len++;
}
//链表长度除以2
len>>=1;
//出一半的栈
while(len-->=0){
if(head.val != stack.pop()){
return false;
}
head = head.next;
}
return true;
}
}
题目描述:
有一个单链表的 head,我们想删除它其中的一个节点 node。
给你一个需要删除的节点 node 。你将 无法访问 第一个节点 head。
链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。
删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
给定节点的值不应该存在于链表中。
链表中的节点数应该减少 1。
node 前面的所有值顺序相同。
node 后面的所有值顺序相同。
自定义测试:
对于输入,你应该提供整个链表 head 和要给出的节点 node。node 不应该是链表的最后一个节点,而应该是链表中的一个实际节点。
我们将构建链表,并将节点传递给你的函数。
输出将是调用你函数后的整个链表。
示例:
输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:指定链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9
与下一个节点交换
时间O(1)空间O(1)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
//将该节点的值改为下一个节点的值
//4->5->1->9 => 4->1->1->9
node.val = node.next.val;
//将该节点的下一个节点删除
//4->1->9
node.next = node.next.next;
}
}
题目描述:
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
示例:
输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
分离奇偶节点后合并
时间O(n)空间O(1)
/**
* 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 oddEvenList(ListNode head) {
if(head == null){
return head;
}
ListNode evenHead = head.next;
//头节点为奇数链的头节点
ListNode odd = head,even = evenHead;
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;
}
}