Sword Offer Problems
Sword Offer Problems
1. Duplicated Integers in Array
In an array of size n, all entries are integers and are in the range [0, n-1]
. However, there are some duplicated integers. Please find out an arbituary duplicated integer. For example:
1 | Input: |
To solve this problem, we can use hashtable, and the time complexity is O(n)
. However, the space complexity is also O(n)
, can we reduce space complexity?
We know that all entries are in the range [0, n-1]
, suppose there is no duplicate, when we sort the array in ascending order, the integer at index i
would be i
, namely:
[0, 1, 2, ... , n-2, n-1]
Because there are some duplicates, if we rearrange the array to make sure that the integer at index i
is i
, there are two possibilities for the rearranged array:
there is no entry at index
i
there are two or more entries at index
i
Take [2, 3, 1, 0, 2, 5, 3]
as an example, we can scan and rearrange the array like this:
1 | We start from index 0: |
In this way, the time complexity is O(n)
, and because we just modify the original array and do not need to allocate extra memory, the space complexity is O(1)
.
If the problem does not allow us to modify the original array, we need to have an extra array of size n
, and then traverse the original array, copy each entry into the new array:
1 | We start from index 0: |
In this way, the time and space complexity are all O(n)
. Can we reduce the space complexity?
There is a trade-off between time complexity and space complexity, if we want to reduce the space complexity, we need to increase time complexity. Take [2, 3, 1, 0, 2, 5, 3]
as an example, we can use binary search
to solve this problem:
The length of [2, 3, 1, 0, 2, 5, 3] is 7, therefore we know that all the entries are in the range [0,6]. We split the array into two parts based on the middle value
(0+6)/2 = 3
:- one part is from 0 to 3, i.e.
[2, 3, 1, 0, 2, 3]
- the other part is from 4 to 6, i.e
[5]
- one part is from 0 to 3, i.e.
There are 5 integers from 4 to 6, which means there is no duplicate in this part. There are 6 integers from 0 to 3, if there is no duplicte, there should be 4 integers, therefore we know there are duplicates in this part.
For the part
[2, 3, 1, 0, 2, 3]
, we split the array into two parts based on the middle value(0+3)/2 = 1
:- one part is from 0 to 1, i.e.
[1, 0]
- the other part is from 2 to 3, i.e
[2, 3, 2, 3]
- one part is from 0 to 1, i.e.
There are 2 integers from 0 to 1, which means there is no duplicate in this part. There are 4 integers from 2 to 3, if there is no duplicte, there should be 2 integers, therefore we know there are duplicates in this part.
For the part
[2, 3, 2, 3]
, we split the array into two parts based on the middle value(2+3)/2 = 2
:- one part is from 2 to 2, i.e.
[2, 2]
- the other part is from 3 to 3, i.e
[3, 3]
- one part is from 2 to 2, i.e.
There are 2 integers from 2 to 2, which means 2 is duplicated. There are 2 integers from 3 to 3, which means 3 is also duplicated.
Here is the code:
1 | public class DuplicatedArray { |
For the above code, the time complexity is O(nlogn)
and the space complexity is O(1)
. We reduce the space complexity by increasing the time complexity.
2. Replace Space with “%20”
Given a String, replace all the spaces in the String with “%20”. For example:
1 | Input: |
To solve this problem, we can use StringBuilder
:
1 | public class ReplaceWithSpace { |
3. Print a Singly-linked List in Reverse Order
Given a head pointer of a singly-linked list, print the value of each node in reverse order.
There are two ways to solve this problem:
Iteration: We can traverse the linked list, and put each node’s value into a
stack
. Then we can pop and print the values from the stack.Recursion: Actually, recursion also makes use of a
stack
, i.e. thecall stack
.
Here is the code for recursion:
1 | public class PrintLinkedListInReverseOrder { |
4. Rebuild a Binary Tree
Rebuild a binary tree based on the preorder traversal and inorder traversal result. For example, given the preorder traversal {1, 2, 4, 7, 3, 5, 6, 8}
and the inorder traversal {4, 7, 2, 1, 5, 3, 8, 6}
, we can rebuild the following binary tree:
1 | 1 |
We know that for preorder traversal, the first node to be traversed is always the root node, then we traverse the left subtree, and then the right subtree. And for inorder traversal, we traverse left subtree first, then the root node and the right subtree. Therefore, by combining the two traversals, we can find out the root node, left subtree and right subtree:
For the left subtree and right subtree, we can find out the root node, leftsubtree and right subtree recursively.
Here is the code to solve the problem:
1 | public class ReconstructBinaryTree { |
5. The Next Node of Binary Tree
Given a binary tree and a node, how can we find the next node of this node according to the inorder traversal? For example, we have a tree like this:
And we know that the inorder traversal of the tree is {d, b, h, e, i, a, f, c, g}
.
According to the inorder traversal, d
‘s next node is b
, b
‘s next node is h
…
How can we implement this in Java? There are two cases:
If a node has right subtree: for example, the node
a
, its next node is the leftmost node of its right subtree, i.e.f
.If a node does not have right subtree:
- If this node is the left child of its parent node, say
h
, then the parent nodee
is the next node. - If this node is the right child of its parent node, say
i
, to find its next node, we need to find its parent nodee
. Becausee
is its parent nodeb
‘s right child, we keep going up tob
, and becauseb
is its parent nodea
‘s left child, we can knowb
‘s parent nodea
isi
‘s next node. To sum up, we should firstly find a node, which is the parent node ofi
, or the parent’s parent…, and this node should also be the lelf child of its own parent node. If we can find a node like this, then this node’s parent node is the next node ofi
. If we cannot find a node like this(e.g. the nodeg
), then we can sayg
is the last node, which does not have a next node.
- If this node is the left child of its parent node, say
Here is the Java code:
1 | public class GetNextNode { |
6. Using Two Stacks to Implement a Queue
We know that a stack is LIFO(last in, first out), and a queue is FIFO(first in, first out). For stack, we have the method push
and pop
, and for queue, we have the method enqueue
and dequeue
. Considering the properties of stack and queue, we can use two stacks to implement a queue:
1 | import java.util.Stack; |
Here is a good video to explain the above code: https://www.youtube.com/watch?v=AN0axYeLue0
Similarly, we can use two queues to implement a stack, and here is a good video about how to do this: https://www.youtube.com/watch?v=ww5Ac232WEU
7. Sorting Ages
Suppose we have a big array, which stores the ages of many people. Now we want to sort the ages, and the time complexity should be O(n)
. We can reduce the time complexity by increasing the space complexity, i.e. using an extra array timesOfAge
:
1 | public class SortAges { |
As shown, the time complexity of the above code is O(n) + 100*O(n) = O(n)
.
8. Cut the Rope
Given a rope of length n
, we can cut it into m
parts(m, n are integers and m>1, n>1). Each part i
of the rope is recorded as k[i], i.e. k[1], … , k[m]. Now we want to know the maximum value of k[1]x…xk[m].
For example, if n=8, when we cut the rope into 3 parts(m=3), i.e. k[1]=2,k[2]=3,k[3]=3. In this way, we can get the maximum product of k[i], which is 18.
The first way to solve this problem is to use dynamic programming
:
1 | package sword_offer; |
As shown above, we start with the simplest case, i.e. the rope is of length 0 or 1, we cannot cut the rope, therefore we return 0.
When the rope is of length 2, we can only cut it into two parts, i.e. k[1] = k[2] = 1, and the product is 1. Therefore we return 1.
When the rope is of length 3, we can cut it into 3 parts(k[1]=k[2]=k[3]=1) or 2 parts(k[1]=1, k[2]=2), and the maximum product is 2.
Then, when the rope is of length 4, we can cut it once, into 1+3 or 2+2, and we can know that dp[4] = max(dp[1]*dp[2], dp[2]*dp[2])
.
By using dynamic programming, the time complexity is O(n^2)
and the space complexity is O(n)
(we use a dp array to store the results).
9. Number of 1s
Given an integer, output the number of 1
s in the binary representation of this integer. For example, given the integer 9
, which is 1001
in binary, the number of 1
s is 2, therefore we output 2
.
We can use bit manipulation to solve this problem.Take integer 9 as an example:
(1001 & 0001) != 0, and we can know the fourth digit of 9 is 1
(1001 & 0010) == 0, and we can know the third digit of 9 is not 1
(1001 & 0100) == 0, and we can know the second digit of 9 is not 1
(1001 & 1000) != 0, and we can know the second digit of 9 is 1
Therefore there are two 1
s in 9
. Here is the code:
1 | public class NumOfOnes { |
And here is a good video to explain this problem: https://www.bilibili.com/video/BV1Y7411z7Yv?from=search&seid=1959341215906372645
10. Print From 1 to Maximum n-digit Number
Given an integer n
, print from 1 to the maximum n-digit number. For example, given the integer 3, the maximum n-digit number is 999, therefore we should print 1,2,3… to 999.
As n
can be very large, the maximum n-digit number could be larger than the maximum long
integer. Therefore we can use n-digit String
to represent each number.
For example, if n = 3, we can use “001” to represent 1, “002” to represent 2, … “999” to represent 999. And when we print each String
, we should erase the 0s in the prefix.
Now, our problem becomes: given a String
of length n
, each digit of the String
can be filled with “0”, “1”, “2”, …, “9”, how many possible combinations are there?
To solve this problem, we should use DFS
:
1 | public class Print1ToMaxOfNDigits { |
As shown, for DFS
, each layer has each its own responsibility, that is, it just need to set the corresponding digit(from 0 to 9), and then deliver the job to the deeper layers. The process is like this: