15. 3Sum
This question is similar with 1. Two Sum.But they have some different,the main different is this question is the solution set must not contain duplicate triplets.So we can't just use enumeration method,we need to think about how to avoid duplication.In the past,there were two main ways to avoid duplication.One is use HashMap,the other one is this array is ordered.If we just use enumeration method to traverse the entire array and then use HashMap.It will cost a lot of time and lost of space.So we should sort this array at first.And we can set center pointn
.There three pointer are n,n+1,nums.length-1
.Because this array is ordered,so if the n
is the head of the array,so it means the all elements after n
are all >0
.So we don't need to traverse all the elements.But this is just a special situation.
And how can we avoid duplication?This is a easy question,there three pointercenter n,left n+1,right nums.length-1
.If the value of their next position is equal to the current,we move the current position to the next.
Solution
public List<List<Integer>> threeSum(int[] nums) {
ArrayList<List<Integer>> lists = new ArrayList<>();
//the special situation
if (nums.length < 3) {
return lists;
}
//sort the array
Arrays.sort(nums);
//traversal the array
for (int i = 0; i < nums.length; i++) {
//if the smallest element is larger than 0,so all the elements are larger than 0,just break
if (nums[i] > 0) {
break;
}
//repetitive situation,just update the location
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
//use two pointers,search from both sides
int left = i + 1, right = nums.length - 1;
while (left < right) {
//find the 3sum
if (nums[i] + nums[left] + nums[right] == 0) {
//use Arrays.aslist make array to list
lists.add(Arrays.asList(nums[i], nums[left], nums[right]));
//repetitive situation,just update the location
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
//update location
left++;
right--;
} else if (nums[i] + nums[left] + nums[right] > 0) {
//if larger than 0,it means right pointer is too bigger
right--;
} else {
//if smaller than 0,it means left pointer is too smaller
left++;
}
}
}
return lists;
}
16. 3Sum Closest
This question is very similar with question 15. 3Sum.But it does not query to repeat this step,you can use double pointers directly.So i think this question is easier than question 15.
Solution
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int result = 0, min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
//if find the same number,just continue
if (i > 0 && nums[i - 1] == nums[i]) {
continue;
}
int left = i + 1, right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
//if find the target,just return
if (sum == target) {
return sum;
}
//find the closest
if (Math.abs(sum - target) < min) {
min = Math.abs(sum - target);
result = sum;
}
//move the double pointers
if (sum > target) {
right--;
} else {
left++;
}
}
}
return result;
}
17. Letter Combinations of a Phone Number
This question is a little difficult,the difficulty lies in how to stitch different letters together.It cannot use enumeration methods,because we don't know how many digits it will enter. So we can't use how many for
to use enumeration methods.At this time,we should think of the problem of finding all branches of a binary tree.We didn't know the number of layers of the binary tree at the time.We need to judge the left subtree and the right subtree whether is null.And use recursive methods to return the values found in each layer.
So we can use the same method in this problem.First traverse the value of digits,each of its values is a layer.Finally,concatenating the traversed values is one of the answer.
And how do we associate phone numbers with letters.We can use HashMap,and use StringBuilder to connect the letters.
Solution
public List<String> letterCombinations(String digits) {
ArrayList<String> list = new ArrayList<>();
//the special situation
if (digits.length() == 0) {
return list;
}
//create the HashMap
HashMap<Character, String> map = new HashMap<>();
map.put('2', "abc");
map.put('3', "def");
map.put('4', "ghi");
map.put('5', "jkl");
map.put('6', "mno");
map.put('7', "pqrs");
map.put('8', "tuv");
map.put('9', "wxyz");
backTracking(0, list, map, new StringBuffer(), digits);
return list;
}
/**
* use recursion to connect the letters
*/
private void backTracking(int i, ArrayList<String> list, HashMap<Character, String> map,
StringBuffer buffer, String digits) {
//if it reaches the end,just return the letters you find
if (i == digits.length()) {
list.add(buffer.toString());
} else {
//such as digits.charAt(i) is '2',and search 2 in HashMap
String s = map.get(digits.charAt(i));
//traverse the number
for (int i1 = 0; i1 < s.length(); i1++) {
//add the last letters
buffer.append(s.charAt(i1));
//recursion
backTracking(i + 1, list, map, buffer, digits);
/*it also writes buffer.deleteCharAt(buffer.length()-1),if you don't write,StringBuilder
will pollute the buff,StringBuilder passes in the same object,
so the last operation must be withdrawn after the recursion is completed,
and the last added character needs to be deleted*/
buffer.deleteCharAt(i);
}
}
}
18. 4Sum
This problem is an upgraded version of 15. 3Sum.The main idea to the original question is the same as question 13.It also use double pointer.But i think if this question change to nSum
,are there still double pointers used?Because this question is already very complicated to solve with double pointers.So i want to use recursion to solve this question.
There are two situations in recursion.First we still sort the array,and we remove the first element of the array every time.
- the result we want include the
nums[0]
- the result we want does not include
nums[0]
So this question becomes
- find
target-nums[0]
fromnums[1]
tonums[nums.length]
- find
target
fromnums[1]
tonums[nums.length]
But if the elements behind the first element are the same as the first element.We must skip all the elements in this paragraph.
Because recursion takes more time and space.So we must reduce the time spent.nums[0]>target/n
is a good thinking.But we do LeetCode question not only to solve the problem,but to learn this kind of thinking,just like recursion.Will make the code we write more code robustness.
Solution
public List<List<Integer>> fourSum(int[] nums, int target) {
//this question requires us 4
return nSum(nums, target, 4);
}
/**
* n is the number of digits we need
*/
private List<List<Integer>> nSum(int[] nums, int target, int n) {
//first sort the array
Arrays.sort(nums);
//then use recursion
return nSum(nums, target, n, 0, new ArrayList<Integer>(), new ArrayList<List<Integer>>());
}
/**
* startIndex is position to start looking for
*/
private List<List<Integer>> nSum(int nums[], int target, int n, int startIndex,
ArrayList<Integer> preArray, ArrayList<List<Integer>> result) {
//if length is 5,start begin 4,we need find 3 numbers,this is impossible,so just return
if (nums.length - startIndex < n) {
return result;
}
//if only find one element,we just traversal the array,until we find the element
if (n == 1) {
for (int i = startIndex; i < nums.length; i++) {
if (nums[i] == target) {
preArray.add(target);
result.add(preArray);
break;
}
}
return result;
}
//special situation
if (nums[startIndex] > target / n) {
return result;
}
//filter out the same elements at the beginning
int same = 0;
for (int i = startIndex + 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1]) {
//record the number of identical elements
same++;
} else {
break;
}
}
//the situation 2,the first element is not we need to find
nSum(nums, target, n, startIndex + same + 1, preArray, result);
//the situation 1,the first element is we need to find
ArrayList<Integer> copyPre = new ArrayList<>(preArray);
copyPre.add(nums[startIndex]);
nSum(nums, target - nums[startIndex], n - 1, startIndex + 1, copyPre, result);
return result;
}
19. Remove Nth Node From End of List
This question is a very interesting question,as we all know how to remove the n
node.But how to remove the node from the end of the list.Search from the beginning,but delete from the end.This situation reminds me about Stack FILO
.So we can push elements into stack at first,and pop n-1
elements.So we can remove the n
element.
- There is one thing we must be notice,If the
n
node from the end of the list.The numbern
is the list's length.So we remove the head,so we can't return the head,we must return then
next node.
Solution1
public ListNode removeNthFromEnd(ListNode head, int n) {
//create a stack,and push all elements into stack
Stack<ListNode> stackNode = new Stack<>();
ListNode temp = head;
while (temp != null) {
stackNode.push(temp);
temp = temp.next;
}
//pop n-1 elements
for (int i = 0; i < n - 1; i++) {
stackNode.pop();
}
//pop the n element
ListNode node = stackNode.pop();
//if the n is the first of the lists,so this stack is empty,just return the node.next
//else just remove the n element
if (!stackNode.isEmpty()) {
ListNode deleteNode = stackNode.peek();
deleteNode.next = deleteNode.next.next;
} else {
return node.next;
}
return head;
}
This question we also can use two pointers.The fast pointer moves n
more nodes than slow pointer.When they move together,when the fast pointer reaches the tail node,the slow pointer also reaches the n
node.
Solution2
public ListNode removeNthFromEnd(ListNode head, int n) {
//the slow pointer is one node slower than the fast pointer
ListNode pre = new ListNode(0);
pre.next = head;
ListNode fast = head;
ListNode slow = pre;
//fast pointer moves n more nodes than slow
for (int i = 0; i < n; i++) {
fast = fast.next;
}
//because fast move to tail node,still move to the null node,so move one more step
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
//if the n node the head of the list,so we can't return the head,we must return the pre next
slow.next = slow.next.next;
return pre.next;
}
22. Generate Parentheses
This question is very similar with Binary Tree traversal.As we all know (
must match )
.And the number of (
is the same as the number of )
.If the number of )
is bigger than the number of (
,it is a wrong match.So we can summarize 2 points.
- If the
n
bracket combinations,there aren
(
andn
)
- If at this time the number of
(
ism
.The number of)
at mostm
So we can use the same solution in Binary Tree.We start at a root node,this node is empty.We can add (
or )
latter.(
is just like the left subtree.)
is just like the right subtree.When the number of subtree is less than n
,it will continue to depth first traversal.
Solution
/**
* define a ArrayList to store the parentheses
*/
ArrayList result = new ArrayList<String>();
public List<String> generateParenthesis(int n) {
//the default number of left subtree and right subtree are all 0
dfs(n, 0, 0, "");
return result;
}
/**
* Depth first traversal
*/
private void dfs(int n, int left, int right, String str) {
if (left == n && right == n) {
//traversed to the bottom of the tree,just return the result
result.add(str);
} else {
if (left < n) {
//add the `(`
dfs(n, left + 1, right, str + "(");
}
//the number of `)` at most the number of `(`
if (right < n && right < left) {
dfs(n, left, right + 1, str + ")");
}
}
}
24. Swap Nodes in Pairs
This question is a very interesting question,because it has many solutions.Can develop our thinking to think more.
Queue
Of course,we can use Queue.Because the characteristic of Queue is first in first out.I make a example.
1 2 3 4
Queue1:1 3
Queue2:2 4
First we create two Queues,and alternate each element.And then take out each element alternately.
Queue:1 3
Queue:4
2
Queue1:3
Queue2:4
2 1
Queue1:3
Queue2:
2 1 4
Queue1
Queue2
2 1 4 3
This difficulty of this solution is when to put it in Queue1 and when to put it in Queue2.And when to pop out.
public ListNode swapPairs(ListNode head) {
//the special situation
if (head == null || head.next == null) {
return head;
}
//make two Queue
LinkedList<ListNode> list1 = new LinkedList<>();
LinkedList<ListNode> list2 = new LinkedList<>();
//we can use boolean,if it is true,add Queue1,if it is false,add Queue2
boolean add = true;
while (head != null) {
if (add) {
list1.add(head);
add = false;
} else {
list2.add(head);
add = true;
}
head = head.next;
}
//We must remove a node to be the head of the result listnode
ListNode res = list2.remove();
ListNode temp = res;
//we also can make a boolean to minus each Queue,if it is true,minus Queue1,else minus Queue2
boolean minus = true;
while (!list1.isEmpty() || !list2.isEmpty()) {
if (!list1.isEmpty() && minus == true) {
temp.next = list1.remove();
minus = false;
} else {
//there are two situations in else,one is list2 is empty,we just need to minus Queue1 at
// last,the another one is list1 is not empty
if (!list2.isEmpty()) {
temp.next = list2.remove();
minus = true;
} else if (!list1.isEmpty()) {
temp.next = list1.remove();
}
}
//update temp node
temp = temp.next;
}
//if don't add this,it will happen infinite loop
if (temp != null) {
temp.next = null;
}
return res;
}
recursion
We can also use recursion.Let's analyze this question.We can decompose the problem into
A B C
B A C
So this fake code is
//swap is like B
swap = A.next;
//swap.next is like C
A.next=recursion(swap.next);
swap.next=A;
return swap;
public ListNode swapPairs(ListNode head) {
//the special situation
if (head == null || head.next == null) {
return head;
}
ListNode swap = head.next;
head.next = swapPairs(swap.next);
swap.next = head;
return swap;
}
Iteration
In iteration,we can also consider ABC
as a group.Iteration is very complicated to tell,but it will be easy to understand if it is shown in a diagram.
public ListNode swapPairs(ListNode head) {
//the special situation
if (head == null || head.next == null) {
return head;
}
//make a result node,to point to head
ListNode result = new ListNode(0);
result.next = head;
ListNode temp = result;
//iteration
while (temp.next != null && temp.next.next != null) {
ListNode list1 = temp.next;
ListNode list2 = temp.next.next;
temp.next = list2;
list1.next = list2.next;
list2.next = list1;
temp = list1;
}
//return the head
return result.next;
}