Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e.,0 1 2 4 5 6 7might become4 5 6 7 0 1 2).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
在一个旋转过的有序数组中查找一个数,如果该数不存在,返回-1。
首先通过二分查找找出数组中最小值的位置。这样我们可以将数组分成2段,使得每一段都是有序的。之后看要查找的数(target)的值落在哪一段,然后再在这一段里进行一次二分查找即可。
对于每一轮二分查找,中点的左侧和右侧必然有有一边是有序的。我们看看target是不是在有序的这个范围内,是的话就查这一边,不是的话就查另一边。
public class Solution {
public int search(int[] nums, int target) {
// First binary search for finding the index of minimum value.
int lo = 0;
int hi = nums.length - 1;
while(lo < hi){
int mi = lo + (hi - lo) / 2;
if(nums[mi] > nums[hi]) lo = mi + 1;
else hi = mi;
}
int min = nums[lo];
// Second binary search for finding target.
if(target >= min && target <= nums[nums.length - 1]){
hi = nums.length - 1;
}
else {
hi = lo - 1;
lo = 0;
}
while(lo <= hi){
int mi = lo + (hi - lo) / 2;
if(nums[mi] == target) return mi;
else if(nums[mi] < target) lo = mi + 1;
else hi = mi - 1;
}
return -1;
}
}
public class Solution {
public int search(int[] nums, int target) {
int lo = 0, hi = nums.length - 1;
while (lo <= hi) {
int mi = lo + (hi - lo)/2;
if (nums[mi] == target) return mi;
if (nums[lo] < nums[mi]) { //left half is sorted
if (nums[lo] <= target && lo < mi && target <= nums[mi - 1])
hi = mi - 1;
else lo = mi + 1;
} else { //right half is sorted
if (mi < hi && nums[mi + 1] <= target && target <= nums[hi])
lo = mi + 1;
else hi = mi - 1;
}
}
return -1;
}
}
]]>
假设我们用2个数组preorder和inorder来表示前序和中序遍历的结果。
要生成的二叉树的任意子树都对应于preorder和inorder的某个子数组。该子树的根节点对应于preorder子数组的第一个元素,然后在inorder中查找该该元素。对于inorder中的的子数组,根节点元素左侧的子数组对应左子树,右侧的子数组对应右子树。之后使用递归重复该过程。
下面给出一段Java示例代码:
public class Solution {
private int[] preorder;
private int[] inorder;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
this.inorder = inorder;
return build(0, 0, preorder.length);
}
private TreeNode build(int pl, int il, int size){
if(size <= 0) return null;
TreeNode root = new TreeNode(preorder[pl]);
int m = 0; // m is the size of left sub-tree
for(; m < size; m++){
if(inorder[il + m] == root.val) break;
}
TreeNode left = build(pl + 1, il, m);
TreeNode right = build(pl + m + 1, il + m + 1 ,size - m - 1);
root.left = left;
root.right = right;
return root;
}
}
假设我们用2个数组inorder和postorder来表示中序和后序遍历的结果。
与已知前序和中序的情况类似,要生成的二叉树的任意子树都对应于inorder和postorder的某个子数组。该子树的根节点对应于postorder子数组的最后一个元素,然后在inorder中查找该该元素。对于inorder中的的子数组,根节点元素左侧的子数组对应左子树,右侧的子数组对应右子树。之后使用递归重复该过程。
下面给出一段Java示例代码:
public class Solution {
private int[] postorder;
private int[] inorder;
public TreeNode buildTree(int[] inorder, int[] postorder) {
this.postorder = postorder;
this.inorder = inorder;
return build(0, 0, postorder.length);
}
private TreeNode build(int pl, int il, int size){
if(size <= 0) return null;
TreeNode root = new TreeNode(postorder[pl + size - 1]);
int m = 0; // m is the size of left sub-tree
for(; m < size; m++){
if(inorder[il + m] == root.val) break;
}
TreeNode left = build(pl, il, m);
TreeNode right = build(pl + m, il + m + 1 ,size - m - 1);
root.left = left;
root.right = right;
return root;
}
}
这种情况无法唯一确定一棵二叉树。
一个最简单的例子就是:
前序遍历[1,2],后序遍历[2,1]。
显然根节点是1,但是节点2既可以是左子节点,也可以使右子节点。无法唯一确定。
]]>Clone an undirected graph. Each node in the graph contains a
labeland a list of itsneighbors
拷贝一个图。每个节点中用List存储它的邻居节点。
基本思路就是遍历原图,使用一个map来记录已经访问过的节点以及它和新节点的对应关系。
遍历可以考虑DFS或者BFS。下面的代码是使用BSF实现的。
Java
/**
* Definition for undirected graph.
* class UndirectedGraphNode {
* int label;
* List<UndirectedGraphNode> neighbors;
* UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); }
* };
*/
public class Solution {
public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) {
if(node == null) return null;
Map<UndirectedGraphNode, UndirectedGraphNode> map =
new HashMap<UndirectedGraphNode, UndirectedGraphNode>();
Queue<UndirectedGraphNode> queue = new LinkedList<UndirectedGraphNode>();
UndirectedGraphNode entrance = new UndirectedGraphNode(node.label);
map.put(node, entrance);
queue.add(node);
while(!queue.isEmpty()){
UndirectedGraphNode oldNode = queue.poll();
UndirectedGraphNode newNode = map.get(oldNode);
for(UndirectedGraphNode oldNeighbor : oldNode.neighbors){
if(!map.containsKey(oldNeighbor)){
map.put(oldNeighbor, new UndirectedGraphNode(oldNeighbor.label));
queue.add(oldNeighbor);
}
newNode.neighbors.add(map.get(oldNeighbor));
}
}
return entrance;
}
}
]]>
Sort a linked list in O(n log n) time using constant space complexity.
排序一个链表。要求O(nlogn)时间,O(1)空间。
说到O(nlogn)时间,首先想到的是合并排序。虽然合并排序对于数组是O(n)空间,但是对于链表却可以做到O(1)空间。
Java
public class Solution {
// Merge sort: O(nlogn) time, O(1) space
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode p1 = head;
ListNode p2 = head.next;
// Find the midpoint
while(p2 != null && p2.next != null){
p1 = p1.next;
p2 = p2.next.next;
}
p2 = sortList(p1.next);
p1.next = null;
p1 = sortList(head);
return merge(p1, p2);
}
private ListNode merge(ListNode h1, ListNode h2){
ListNode fakeHead = new ListNode(Integer.MIN_VALUE);
ListNode p = fakeHead;
while(h1 != null && h2 != null){
if(h1.val < h2.val){
p.next = h1;
h1 = h1.next;
}
else{
p.next = h2;
h2 = h2.next;
}
p = p.next;
}
p.next = (h1 == null) ? h2 : h1;
return fakeHead.next;
}
}
]]>
Sort a linked list using insertion sort.
使用插入排序法对一个链表进行排序。
没有什么特别深奥之处。注意处理好边界情况即可。
Java
public class Solution {
public ListNode insertionSortList(ListNode head) {
if(head == null) return head;
ListNode fakeHead = new ListNode(1 << 31);
fakeHead.next = head;
ListNode tail = head;
while(tail.next != null){
// If the current checked node is the largest one, don't move it.
// Go check next.
if(tail.next.val >= tail.val) tail = tail.next;
else{
ListNode p = fakeHead;
while(true){
if(p.next != null && p.next.val > tail.next.val){
ListNode next = p.next;
p.next = tail.next;
tail.next = tail.next.next;
p.next.next = next;
break;
}
p = p.next;
}
}
}
return fakeHead.next;
}
}
]]>
Given an absolute path for a file (Unix-style), simplify it.
For example,
path ="/home/", =>"/home"
path ="/a/./b/../../c/", =>"/c"
路径化简。
基本思路就是使用栈来模拟进入某一路径或者返回上一级。
注意以下边界情况:
Java
public class Solution {
public String simplifyPath(String path) {
StringBuilder sb = new StringBuilder();
Stack<String> stack = new Stack<String>();
for(int i = 1, prevSlash = 0; i <= path.length(); i++){
if(i == path.length() || path.charAt(i) == '/'){
String folder = path.substring(prevSlash + 1, i);
prevSlash = i;
if(folder.equals("..")){
if(!stack.isEmpty()) stack.pop();
}
else if(!folder.equals(".") && folder.length() > 0)
stack.push(folder);
}
}
while(!stack.isEmpty()) sb.insert(0, "/" + stack.pop());
return sb.length() == 0 ? "/" : sb.toString();
}
}
]]>
Given an array of numbers
nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
For example:
Givennums = [1, 2, 1, 3, 2, 5], return[3, 5].Note:
- The order of the result is not important. So in the above example,
[5, 3]is also correct.- Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
数组中除了2个数以外,其他所有的数都是成对的。要求把这2个数找出来。
本题也用到了异或运算的性质:A^A = 0, A^B^A = B
首先遍历数组,将数组中所有的数进行异或之后,得到值就等于不成对的两个数的异或值。例如,对于[1,2,1,3,2,5],异或所有的数得到6,这也正是其中不成对的两个数3和5的异或结果。
得到这两个数的异或结果有什么用的?怎样把他们分开?
我们知道,既然两个数不成对,那么他们之间至少存在一个二进制位是不同的。我们可以通过这个不同的位来区分这两个数。对于异或运算的结果,1表示原来的两个数在这一位上不同。现在找到异或结果的任何一个置为1的位即可。还是看刚才的例子,3和5的异或运算如下:
3 (0011)
xor 5 (0101)
= 6 (0110)
现在我们想知道异或运算结果的某个为1的位。最简单暴力的方法就是遍历每一位,看是不是1。但是,我们有一个更tricky的方法:一个数和它的相反数做与运算,得到的结果就是该数的最低的为1的位。还是用刚才的例子(为了简洁,我们假设是4位二进制补码):
+6 (0110)
and -6 (1010)
= (0010)
现在我们就知道了,3和5在倒数第二位上有所不同。再次遍历数组,只是这次我们根据倒数第二位上是0还是1,把数组中的数分成两组。这样可以保证3和5一定不在同一组。然后再对2组分别进行异或运算,即可得到最后的结果。
Java
public class Solution {
public int[] singleNumber(int[] nums) {
int[] res = new int[2];
int temp = 0;
for(int n : nums) temp ^= n;
temp &= -temp; // Get the last set bit
for(int n : nums){
if((n & temp) == 0) res[0] ^= n;
else res[1] ^= n;
}
return res;
}
}
相关文章:
《LeetCode #136 Single Number》
《LeetCode #137 Single Number II》
参考文章:
]]>Given a complete binary tree, count the number of nodes.
给定一个完全二叉树的根节点,求该完全二叉树节点的个数。
完全二叉树就是:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。
本题的关键就是求二叉树最后一层的节点个数。对此我们可以使用二分查找。
使用一个整数来表示从根节点到叶子节点的路径,每一位代表方向(0向左,1向右)。例如,1个4层的二叉树,如果路径为5,写成二进制是101,就代表从根节点出发,右,左,右。
通过二分查找,我们可以得到最后一层第一个空节点的位置,也就知道了最后一层有几个几点。然后加上上面几层的节点数就是答案。
Java
public class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
int level = 0;
TreeNode p = root;
while(p != null){
level++;
p = p.left;
}
if(level == 1) return 1;
int lo = 0;
int hi = 1 << (level - 1);
// Find the position of first null in last level
while(lo < hi){
int mi = lo + (hi - lo) / 2;
if(access(root, level, mi) == null) hi = mi;
else lo = mi + 1;
}
return (1 << (level - 1)) + lo - 1;
}
private TreeNode access(TreeNode root, int level, int path){
TreeNode p = root;
for(int i = level - 2; i >= 0; i--){
int direction = (path >> i) & 1;
if(direction == 0) p = p.left;
else p = p.right;
}
return p;
}
}
]]>
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given[3,2,1,5,6,4]and k = 2, return 5.
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
查找无序数组中第K大的数。
最简单暴力的方法就是先对整个数组排序,然后返回 nums[k-1] 即可。时间复杂度O(nlogn)。
有没有更高效的方法呢?我们不妨参考快速排序的思路,但是不对整个数列排序。
partition(int[] a, int lo, int hi)方法和快速排序一样,选取该分段中的第一个数为标兵(pivot)。所有大于pivot的数放到其左侧,小于pivot的数放到其右侧。最后返回pivot的位置。在经过一次partition方法之后,根据k和pivot的位置,来选择是对左侧部分还是对右侧部分继续进行partition。该方法时间复杂度为O(n)。
例如,现在有数组[3, 2, 1, 5, 6, 4],k=2。
在经过第1次partition之后,数组变为[4, 6, 5, 3, 1, 2],pivot位置为第4个(从1开始计数)。我们要找的是第2大的数,小于pivot的位置,所以该数在pivot的左侧。继续对pivot左侧的部分进行partition。
在经过第2次partition之后,数组变为[5, 6, 4, 3, 1, 2],pivot位置为第3个(从1开始计数)。继续对pivot左侧的部分进行partition。
在经过第3次partition之后,数组变为[6, 5, 4, 3, 1, 2],pivot位置为第2个(从1开始计数)。这正是我们要查找的数。返回该数。
Java
public class Solution {
public int findKthLargest(int[] nums, int k) {
k--;
int lo = 0;
int hi = nums.length - 1;
int index = 0;
while(lo < hi){
index = partition(nums, lo, hi);
if(k < index) hi = index - 1;
else if(k > index) lo = index + 1;
else return nums[index];
}
return nums[lo];
}
// Return the index of pivot after sort
// Numbers on its left are greater, numbers on its right are smaller
private int partition(int[] a, int lo, int hi){
int i = lo;
int j = hi;
int pivot = a[lo];
while(i < j){
while(i < j && a[j] <= pivot) j--;
a[i] = a[j];
while(i < j && a[i] >= pivot) i++;
a[j] = a[i];
}
a[i] = pivot;
return i;
}
}
]]>
Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.
任意长度非负整数乘法。
基本思路就是模拟用竖式手算乘法的过程。
注意处理进位和乘0的情况。
Java
public class Solution {
public String multiply(String num1, String num2) {
// Mind the case of multiplication with 0
if(num1.equals("0") || num2.equals("0")) return "0";
StringBuilder sb = new StringBuilder();
int[][] temp = new int[num2.length()][num1.length() + num2.length()];
for(int i = num2.length() - 1; i >= 0; i--){
int d2 = num2.charAt(i) - '0';
int carry = 0;
for(int j = num1.length() - 1; j >= 0; j--){
int d1 = num1.charAt(j) - '0';
int product = d1 * d2 + carry;
temp[i][j + i + 1] = product % 10;
carry = product / 10;
}
temp[i][i] = carry;
}
int carry = 0;
for(int j = num1.length() + num2.length() - 1; j >= 0; j--){
int sum = 0;
for(int i = 0; i < num2.length(); i++){
sum += temp[i][j];
}
sum += carry;
sb.append(sum % 10);
carry = sum / 10;
}
if(sb.charAt(sb.length() - 1) == '0') sb.deleteCharAt(sb.length() - 1);
return sb.reverse().toString();
}
}
]]>