链表
无法高效获取长度,无法根据偏移快速访问元素,是链表的两个劣势。 面试的时候经常碰见诸如寻找公共尾部入口、获取倒数第k个元素,获取中间位置的元素,判断链表是否存在环,判断环的长度等和长度与位置有关的问题。 这些问题都可以通过灵活运用**双指针 **来解决
刷题路线
题目 解法 题解 从尾到头打印链表 辅助栈;递归 两种 链表中倒数第k个结点 快慢指针;辅助list 双指针 反转链表 递归;迭代 两种 合并两个或k个有序链表 迭代;递归 两种 复杂链表的复制 原地修改(扩充&分离);辅助HashMap 两种 两个链表的第一个公共结点 双指针(你的名字);辅助HashMap 走对方的路相遇 链表中环的入口结点 双指针;HashMap 快慢指针环形相遇 删除排序链表中的重复元素 HashMap;双指针;递归 前两种 ;双指针+尾插法
解法 时间复杂度 空间复杂度 辅助栈 O(n) O(n) 递归 O(n) O(n)
class Solution {
public int [ ] reversePrint ( ListNode head) {
LinkedList < Integer > stack= new LinkedList < > ( ) ;
while ( head!= null ) {
stack. offerLast ( head. val) ;
head= head. next;
}
int [ ] res= new int [ stack. size ( ) ] ;
for ( int i= 0 ; i< res. length; i++ ) {
res[ i] = stack. pollLast ( ) ;
}
return res;
}
}
实际返回以该节点为开始的链表
class Solution {
public ListNode getKthFromEnd ( ListNode head, int k) {
if ( head== null || k== 0 ) return null ;
ListNode fast= head, slow= head;
for ( int i= 0 ; i< k; i++ ) {
if ( fast== null ) return null ;
fast= fast. next;
}
while ( fast!= null ) {
fast= fast. next;
slow= slow. next;
}
return slow;
}
}
类似题目 返回倒数第K个节点 返回值不同 只返回值即可 题解
class Solution {
public int kthToLast ( ListNode head, int k) {
ArrayList < Integer > list= new ArrayList < > ( ) ;
while ( head!= null ) {
list. add ( head. val) ;
head= head. next;
}
return list. get ( list. size ( ) - k) ;
}
}
解法 时间复杂度 空间复杂度 递归 O(n) O(n) 迭代 O(n) O(1)
class Solution {
public ListNode reverseList ( ListNode head) {
if ( head== null || head. next== null ) return head;
ListNode last= reverseList ( head. next) ;
head. next. next= head;
head. next= null ;
return last;
}
}
public ListNode reverseList ( ListNode head) {
ListNode prev = null ;
ListNode curr = head;
while ( curr != null ) {
ListNode nextTemp = curr. next;
curr. next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
解法 时间复杂度 空间复杂度 递归 O(n+m) O(n+m) 迭代 O(n+m) O(1)
class Solution {
public ListNode mergeTwoLists ( ListNode l1, ListNode l2) {
if ( l1== null ) return l2;
else if ( l2== null ) return l1;
ListNode head= new ListNode ( - 1 ) ;
ListNode cur= head;
while ( l1!= null && l2!= null ) {
if ( l1. val<= l2. val) {
cur. next= l1;
l1= l1. next;
} else {
cur. next= l2;
l2= l2. next;
}
cur= cur. next;
}
cur. next= l1== null ? l2: l1;
return head. next;
}
}
class Solution {
public ListNode mergeTwoLists ( ListNode l1, ListNode 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 {
l2. next = mergeTwoLists ( l1, l2. next) ;
return l2;
}
}
}
解法 时间复杂度 空间复杂度 HashMap辅助 O(n) O(n) 原地修改(扩充&分离) O(n) O(1)
class Solution {
public Node copyRandomList ( Node head) {
if ( head == null ) {
return head;
}
Map < Node , Node > map = new HashMap < > ( ) ;
for ( Node cur = head; cur != null ; cur = cur. next) {
map. put ( cur, new Node ( cur. val) ) ;
}
for ( Node cur = head; cur != null ; cur = cur. next) {
map. get ( cur) . next = map. get ( cur. next) ;
map. get ( cur) . random = map. get ( cur. random) ;
}
return map. get ( head) ;
}
}
class Solution {
public Node copyRandomList ( Node head) {
if ( head == null ) {
return head;
}
for ( Node node = head, copy = null ; node != null ; node = node. next. next) {
copy = new Node ( node. val) ;
copy. next = node. next;
node. next = copy;
}
for ( Node node = head; node != null ; node = node. next. next) {
if ( node. random != null ) {
node. next. random = node. random. next;
}
}
Node newHead = head. next;
for ( Node node = head, temp = null ; node != null && node. next != null ; ) {
temp = node. next;
node. next = temp. next;
node = temp;
}
return newHead;
}
}
解法 时间复杂度 空间复杂度 双指针 O(n+m) O(1) 哈希表法 O(m+n) O(m)/O(n)
public class Solution {
public ListNode getIntersectionNode ( ListNode headA, ListNode headB) {
if ( headA== null || headB== null ) return null ;
ListNode a= headA, b= headB;
while ( a!= b) {
a= ( a== null ) ? headB: a. next;
b= ( b== null ) ? headA: b. next;
}
return a;
}
}
解法 时间复杂度 空间复杂度 双指针 O(n) O(1) 哈希表法 O(n) O(n)
public class Solution {
public ListNode EntryNodeOfLoop ( ListNode pHead)
{
if ( pHead== null ) return null ;
List < ListNode > list= new ArrayList < > ( ) ;
while ( pHead!= null ) {
if ( list. contains ( pHead) ) return pHead;
list. add ( pHead) ;
pHead= pHead. next;
}
return pHead;
}
}
public boolean hasCycle ( ListNode head) {
Set < ListNode > nodesSeen = new HashSet < > ( ) ;
while ( head != null ) {
if ( nodesSeen. contains ( head) ) {
return true ;
} else {
nodesSeen. add ( head) ;
}
head = head. next;
}
return false ;
}
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 ;
}
```java
public class Solution {
public ListNode detectCycle ( ListNode head) {
if ( head== null || head. next== null ) return null ;
ListNode slow= head, fast= head;
while ( true ) {
if ( fast== null || fast. next== null ) return null ;
fast= fast. next. next;
slow= slow. next;
if ( fast== slow) break ;
}
fast= head;
while ( fast!= slow) {
fast= fast. next;
slow= slow. next;
}
return slow;
}
}
保留重复的数字=删除链表中重复的结点 (牛客网)
class Solution {
public ListNode deleteDuplicates ( ListNode head) {
Set < ListNode > set= new HashSet < ListNode > ( ) ;
if ( head== null || head. next== null ) return head;
ListNode pre= head, cur= head. next;
while ( cur!= null ) {
if ( cur. val== pre. val) {
pre. next= cur. next;
} else {
pre= pre. next;
}
cur= cur. next;
}
return head;
}
}
class Solution {
public ListNode deleteDuplicates ( ListNode head) {
if ( head == null || head. next == null ) {
return head;
}
head. next = deleteDuplicates ( head. next) ;
if ( head. val == head. next. val) head = head. next;
return head;
}
}
不保留重复的数字
解法 时间复杂度 空间复杂度 双指针 O(n) O(1) 双指针+尾插法 哈希表法 O(n) O(n) 递归 O(n) O(n)
1: 哈希表法 hash_map 统计出现次数,如果出现次数等于1,那么就放在新链表后面。
:2:双指针: 定义一个 dummy 头结点,链接上原链表,cur 指向原链表头部 ① 当前结点value != 当前结点的下一结点value。那么让pre指针来到当前结点,这样就链接了前一结点和当前结点。然后当前结点移至下一结点 ② 当前结点value == 当前结点的下一结点value。那么就让 cur 一直往下走直到 当前结点value != 当前结点的下一结点value,然后此时是不能动 pre 指针的,要避免后面还有重复的,所以让 pre->next = cur->next。然后当前结点移至下一结点。 循环结束的条件结合例子即可,如[1,1]
3.递归法
public class Solution {
public ListNode deleteDuplication ( ListNode pHead)
{
if ( pHead== null || pHead. next== null ) return pHead;
ListNode newNode= deleteDuplication ( pHead. next) ;
if ( pHead. next!= null && pHead. next. val== pHead. val) {
while ( pHead. next!= null && pHead. next. val== pHead. val)
{
pHead= pHead. next;
}
return deleteDuplication ( pHead. next) ;
}
else {
pHead. next= deleteDuplication ( pHead. next) ;
}
return pHead;
}
}
class Solution {
public ListNode deleteDuplicates ( ListNode head) {
if ( head== null || head. next== null ) {
return head;
}
ListNode first= new ListNode ( - 1 ) ;
first. next= head;
ListNode pre= first, cur= head;
while ( cur!= null && cur. next!= null ) {
if ( cur. val!= cur. next. val) {
pre= cur;
cur= cur. next;
} else {
while ( cur. next!= null && cur. val== cur. next. val) {
cur= cur. next;
}
pre. next= cur. next;
cur= cur. next;
}
}
return first. next;
}
}
class Solution {
public ListNode deleteDuplicates ( ListNode head) {
if ( head == null ) return head;
ListNode dummy = new ListNode ( - 1 ) ;
ListNode tail = dummy;
for ( ListNode l = head, r = head; l != null ; l = r) {
while ( r != null && r. val == l. val) r = r. next;
if ( l. next == r) {
tail. next = l;
tail = l;
tail. next = null ;
}
}
return dummy. next;
}
}
作者:optimjie
链接:https: / / leetcode- cn. com/ problems/ remove- duplicates- from- sorted- list- ii/ solution/ javashuang- zhi- zhen- dai- ma- jiao- duan- rong- yi- li- ji/
来源:力扣(LeetCode )
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。