leetcode.com/contest/weekly-contest-223
Welcome to Weekly Contest 223! Feel free to share and post your contest experience here!
You can also view the rankings for the contest here.
Links to the individual problems are included below:
Decode XORed Array (3 points)
Swapping Nodes in a Linked List (4 points)
Minimize Hamming Distance After Swap Operations (5 points)
Find Minimum Time to Finish All Jobs (6 points)
Comparing to the last 10 contests, Q1: average difficulty, 43.89% acceptance; Q2: easier than usual, 37.78% acceptance; Q3: average difficulty, 12.08% acceptance; Q4: easier than usual, 3.61% acceptance; overall: this contest is easier than usual. I compile past contest data to provide a data-driven way to assess problem difficulty. See more at:
https://docs.google.com/spreadsheets/d/14UU9Ny7ohum7JTgG1zPL0Z5IsR_qbSeLM8jNcMVfvJI/edit?usp=sharing
(Acceptance rate is calculated with caveats detailed in the spreadsheet.)
Easy
809Add to ListShare
There is a hidden integer array arr that consists of n non-negative integers.
It was encoded into another integer array encoded of length n - 1, such that encoded[i] = arr[i] XOR arr[i + 1]. For example, if arr = [1,0,2,1], then encoded = [1,2,3].
You are given the encoded array. You are also given an integer first, that is the first element of arr, i.e. arr[0].
Return the original array arr. It can be proved that the answer exists and is unique.
Example 1:
Input: encoded = [1,2,3], first = 1 Output: [1,0,2,1] Explanation: If arr = [1,0,2,1], then first = 1 and encoded = [1 XOR 0, 0 XOR 2, 2 XOR 1] = [1,2,3]
Example 2:
Input: encoded = [6,2,7,3], first = 4 Output: [4,2,0,7,4]
Constraints:
Since that encoded[i] = arr[i] XOR arr[i+1], then arr[i+1] = encoded[i] XOR arr[i].
Iterate on i from beginning to end, and set arr[i+1] = encoded[i] XOR arr[i].
def decode(self, A, first):
res = [first]
for a in A:
res.append(res[-1] ^ a)
return res
def decode(self, A, first):
return list(accumulate([first] + A, lambda x, y: x ^ y))
Medium
10012Add to ListShare
You are given the head of a linked list, and an integer k.
Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node from the end (the list is 1-indexed).
Example 1:
Input: head = [1,2,3,4,5], k = 2 Output: [1,4,3,2,5]
Example 2:
Input: head = [7,9,6,6,7,8,3,0,9,5], k = 5 Output: [7,9,6,6,8,7,3,0,9,5]
Example 3:
Input: head = [1], k = 1 Output: [1]
Example 4:
Input: head = [1,2], k = 1 Output: [2,1]
Example 5:
Input: head = [1,2,3], k = 2 Output: [1,2,3]
Constraints:
We can transform the linked list to an array this should ease things up
After transforming the linked list to an array it becomes as easy as swapping two integers in an array then rebuilding the linked list
This is a simple problem based on the Linked List data structure. If you have solved problems using a Linked List before and have a basic idea on how a Linked List is traversed and modified, this problem will be easy to implement.
The general idea behind the problem is that we have to find the k^{th}kth node from the beginning and the k^{th}kth node from the end of the Linked List. Once we have pointers to both of these nodes, we could simply swap their values.
The problem description states that we have to swap the values of the nodes. Therefore, the actual nodes in the Linked List should remain unchanged and there is no need to change the actual list node pointers.
In this article, we're going to look at 3 different approaches. Each approach further reduces the number of passes we need to do through the list. Let's look at the approaches in detail.
Intuition
Given the problem to swap 22 nodes in the linked list at k^{th}kth position, we need to position 22 pointers. The first pointer would point to the k^{th}kth node at the beginning of the list given by frontNode. The second pointer would point to the k^{th}kth node at the end of the list given by endNode.
Finding the position of frontNode is simple. We can start traversing from the head node until we reach the k^{th}kth node. But to find the endNode, we must first know the length of the Linked List. If the length of the Linked List is \text{listLength}listLength, the k^{th}kth node from the end would be the \text{(listLength - k)}^{th}(listLength - k)th node.
The following figure illustrates the idea.
Algorithm
As explained above, we must implement the algorithm using 3 separate passes.
Pass 1: Find the length of the Linked List by traversing each node in the list from head node to last node and increment the counter by 11. Let the counter used to find length be listLength.
Pass 2: Traverse until the k^{th}kth node from the head node and set the frontNode.
Pass 3: Traverse until the listLength - k node from the head node and set the endNode.
Swap the values of frontNode and endNode using temporary variable temp.
Return the head node.
Implementation
class Solution {
public ListNode swapNodes(ListNode head, int k) {
int listLength = 0;
ListNode currentNode = head;
// find the length of linked list
while (currentNode != null) {
listLength++;
currentNode = currentNode.next;
}
// set the front node at kth node
ListNode frontNode = head;
for (int i = 1; i < k; i++) {
frontNode = frontNode.next;
}
//set the end node at (listLength - k)th node
ListNode endNode = head;
for (int i = 1; i <= listLength - k; i++) {
endNode = endNode.next;
}
// swap the values of front node and end node
int temp = frontNode.val;
frontNode.val = endNode.val;
endNode.val = temp;
return head;
}
}
Complexity Analysis
Time Complexity : \mathcal{O}(n)O(n), where nn is the length of the Linked List. We are iterating over the Linked List thrice.
In the first pass, we are finding the length of the Linked List which would take \mathcal{O}(n)O(n) time.
In second pass and third pass, we are iterating k and n - k times respectively. This would take \mathcal{O}(k + n - k)O(k+n−k) i.e \mathcal{O}(n)O(n) time.
Thus, the total time complexity would be \mathcal{O}(n) + \mathcal{O}(n) = \mathcal{O}(n)O(n)+O(n)=O(n).
Space Complexity: \mathcal{O}(1)O(1), as we are using constant extra space to maintain list node pointers frontNode, endNode and currentNode.
Approach 1 solved the problem with three passes, in O(n)O(n) time. We can't do better than O(n)O(n), because all valid solutions must count how many nodes there are in order to be able to find the k^{th}kth last node. But while we know we can't do better than O(n)O(n), it's still often preferable to minimize the number of loops needed for Linked List problems.
Interview Tip: Reducing the number of passes/separate loops is a very common follow-up question for Linked List problems.
The first optimization we could think of over the three pass approach is that we could find the length of the Linked List as well as the position of frontNode in a single pass.
To find the length of the Linked List we are iterating from the head node until the end of the Linked List. On the way, we would also come across the k^{th}kth node from the head of the list and we could set the frontNode pointer at the point.
Algorithm
The problem can be solved in two passes with the following steps:
Iterate from the head of the Linked List until the end and store the length found in listLength. In the same loop, check if we have reached the k^{th}kth node, and if so, set frontNode to point at that node.
Iterate from head node until listLength - k node and set the endNode.
Swap the values of frontNode and endNode using temporary variable temp.
Return the head node.
Implementation
class Solution {
public ListNode swapNodes(ListNode head, int k) {
int listLength = 0;
ListNode frontNode = null;
ListNode endNode = null;
ListNode currentNode = head;
// find the length of list and set the front node
while (currentNode != null) {
listLength++;
if (listLength == k) {
frontNode = currentNode;
}
currentNode = currentNode.next;
}
// set the end node at (listLength - k)th node
endNode = head;
for (int i = 1; i <= listLength - k; i++) {
endNode = endNode.next;
}
// swap front node and end node values
int temp = frontNode.val;
frontNode.val = endNode.val;
endNode.val = temp;
return head;
}
}
Complexity Analysis
Time Complexity : \mathcal{O}(n)O(n), where nn is the length of the Linked List. We are iterating over the Linked List twice.
In the first pass, we are finding the length of the Linked List and setting the frontNode which would take \mathcal{O}(n)O(n) time.
In the second pass, we are setting the endNode by iterating n - k times.
Thus, the total time complexity would be \mathcal{O}(n) + \mathcal{O}(n - k)O(n)+O(n−k) which is equivalent to \mathcal{O}(n)O(n).
Space Complexity: \mathcal{O}(1)O(1), as we are using constant extra space to maintain list node pointers frontNode, endNode and currentNode.
Intuition
Can we implement the problem in a single pass? The problem is that to find the position of endNode we must know the length of the Linked List. And to find the length of the Linked List we must have iterated over the list before at least once.
However, we could use a trick to find the position of the pointer endNode in a single pass. The trick is:
If endNode is k positions behind a certain node currentNode, when currentNode reaches the end of linked list i.e at n^{th}nth node , the endNode would be at the n - k^{th}n−kth node.
A similar trick is used in a few other Linked List problems like Remove Nth Node From the End of List and the Fast and Slow Pointer Approach in Find The Middle Of Linked List.
The following figure illustrates the idea.
Let's see how we can implement this approach.
Algorithm
The problem can be implemented in a single pass using the following steps,
Start iterating from the head of the Linked List until the end using a pointer currentNode.
Keep track of the number of nodes traversed so far using the variable listLength. The listLength is incremented by 11 as each node is traversed.
If listLength is equal to k, we know that currentNode is pointing to the k^{th}kth node from the beginning. Set frontNode to point to the k^{th}kth node. Also, at this point, initialize endNode to point at the head of the linked list. Now we know that endNode is k nodes behind the head node.
If endNode is not null, we know that it is positioned k nodes behind the currentNode and so we increment endNode in addition to currentNode. When currentNode reaches the end of the list, endNode would be pointing at a node which is k nodes behind the last node.
Swap the values of frontNode and endNode using temporary variable temp.
Return the head node.
Implementation
class Solution {
public ListNode swapNodes(ListNode head, int k) {
int listLength = 0;
ListNode frontNode = null;
ListNode endNode = null;
ListNode currentNode = head;
// set the front node and end node in single pass
while (currentNode != null) {
listLength++;
if (endNode != null)
endNode = endNode.next;
// check if we have reached kth node
if (listLength == k) {
frontNode = currentNode;
endNode = head;
}
currentNode = currentNode.next;
}
// swap the values of front node and end node
int temp = frontNode.val;
frontNode.val = endNode.val;
endNode.val = temp;
return head;
}
}
Complexity Analysis
Time Complexity : \mathcal{O}(n)O(n), where nn is the size of Linked List. We are iterating over the entire Linked List once.
Space Complexity: \mathcal{O}(1)O(1), as we are using constant extra space to maintain list node pointers frontNode, endNode and currentNode.
Medium
2128Add to ListShare
You are given two integer arrays, source and target, both of length n. You are also given an array allowedSwaps where each allowedSwaps[i] = [ai, bi] indicates that you are allowed to swap the elements at index ai and index bi (0-indexed) of array source. Note that you can swap elements at a specific pair of indices multiple times and in any order.
The Hamming distance of two arrays of the same length, source and target, is the number of positions where the elements are different. Formally, it is the number of indices i for 0 <= i <= n-1 where source[i] != target[i] (0-indexed).
Return the minimum Hamming distance of source and target after performing any amount of swap operations on array source.
Example 1:
Input: source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]] Output: 1 Explanation: source can be transformed the following way: - Swap indices 0 and 1: source = [2,1,3,4] - Swap indices 2 and 3: source = [2,1,4,3] The Hamming distance of source and target is 1 as they differ in 1 position: index 3.
Example 2:
Input: source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = [] Output: 2 Explanation: There are no allowed swaps. The Hamming distance of source and target is 2 as they differ in 2 positions: index 1 and index 2.
Example 3:
Input: source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]] Output: 0
Constraints:
The source array can be imagined as a graph where each index is a node and each allowedSwaps[i] is an edge.
Nodes within the same component can be freely swapped with each other.
For each component, find the number of common elements. The elements that are not in common will contribute to the total Hamming distance.
Hard
1376Add to ListShare
You are given an integer array jobs, where jobs[i] is the amount of time it takes to complete the ith job.
There are k workers that you can assign jobs to. Each job should be assigned to exactly one worker. The working time of a worker is the sum of the time it takes to complete all jobs assigned to them. Your goal is to devise an optimal assignment such that the maximum working time of any worker is minimized.
Return the minimum possible maximum working time of any assignment.
Example 1:
Input: jobs = [3,2,3], k = 3 Output: 3 Explanation: By assigning each person one job, the maximum time is 3.
Example 2:
Input: jobs = [1,2,4,7,8], k = 2 Output: 11 Explanation: Assign the jobs the following way: Worker 1: 1, 2, 8 (working time = 1 + 2 + 8 = 11) Worker 2: 4, 7 (working time = 4 + 7 = 11) The maximum working time is 11.
Constraints:
We can select a subset of tasks and assign it to a worker then solve the subproblem on the remaining tasks
def minimumTimeRequired(self, A, k):
n = len(A)
A.sort(reverse=True) # opt 1
def dfs(i):
if i == n: return True # opt 3
for j in xrange(k):
if cap[j] >= A[i]:
cap[j] -= A[i]
if dfs(i + 1): return True
cap[j] += A[i]
if cap[j] == x: break # opt 2
return False
# binary search
left, right = max(A), sum(A)
while left < right:
x = (left + right) / 2
cap = [x] * k
if dfs(0):
right = x
else:
left = x + 1
return left
leetcode.com/problems/find-minimum-time-to-finish-all-jobs/discuss/1010057/Python-Binary-search-24ms