目录
二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
/*
* 思路 矩阵是有序的,从右上角来看,向左数字递减,向下数字递增,
* 因此从右上角开始查找,当要查找数字比左下角数字大时。下移
* 要查找数字比左上角数字小时,左移
*/
public class Solution {
public boolean Find(int[][] array, int target) {
int len = array.length - 1;
int i = 0;
while ((len >= 0) && (i < array[0].length)) {
if (array[len][i] > target) {
len--;
} else if (array[len][i] < target) {
i++;
} else {
return true;
}
}
return false;
}
}
-
替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
public class Solution {
// 从后往前,先计算需要多少空间,然后从后往前移动,则每个字符只为移动一次.
public String replaceSpace(StringBuffer str) {
if (str == null) {
return null;
}
int blankNum = 0;
int length = str.length();
int newLength = 0;
for (int i = 0; i < length; i++) {
if (str.charAt(i) == ' ') {
blankNum++;
}
}
newLength = length + 2 * blankNum; // 替换后的字符串长度
char[] newChars = new char[newLength];// 新的字符数组
int index = newLength - 1;
for (int i = length - 1; i >= 0; i--) {
if (str.charAt(i) == ' ') {
newChars[index--] = '0';
newChars[index--] = '2';
newChars[index--] = '%';
} else {
newChars[index--] = str.charAt(i);
}
}
return new String(newChars);
}
}
public class Solution {
//借助StringBuffer
public String replaceSpace(StringBuffer str) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == ' ') {
sb.append("%20");
} else {
sb.append(str.charAt(i));
}
}
return sb.toString();
}
}
-
从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
链表结点定义:
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
使用递归:
import java.util.ArrayList;
public class Solution {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
//使用递归实现
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
if (listNode != null) {
printListFromTailToHead(listNode.next);
arrayList.add(listNode.val);
}
return arrayList;
}
}
使用栈的后进先出
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<>();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}
ArrayList<Integer> list = new ArrayList<>();
while (!stack.isEmpty()) {
list.add(stack.pop());
}
return list;
}
}
-
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
TreeNode root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
return root;
}
// 前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
private TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) {
if (startPre > endPre || startIn > endIn)
return null;
TreeNode root = new TreeNode(pre[startPre]);
for (int i = startIn; i <= endIn; i++)
if (in[i] == pre[startPre]) {
root.left = reConstructBinaryTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1);
root.right = reConstructBinaryTree(pre, i - startIn + startPre + 1, endPre, in, i + 1, endIn);
}
return root;
}
}
用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。有两个栈,栈1和栈2.当入栈的时候,我们将它全放进栈1中,当需要出栈的时候,我们将栈1出栈到栈2中,然后再将栈2依次出栈。所以入栈的时候,思路很简单;当需要出栈的时候,我们用API提供的方法while(stack1.isEmpty())来将所有栈1的元素压入栈2中,然后将栈2弹出就可以.
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (stack1.empty() && stack2.empty()) {
throw new RuntimeException("Queue is empty!");
}
if (stack2.empty()) {
while (!stack1.empty()) {
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
用两个队列实现一个栈
移步
http://blog.youkuaiyun.com/mine_song/article/details/63322097
旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
使用二分查找:
public class Solution {
public int minNumberInRotateArray(int[] array) {
if (array == null || array.length == 0)
return 0;
int low = 0;
int high = array.length - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (array[mid] > array[high]) {
low = mid + 1;
// high = high - 1;可以避免low,high,mid相等的找不到最小值情况。
// int[] array={1,0,1,1,1};
} else if (array[mid] == array[high]) {
high = high - 1;
} else {
high = mid;
}
}
return array[low];
}
}
首先数组长度为零时,返回零,因为测试要求这样。然后有一个特殊情况是没有旋转,那么返回array[0],其次一般情况while一直循环,直到后面的数 < 前面的数停止,这个数就是我们要找的。
public class Solution {
public int minNumberInRotateArray(int[] array) {
if (array.length == 0)
return 0;
// 避免i+1越界,i要小于array.length - 1
for (int i = 0; i < array.length - 1; i++) {
if (array[i] > array[i + 1])
return array[i + 1];
}
// 所有元素相等时候或者未旋转,返回array[0]
return array[0];
}
}
斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39
public class Solution {
public int Fibonacci(int n) {
// 方法:用递归,系统会让一个超大的n来让StackOverflow,所以递归就不考虑了
// 使用迭代法,用fn1和fn2保存计算过程中的结果,并复用起来
int fn1 = 1;
int fn2 = 1;// 考虑出错情况
int res = 0;
if (n <= 0) {
return 0;
} // 第一和第二个数直接返回
if (n == 1 || n == 2) {
return 1;
}
for (int i = 3; i <= n; i++) {
res = fn1 + fn2;
fn2 = fn1;
fn1 = res;
}
return res;
}
}
跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution {
public int JumpFloor(int target) {
int fn1 = 1;
int fn2 = 2;
int res = 0;
if (target <= 0) {
return 0;
}
if (target == 1) {
return 1;
}
if (target == 2) {
return 2;
}
for (int i = 3; i <= target; i++) {
res = fn1 + fn2;
fn1 = fn2;
fn2 = res;
}
return res;
}
}
递归
public class Solution {
public int JumpFloor(int target) {
int result = 0;
if (target > 0) {
if (target <= 2)
return target;
else
return result = JumpFloor(target - 1) + JumpFloor(target - 2);
}
return result;
}
变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
:
非递归:
public class Solution {
public int JumpFloorII(int target) {
int jumpFlo = 1;
while (--target > 0) {
jumpFlo *= 2;
}
return jumpFlo;
}
}
2^(n-1)可以用位移操作进行
public class Solution {
public int JumpFloorII(int target) {
return 1<<--target;
}
}
使用递归:
public class Solution {
public int JumpFloorII(int target) {
if (target < 0) {
return 0;
} else if (target == 1) {
return 1;
} else {
return 2 * JumpFloorII(target - 1);
}
}
}
矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
同上述青蛙跳台阶
二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
public class Solution {
public int NumberOf1(int n) {
int num = 0;
while (n != 0) {
n = n & (n - 1);
num++;
}
return num;
}
}
数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方
public class Solution {
//时间复杂度O(n)
public double Power(double base, int exponent) {
int n = Math.abs(exponent);
if (n == 0)
return 1;
if (n == 1)
return base;
//以上两个if判断可省。for循环中判断
double result = 1.0
;
for (int i = 0; i < n; i++) {
result *= base;
}
if (exponent < 0) {
result = 1 / result;
}
return result;
}
}
使用递归,时间复杂度O(logn)
当n为偶数,a^n =(a^n/2)*(a^n/2)
当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a
举例
2^11 = 2^1 * 2^2 * 2^8
2^1011 = 2^0001 * 2^0010 * 2^1000
public class Solution {
// 时间复杂度O(lgn)
public double power(double base, int exponent) {
int n = Math.abs(exponent);
double result = 0.0;
if (n == 0)
return 1.0;
if (n == 1)
return base;
result = power(base, n >> 1);
result *= result;
// 如果指数n为奇数,则要再乘一次底数base
// 最后一位是1,与1相与得1,是奇数
if ((n & 1) == 1)
result *= base;
// 如果指数为负数,则应该求result的倒数
if (exponent < 0)
result = 1 / result;
return result;
}
}
调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
1.使用冒泡排序,前偶后奇就交换
public void reOrderArray(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
// 前偶后奇数就交换
if ((array[j] & 1) == 0 && (array[j + 1] & 1) == 1) {
array[j] = array[j] ^ array[j + 1];
array[j + 1] = array[j] ^ array[j + 1];
array[j] = array[j] ^ array[j + 1];
}
}
}
}
2.空间换时间,使用额外数组。
public void reOrderArray(int[] array) {
int[] newArr = new int[array.length];
//newArr的下标计数器
int j = 0;
for (int i = 0; i < array.length; i++)
if ((array[i] & 1) == 1) {
newArr[j] = array[i];
j++;
}
for (int i = 0; i < array.length; i++)
if ((array[i] & 1) == 0) {
newArr[j] = array[i];
j++;
}
for (int i = 0; i < array.length; i++)
array[i] = newArr[i];
}
相对位置发生变化的解法
public class Solution {
public void reOrderArray(int[] array) {
if (array == null)
return;
int begin = 0;
int end = array.length - 1;
while (begin <= end) {
while (begin <= end && ((array[begin] & 1) == 1))
begin++;
while (begin <= end && ((array[end] & 1) == 0))
end--;
if (begin <= end) {
array[begin] = array[begin] ^ array[end];
array[end] = array[begin] ^ array[end];
array[begin] = array[begin] ^ array[end];
}
}
}
}
链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
快慢指针,让快指针先走k步,然后慢指针开始走,若快指针走到末尾(为null),就是慢指针指向的就是倒数第k个结点
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public class Solution {
public ListNode FindKthToTail(ListNode head, int k) {
ListNode front = head;
int i = 0;
for (; front != null && i < k; i++) {
front = front.next;
}
// 如果k大于链表的长度或者k小于0,返回null;
if (i != k)
return null;
ListNode behind = head;
while (front != null) {
front = front.next;
behind = behind.next;
}
// 若k等于0,则behind为null
return behind;
}
}
反转链表
输入一个链表,反转链表后,输出链表的所有元素。
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public class Solution {
public ListNode ReverseList1(ListNode head) {
if (head == null)
return null;
// head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null;
ListNode pre = null;
ListNode next = null;
// 当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点
// 需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2
// 即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了
// 所以需要用到pre和next两个节点
// 1->2->3->4->5
// 1<-2<-3 4->5
while (head != null) {
// 做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre
// 如此就可以做到反转链表的效果
// 先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂
next = head.next;
// 保存完next,就可以让head从指向next变成指向pre了,代码如下
head.next = pre;
// head指向pre后,就继续依次反转下一个节点
// 让pre,head,next依次向后移动一个节点,继续下一次的指针反转
pre = head;
head = next;
}
// 如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点
// 直接输出pre就是我们想要得到的反转后的链表
return pre;
}
}
public class Solution {
public ListNode ReverseList(ListNode head) {
if (head == null)
return null;
ListNode newhead = null;
ListNode pre = null;
ListNode pNext = null;
ListNode pNode = head;
while (pNode != null) {
pNext = pNode.next;
//最后一个头结点赋值给newHead
if (pNext == null)
newhead = pNode;
pNode.next = pre;
pre = pNode;
pNode = pNext;
}
return newhead;
}
}
合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
比较两个链表的首结点,哪个小的的结点则合并到第三个链表尾结点,并向前移动一个结点。
步骤一结果会有一个链表先遍历结束,或者没有
第三个链表尾结点指向剩余未遍历结束的链表
返回第三个链表首结点
public ListNode Merge(ListNode list1, ListNode list2) {
if (list1 == null)
return list2;
if (list2 == null)
return list1;
//新建一个头节点,用来存合并的链表。
ListNode newList = new ListNode(-1);
ListNode temp = newList;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
temp.next = list1;
list1 = list1.next;
temp = temp.next;
} else {
temp.next = list2;
list2 = list2.next;
temp = temp.next;
}
}
//把未结束的链表连接到合并后的链表尾部
if (list1 != null) {
temp.next = list1;
}
if (list2 != null) {
temp.next = list2;
}
return newList.next;
}
递归:
public ListNode Merge(ListNode list1, ListNode list2) {
if (list1 == null)
return list2;
if (list2 == null)
return list1;
if (list1.val <= list2.val) {
list1.next = Merge(list1.next, list2);
return list1;
} else {
list2.next = Merge(list1, list2.next);
return list2;
}
}
树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路:参考剑指offer
1、首先设置标志位result = false,因为一旦匹配成功result就设为true,剩下的代码不会执行,如果匹配不成功,默认返回false
2、递归思想,如果根节点相同则递归调用DoesTree1HaveTree2(),如果根节点不相同,则判断tree1的左子树和tree2是否相同,再判断右子树和tree2是否相同
3、注意null的条件,HasSubTree中,如果两棵树都不为空才进行判断,DoesTree1HasTree2中ÿ