Data Structure 5 - linked list
Background
Definition
A linked list is a linear collection of data elements, whose order is not given by their physical placement in memory. Instead, each element points to the next. It is a data structure consisting of a collection of nodes which together represent a sequence.
In its most basic form, each node contains: data, and a reference (in other words, a link) to the next node in the sequence.
(+) save memory compared to array, it only allocates the memory required for values to be stored
(+) dynamic, length can be increased or decreased as necessary
(+) list nodes can live anywhere (not continguously) in the memory, only update references
(+) can be easily inserted or removed without reallocation or reorganization
(-) access time is linear (and difficult to pipeline)
(-) no faster access, such as random access. Arrays have better cache locality compared to linked lists.
(-) no efficient indexing, (iterate k times to find index k), many basic operations—such as obtaining the last node of the list, finding a node that contains a given datum, or locating the place where a new node should be inserted—may require iterating through most or all of the list elements.
Linked lists are among the simplest and most common data structures. They can be used to implement several other common abstract data types, including lists, stacks, queues, associative arrays, and S-expressions, though it is not uncommon to implement those data structures directly without using a linked list as the basis.
Points to Note
- single linked list or double
- when deletion in a double linked list, must also update n.next, to set n.next.prev equal to n.prev
-
two pointers (slow and fast) - the Runner Technique
rearrange a1->a2->a3->...->an->b1->b2->b3->...->bn to a1->b1->a2->b2..................->an->bn use the fast move every 2 nodes a1->a2->a3->...->an->b1->b2->b3->...->bn f f s s s f move f to the front and begin "weaving" on each iteration, s selects an element from b, and inserts it after f
- recursion algs take at least SO(n), n is the depth of the recursive call, all recursive algs can be implemented iteratively, recursive solutions are often cleaner but less optimal
Basic Operations
- find() - TO(n)
- access_first() - TO(1)
- access_last() - TO(n)
- access_arbitrary() - TO(n)
- insert_front() - TO(1)
- insert_end() - TO(n)
- insert_arbitrary() - TO(n)
- delete_front() - TO(1)
- delete_end() - TO(n)
- size() - TO(n)
- isEmpty() - TO(1)
Implementation
class ListNode():
def __init__(self,x):
self.val=x
self.next=None
#create linked list
head=ListNode(0)
head.next=ListNode(1)
head.next.next=ListNode(2)
head.next.next.next=ListNode(3)
head.next.next.next.next=ListNode(4)
- insert_front, insert_end, insert_mid
- delete - use two pointers, cur h and pre
- print = traverse
Note:
the difference between while last and while last.next in scanning the list
last=self.head
0->1->2->3->N
while last.next while last:
last=last.next last=last.next
0->1->2->3->N 0->1->2->3->N
^ ^
last stops at 3 last stops at None
class LinkedList():
def __init__(self):
self.head=None
def printList(self):
node=self.head
while node:
print(node.val)
node=p.next
def insert_front(self,data):
new=ListNode(data)
new.next=self.head
self.head=new
def insert_end(self,data):
new=ListNode(data)
if not self.head:
self.head=new
return
last=self.head
while last.next:
last=last.next
last.next=new
def insert_mid(self,mid,data):
if not mid:
print('The mentioned mid node is absent.')
return
new=ListNode(data)
new.next=mid.next
mid.next=new
#use two pointers to record current h and pre h
def delete(self,data):
h=self.head
#if data==head.val, delete head
if h.val==data:
self.head=h.next
h=None
return
while h:
if h.val==data:
break
pre=h
h=h.next
#if data not in list, h to the end None
if not h:
return
pre.next=h.next
h=None
l=LinkedList()
l.head=ListNode(1)
n2=ListNode(2)
n3=ListNode(3)
l.head.next=n2
n2.next=n3
l.insert_front(0)
l.insert_end(4)
l.insert_mid(l.head.next,1.5)
l.printList()
>>
0
1
1.5
2
3
4
l.delete(3)
l.printList()
>>
0
1
1.5
2
4
Simple Deletion:
def deleteNode(head,data):
h=head
if h.val==data:
return head.next
while h.next:
if h.next.val==data:
h.next=h.next.next
return head
h=h.next
return head
Cracking
2.1 Remove Dups
Write code to remove duplicates from an unsorted linked list.
- use hash table or set to track dups
Solution: TO(n), SO(n)
def deleteDups(head):
h=head
prev=None
seen=set()
while h:
if h.val in seen:
prev.next=h.next
else:
seen.add(h.val)
prev=h
h=h.next
Solution 2: no buffer allowed, use two pointers, TO(n^2), SO(1)
def deleteDups(head):
cur=head
while cur:
runner=cur
while runner.next:
if runner.next.val==cur.val:
runner.next=runner.next.next
else:
runner=runner.next
cur=cur.next
2.2 Return Kth to Last
Implement an algorithm to find the kth to last element of a singly linked list.
define passing k=1, return the last element
k=2, return to the second to last element
Solution 1: if the length is known, iterate (length-k), too trivial
Solution 2.1: recursive, SO(n)
1->2->3->N
p((1),2)
idx=p((2),2)+1
idx=p((3),2)+1=2 idx==k print((2).val), because (2).next=(3)
idx=p((N),2)+1=1
0
def printKthToLast(head,k):
if not head:
return 0
idx=printKthToLast(head.next,k)+1
if idx==k:
print(head.val)
return idx
Solution 2.2: wrapping into a class
class ListNode():
def __init__(self,val):
self.val=val
self.next=None
class Index():
value=0
def kthToLast(head,k):
idx=Index()
return kthToLast2(head,k,idx)
def kthToLast2(head,k,idx):
if not head:
return None
node=kthToLast2(head.next,k,idx)
idx.value+=1
if idx.value==k:
return head
return node
h=ListNode(1)
h.next=ListNode(2)
h.next.next=ListNode(3)
print(kthToLast(h,3).val)
Solution 3: iterative, two pointers, TO(n), SO(1)
p1 goes first at k steps
p2 goes together with p1 for the rest steps of p1
i.e. total 5 nodes, k=2
p1 goes 2 steps
then p2 goes 5-2 steps with p1 touches the end
def KthToLast(head,k):
p1=head
p2=head
for i in range(k):
if not p1:
return None
p1=p1.next
while p1:
p1=p1.next
p2=p2.next
return p2
2.3 Delete Middle Node
Implement an algorithm to delete a node in the middle of a singly linked list, given only access to that node. (Given the middle node)
Input: the node from the linked list a->b->c->d->e->f
Output: nothing is returned, but the new linked list looks like a->b->d->e->f
- This problem can not be solved if the node to be deleted is the last node
- to handle this case, could consider marking the node as dummy
def deleteNode(node):
if not node or not node.next:
return False
node.data=node.next.data
node.next=node.next.next
return True
2.4 Partition
Write code to partition a linked list around a value x, such that all nodes less than x come before all nodes greater than or equal to x. If x is contained within the list, the values of x only need to be after the elements less than x. The partition element x can appear anywhere in the “right partition”. It does not need to appear between the left and right partitions.
Input: 3->5->8->5->10->2->1, x=5
Output: 3->1->2->10->5->5->8
- array shifting is expensive, however, in linked list is easy
- we can create 2 different linked lists one for elements <x, one for >=x, then merge_sort
- this approach is stable because every element stays in their original order
Solution 1:
def partition(node,x):
left_head=None
left_end=None
right_head=None
right_end=None
while node:
node_next=node.next
node.next=None
if node.val<x:
if not left_head:
left_head=node
left_end=left_head
else:
left_end.next=node
left_end=node #before_end always points to the end node
else:
if not right_head:
right_head=node
right_end=right_head
else:
right_end.next=node
right_end=node
node=node_next
if not left_head:
return right_head
left_end.next=right_head
return left_head
Solution 2??: start a new list usding the existing nodes, elements <x are put at the head, otherwise put at the tail
?? 1->2->3->3 ??
?? 3->5->8->5->10 ??
Does this two halves naturally connected??
def partition(node,x):
head=node
tail=node
while node:
if node.val<x:
#insert node at head
node.next=head
head=node
else:
#insert node at tail
tail.next=node
tail=node
node=node.next
tail.next=None
return head
2.5 Sum Lists
You have two numbers represented by a linked list, where each node contains a single digit. The digits are stored in reverse order, such that the 1’s digit is at the head of the list. Write a function that adds the two numbers and return the sum as a linked list. (same as leetcode 2)
Input: 7->1->6 + 5->9->2 that is 617+295
Output: 2->1->9 that is 912
Solution: recursive
7->1->6
+ 5->9->2
-----------------
12 11 9
keep 2 1 9
carry 1 1
def addLists(l1,l2,carry):
if not l1 and not l2 and carry==0:
return None
res=ListNode(0)
value=carry
if l1:
value+=l1.val
if l2:
value+=l2.val
res.val=value%10
if l1 or l2:
more=addLists(l1.next if l1 else None, l2.next if l2 else None, 1 if value >=10 else 0)
res.next=more
return res
Follow up - In right order:
Input: 6->1->7 + 2->9->5 that is 617+295
Output: 9->1->2 that is 912
Solution:
class PartialSum():
head=ListNode(None)
carry=0
def addLists(l1,l2):
len1=length(l1)
len2=length(l2)
if len1<len2:
l1=padList(l1,len2-len1)
else:
l2=padList(l2,len1-len2)
ps=PartialSum()
ps=addListHelper(l1,l2)
if ps.carry==0:
return ps.head
else:
res=insertBefore(ps.head,ps.carry)
return res
def addListHelper(l1,l2):
if not l1 and not l2:
ps=PartialSum()
return ps
ps=addListHelper(l1.next,l2.next)
val=ps.carry+l1.val+l2.val
full_res=insertBefore(ps.head,val%10)
ps.head=full_res
ps.carry=val//10
return ps
def padList(l,padding):
head=l
for i in range(padding):
head=insertBefore(head,0)
return head
def insertBefore(l,data):
node=ListNode(data)
if l:
node.next=l
return node
def length(l):
cnt=0
while l:
cnt+=1
l=l.next
return cnt
2.6 Palindrome
Implement a function to check if a linked list is a palindrome.
i.e. 0->1->2->1->0
Solution 1: reverse and compare, if same they are identical
def isPalindrome(head):
reversed=reverseAndClone(head)
return isEqual(head,reversed)
def reverseAndClone(node):
head=ListNode(None)
while node:
n=ListNode(node.val)
n.next=head
head=n
node=node.next
return head
def isEqual(one,two):
while one and two:
if one.val!=two.val:
return False
one=one.next
two=two.next
return not one and not two
Solution 2: iterative, stack to check the first half of the list is the reverse of the second half
if length is known, we can directly put them onto stack
if length is unknown, use fast and slow pointers - at each step, we push the data from the slow pointer onto a stack
def isPalindrome(head):
fast=head
slow=head
stack=[]
while fast and fast.next:
stack.append(slow.val)
slow=slow.next
fast=fast.next.next
if fast:
slow=slow.next
while slow:
top=stack.pop()
if top!=slow.val:
return False
slow=slow.next
return True
Solution: recursive
class Result():
node=ListNode()
bool=False
def isPalindrome(head):
length=lengthOfList(head)
p=Result()
p=isPalindromeRecurse(head,length)
return p.bool
def isPalindromeRecurse(head,length):
if not head or length<=0:
return Result(head,True)
elif length==1:
return Result(head.next,True)
res=isPalindromeRecurse(head.next,length-2)
if not res.bool or not res.node:
return res
res.bool=(head.val==res.node.val)
res.node=res.node.next
return res
def lengthOfList(node):
size=0
while node:
size+=1
node=node.next
return size
Problems
Remove
leetcode 237 - Delete Node in a Linked List [E]
Given linked list: 4->5->1->9, node=5
Output: 4->1->9
class Solution():
def deleteNode(self,node):
node.val=node.next.val
node.next=node.next.next
leetcode 19 - Remove Nth Node From End of List [M]
Given a linked list, remove the n-th node from the end of list and return its head.
Example:
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
Consider:
- whether n is valid node, what if n>len of list?
- what if deleting the head? -> use dummy
- how about if it’s a cycled linked list? -> not clear
Solution:
- create dummy head
- find the node needs to be deleted using fast and slow pointers (rabbit turtle race)
- fast goes first, then slow and fast go together, slow stops at the one before the nth node
Note: while fast.next: fast=fast.next -> fast stop at the last node, cuz fast.next is None -
slow->slow.next.next
dummy->1->2->3->4->5 n=2 s,f dummy->1->2->3->4->5 f dummy->1->2->3->4->5 s f s->s.next.next =dummy->1->2->3->5
class Solution():
def removeNthfromEnd(self,head,n):
dummy=ListNode(0)
dummy.next=head
slow=fast=dummy
for i in range(n):
fast=fast.next
while fast.next:
fast=fast.next
slow=slow.next
slow.next=slow.next.next
return dummy.next
leetcode 83 - Remove Duplicates from Sorted List [E] - remove one dup
Given a sorted linked list, delete all duplicates such that each element appear only once.
Example 2:
Input: 1->1->2->3->3
Output: 1->2->3
Solution:
- put p at head
-
while p and p.next: p=p.next, p stop at the end
1->1->2->3->3 p p->p.next.next if p.val==p.next.val 1->2->3->3 p p=p.next if p.val!=p.next.val 1->2->3->3 p p=p.next if p.val!=p.next.val 1->2->3->3 p p->p.next.next if p.val==p.next.val 1->2->3->N p
class Solution():
def deleteDuplicates(self, head):
p=head
while p and p.next:
if p.val==p.next.val:
p.next=p.next.next
else:
p=p.next
return head
leetcode 82 - Remove Duplicates from Sorted List II [M] - remove all dups
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.
Example 1:
Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:
Input: 1->1->1->2->3
Output: 2->3
Solution:
- create dummy cuz might delete head
- create pre (p) start from dummy always ahead of cur (c)
- find c.val==c.next.val, move c to next
-
check p.next is still c, if not p->c.next (delete all dups)
dummy->1->2->3->3->4->4->5 p c p=p.next if c==p.next dummy->1->2->3->3->4->4->5 p c p=p.next if c==p.next dummy->1->2->3->3->4->4->5 p c c=c.next if c.val==c.next.val dummy->1->2->3->3->4->4->5 p c p->c.next if c!=p.next =dummy->1->2->4->4->5 p c c=c.next if c.val==c.next.val dummy->1->2->4->4->5 p c p->c.next if c!=p.next =dummy->1->2->5 p c p=p.next if c==p.next dummy->1->2->5 p
class Solution():
def deleteDuplicates(self, head):
dummy=ListNode(0)
dummy.next=head
pre=dummy
while pre.next:
cur=pre.next
while cur.next and cur.val==cur.next.val:
cur=cur.next
if cur==pre.next:
pre=pre.next
else:
pre.next=cur.next
return dummy.next
leetcode 203 - Remove Linked List Elements [E]
Remove all elements from a linked list of integers that have value val.
Input: 1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5
Solution:
- create dummy in case removing the head
- p ahead of head at each step
-
if encounter value, p->h.next, h->N
dummy->1->2->6->3->4->5->6 p h p h p h h.val==val 2->3 p->h.next h h=h.next
class Solution():
def removeElements(self,head,val):
dummy=p=ListNode(0)
dummy.next=head
while head:
if head.val==val:
p.next=head.next
else:
p=p.next
head=head.next
return dummy.next
Partition
leetcode 86 - Partition List [M]
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. You should preserve the original relative order of the nodes in each of the two partitions.
Example:
Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
Solution:
put all nodes less than x into the front and the rest remains the origin
- create two dummy heads, lesser_head and greater_head, and point l and g to them
- put p to head, if p.val<x, put it to l.next, else put it to g.next
-
merge two lists, g->None, l->greater_head.next
lesser_head ->1->4->3->2->5->2 greater_head->1->4->3->2->5->2 x=3 l,g p if 1<x l->p l=p lesser_head->1 l ->1->4->3->2->5->2 p if 4>=x g->p g=p greater_head->4 g ->1->4->3->2->5->2 p if 3>=x g->p g=p greater_head->4->3 g ->1->4->3->2->5->2 p if 2<x l->p l=p lesser_head->1->2 l ->1->4->3->2->5->2 p if 5>=x g->p g=p greater_head->4->3->5 g ->1->4->3->2->5->2 p if 2<x l->p l=p lesser_head->1->2->2 l l->greader_head.next g->None lesser_head->1->2->2->4->3->5->N (result)
class Solution():
def partition(self,head,target):
l_head=l=ListNode(0)
g_head=g=ListNode(0)
p=head
while p:
if p.val<target:
l.next=p
l=p #or l=l.next
else:
g.next=p
g=p #or g=g.next
p=node.next
g.next=None
l.next=g_head.next
return l_head.next
Merge
leetcode 21 - Merge Two Sorted Lists [E]
Example:
Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4
Solution:
- create dummy and pre
- compare val of each l1,l2, put it next to new p
-
finish with shorter l, attach the leftover of the longer
1->4 l1 2->3->5->6 l2 2->3->5->6 l2 1->4 l1 1->2->3->4|->5->6 (leftover of l2) while done p->p->p->p->leftover
class Solution(object):
def merge(self,l1,l2):
pre=dummy=ListNode(0) # a new list
while l1 and l2:
if l1.val<l2.val:
pre.next=l1
l1=l1.next
else:
pre.next=l2
l2=l2.next
pre=pre.next
pre.next=l1 or l2
return dummy.next
later check!! leetcode 23 - Merge K Sorted Lists [H]
Example:
Input:
[1->4->5,
1->3->4,
2->6]
Output: 1->1->2->3->4->4->5->6
Solution: heapify
import heapq
class Solution(object):
def mergeKLists(self,lists):
pre=dummy=ListNode(0)
next_nodes=[(l.val,l) for l in lists if l]
heapq.heapify(next_nodes)
while next_nodes:
value,node=heapq.heappop(next_nodes)
pre.next=node
pre=pre.next
if node.next:
heapq.heappush(next_nodes,(node.next.val,node.next))
return dummy.next
Swap
leetcode 24 - Swap Nodes in Pairs [M]
Given a linked list, swap every two adjacent nodes and return its head.
Example:
Given 1->2->3->4, you should return the list as 2->1->4->3.
Solution:
- create dummy and pre
- point first (f) to 1 and second (s) to 2
-
first->second.next, pre->second, pre.next->first
dummy->1->2->3->4 p f s 1->3 f->s.next dummy->2 p->s p 2->1->3 p.next->f = dummy->2->1->3->4->N p f s 3->N f->s.next dummy->2->1->4 p->s p 4->3->N p.next->f
class Solution():
def swapPairs(self,head):
pre=dummy=ListNode(0)
dummy.next=head
while pre.next and pre.next.next:
first=pre.next
second=pre.next.next
first.next=second.next
pre.next=second
pre.next.next=first
pre=pre.next.next
return dummy.next
Rotate
leetcode 61 - Rotate List [M]
Given a linked list, rotate the list to the right by k places, where k is non-negative.
Example 1:
Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:
Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
Solution:
- go through the linked list to get the total len
-
use fast and slow pointer to find rotate node
1->2->3->4->5 h f s f 4->5->1->2-> f->h h=s.next 4->5->1->2->3->N s->N
class Solution():
def rotateList(self,head,k):
if not head:
return
l=0
while head:
l+=1
head=head.next
fast,slow=head,head
for i in range(k%l):
fast=fast.next #fast stop at 3 if k=2
while fast.next:
slow=slow.next
fast=fast.next #fast stops at 5 slow stops at 3
fast.next=head
head=slow.next
slow.next=None
return head
Reverse
leetcode 206 - Reverse Linked List [E]
Example:
Input: 1->2->3->4->5->Null
Output: 5->4->3->2->1->Null
Solution1: operating on the original linked list
- point p to head
- head move to next
- p.next=new_head
-
move new_head to forehand p
1->2->3->N | p h->h(Node(2)) p->new_head(Node(None)) =1->N | new_head(Node(1)) 1->2->3->N | p h->h(Node(3)) p->new_head(Node(1)) =2->1->N | new_head(Node(2)) 1->2->3->N | p h->h(Node(4)) p->new_head(Node(2)) =3->2->1->N | new_head(Node(3))
class Solution():
def reverseList(self,head):
new_head=None
while head:
p=head
head=head.next
p.next=new_head
new_head=p
return new_head
Simplified:
class Solution():
def reverseList(self,head):
new_head,p=None,head
while p:
new_head,p.next,p=p,new_head,p.next
return new_head
Solution2: iteratively
Solution3: recursively
leetcode 92 - Reverse Linked List II [M]
Example:
Input: 1->2->3->4->5->Null, m=2, n=4
Output: 1->4->3->2->5->Null
Solution:
- prepare dummy node pointed to head
- point m,n nodes to head, pre (p) to dummy
- find m,n nodes and put p ahead of mnode
- while mnode!=nnode, p->mnode.next, mnode->nnode.next, nnode->mnode, put mnode to p.next
note: p,nnode not change, only mnode change position to p.next
loop1: first get 1->3->4->5->N, then 2->5->N, then put 4->2, get 1->3->4->2->5->N
loop2: first get 1->4->2->5->N, then 3->2->5->N, then put 4->3, get 1->4->3->2->5->N
dummy->1->2->3->4->5->N
| |
p h,m,n
dummy->1->2->3->4->5->N
| | |
p m n
dummy->1->2->3->4->5->N
|
p->m.next(Node(3))
=1->3->4->5->N
|
n
dummy->1->2->3->4->5->N
|
m->n.next(Node(5))
=2->5->N
^
|
dummy->1->3->4(->5->N)
|
n->m(Node(2))
dummy->1->3->4->2->5->N <new!>
| | |
p m n
dummy->1->3->4->2->5->N
|
p->m.next(Node(4))
=1->4->2->5->N
dummy->1->3->4->2->5->N
|
m->n.next(Node(2))
=3->2->5->N
^
|
dummy->1->4(->2->5->N)
|
n->m(Node(3))
dummy->1->4->3->2->5->N
| |
p n,m(end while)
class Solution():
def reverse(self,head,m,n):
dummy=ListNode(0)
dummy.next=head
mnode=head
nnode=head
pre=dummy
for i in range(1,m):
pre=mnode
mnode=mnode.next
for i in range(1,n):
nnode=nnode.next
while mnode!=nnode:
pre.next=mnode.next
mnode.next=nnode.next
nnode.next=mnode
mnode=pre.next
return dummy.next
leetcode 143 - Reorder List [M]
Example 1:
Given 1->2->3->4
Reorder it to 1->4->2->3
Example 2:
Given 1->2->3->4->5
Reorder it to 1->5->2->4->3
Solution:
- find mid node using fast and slow points, (first left if even len)
-
reverse linked list after mid(slow)
new_head,p=None,slow
while p:
new_head,p.next,p=p,new_head,p.next1->2->3->4->5 | slow(mid),p 1->2->3->4->5 new_head*=Node(None) | new_head# p->new_head*(Node(None)) =3->N 1->2->3->4->5 new_head#=Node(3) | new_head~ p->new_head#(Node(3)) =4->3->N 1->2->3->4->5 new_head~=Node(4) | new_head+ p->new_head~(Node(4)) =5->4->3->N
-
cross insert
first,second=head,new_head
while second.next:
first.next,first=second,first.next
second.next,second=first,second.next1->2->3->4->5 5->4->3->N | | head new_head first(f) second(s) f->s 1->2->3->4->5 =1->5 f# s->f# 5->4->3->N =1->5->2 s* f#->s* 1->2->3->4->5 =1->5->2 ->4 f~ s*->f~ 5->4->3->N =1->5->2->4 ->3 s.next=None stop!
class Solution():
def reorderList(self,head):
if not head:
return None
fast,slow=head,head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
new_head,p=None,slow
while p:
new_head,p.next,p=p,new_head,p.next
first,second=head,new_head
while second.next:
first.next,first=second,first.next
second.next,second=first,second.next
leetcode 25 - Reverse Nodes in k-Group [H]
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.
Example:
Given this linked list: 1->2->3->4->5
For k = 2, you should return: 2->1->4->3->5 (group in 2)
For k = 3, you should return: 3->2->1->4->5 (group in 3, leftover remains)
Solution: put k group in stack and pop!!
- create dummy and cur
- point first (f) to 1
- for i in range(k): put first node in stack and move first to next
- check len(stack)?=k, if not return dummy.next
- pop node in stack next to dummy, move cur to next
-
point cur->first
dummy->1->2->3->4->5 c f f stack=[1,2] len(stack)==k dummy->2->1 c->stack.pop() c 1->3->4->5 c->f f f stack=[3,4] len(stack)==k 1->4->3 c->stack.pop() 3->5 c->f stack=[5] len(stack)!=k return dummy.next
class Solution():
def reverseKGroup(self,head,k):
if not head:
return None
cur=dummy=ListNode(0)
first=dummy.next
stack=[]
while first:
for i in range(k):
if first:
stack.append(first)
first=first.next
if len(stack)!=k:
return dummy.next
while stack:
cur.next=stack.pop()
cur=cur.next
cur.next=first
return dummy.next
Fast n Slow
leetcode 141 - Linked List Cycle [E] - T/F
Given a linked list, determine if it has a cycle in it.
Example 1:
Input: head = [3,2,0,-4], pos = 1
3->2->0->-4
^ |
|-------
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the second node.
Example 2:
Input: head = [1,2], pos = 0
1->2
^ |
|---
Output: true
Explanation: There is a cycle in the linked list, where tail connects to the first node.
Example 3:
Input: head = [1], pos = -1
Output: false
Explanation: There is no cycle in the linked list.
class Solution(object):
def hasCycle(self,head):
fast, slow=head, head
while fast and fast.next:
if fast==slow:
return True
slow=slow.next
fast=fast.next.next
return False
leetcode 142 - Linked List Cycle II [M] - return cycle head
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Solution:
- let fast (2 steps) and slow (1 step) meet
- put fast back to the head
- the cycle head will be the same node when fast (1 step) and slow (1 step) meet again
class Solution(object):
def detectCycle(self,head):
fast, slow=head, head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
fast=head
while fast!=slow:
fast=fast.next
slow=slow.next
return slow
return None
leetcode 234 - Palindrome Linked List [E]
Given a singly linked list, determine if it is a palindrome.
Examples:
Input: 1->2
Output: false
Input: 1->2->2->1
Output: true
Solution:
while fast and fast.next is for odd and even length of list
if odd length (including None), fast reaches the end of None
if even length (including None), fast reaches the node before end
1->2->2->1->N stack=[1,2]
f f f
s s s
1->2->3->2->1->N stack=[1,2]
f f f
s s s
record slow visited nodes into a stack
if fast reaches the None, slow keeps its current position
if fast doesnot reach the None, slow moves to next
1->2->2->1->N stack=[1,2]
f f f
s s s*
1->2->3->2->1->N stack=[1,2]
f f f
s s s->s*
compare stack.pop with s*, move slow to the end of the list and return matching result
class Solution():
def isPalindrome(self,head):
fast=slow=head
stack=[]
while fast and fast.next:
stack.append(slow.val)
slow=slow.next
fast=fast.next.next
if fast:
slow=slow.next
while slow:
top=stack.pop()
if top!=slow.val:
return False
slow=slow.next
return True
Sorting
leetcode 147 - Insertion Sort List [M]
Example:
Input: 4->2->1->3
Output: 1->2->3->4
Solution:
Insertion sort: check next if smaller move it to front
- create dummy
- scan and find head.val>head.next.val,t=h.next
- q=dummy, h->h.next.next
-
scan q from dummy, find q.next.val>t.val, t->q.next, q->t
dummy->4->2->1->3->N | | | q h t (when 4>2, t=h.next) <step2> q-->=4---->1->3->N h->h.next.next <step3> 2->4->1->3->N t->q.next(4->1->3->N) when q.next=4>2=t <step4> q------>t =dummy->2->4->1->3->N | | | q h t (4>1) q--->2-=4->3->N h->h.next.next 1->2->4->3->N t->q.next(2->4->3->N) when q.next=2>1=t q--------->t =dummy->1->2->4->3->N | | | q h t (4>3) q--->1->2-=4->N h->h.next.next | 3->4->N t->q.next(4->N) when q.next=4>3=t q---->t =dummy->1->2->3->4->N h.next=None stop!
class Solution():
def insertionSortList(self,head):
if not head or not head.next:
return head
dummy=ListNode(0)
dummy.next=head
while head.next:
if head.val<=head.next.val:
head=head.next
else:
t=head.next
q=dummy
head.next=head.next.next
while q.next and q.next.val<t.val:
q=q.next
t.next=q.next
q.next=t
return dummy.next
leetcode 148 - (Merge) Sort List [M]
Sort linked list in O(nlogn) time complexity with O(n) space complexity
Example:
Input: 4->2->1->3
Output: 1->2->3->4
Solution:
Merge sort: split list in half, sort halves and merge
def merge_sort(a):
if len(a)<=1:
return a
left,right=merge_sort(a[:len(a)//2]),merge_sort(a[len(a)//2:])
return merge(left,right)
- find mid using fast and slow, slow point to mid if odd len, second mid if even len
- cut the first half with p->N
- run new recursive sortList with head and slow as second head
-
merge two sorted lists (leetcode 21)
5->3->1->2->4 5->3->1->2->4->0->N f,s,p p s f p s f
class Solution():
def sortList(self,head):
if not head or not head.next
return head
pre,fast,slow=head,head,head
while fast and fast.next:
pre=slow
slow=slow.next
fast=fast.next.next
pre.next=None
l1=self.sortList(head)
l2=self.sortList(slow)
return self.mergeTwoList(l1,l2)
#leetcode 21
def mergeTwoList(self,l1,l2):
pre=dummy=ListNode(0)
if not l1:
return l2
if not l2:
return l1
while l1 and l2:
if l1.val<l2.val:
pre.next=l1
l1=l1.next
else:
pre.next=l2
l2=l2.next
pre=pre.next
pre.next=l1 or l2
return dummy.next
Intersection
leetcode 160 - Intersection of Two Linked Lists [E]
Write a program to find the node at which the intersection of two singly linked lists begins.
For example, the following two linked lists:
A: a1->a2-
|-> c1->c2->c3
B: b1->b2->b3-
begin to intersect at c1
Solution:
go through the two linked lists to the end and then change head, will meet again
a1 -> a2 --
p1_1 p1_2 |
p2_7 p2_8 |
|-> c1 -> c2 -> c3 -> N
| p1_3 p1_4 p1_5 p1_6 move to b-head
| p2_4 p2_5 p2_6 p2_7 move to a-head
| [p2_9]
| [meets]
| [p1_9]
b1 -> b2 -> b3 -
p2_1 p2_2 p2_3
p1_6 p1_7 p1_8
class Solution():
def getIntersectionNode(self, headA, headB):
p1,p2=headA,headB
while p1!=p2:
p1=headB if not p1 else p1.next
p2=headA if not p2 else p2.next
return p1