# 二维数组中的查找

       /*
* 思路 矩阵是有序的，从右上角来看，向左数字递减，向下数字递增，
* 因此从右上角开始查找，当要查找数字比左下角数字大时。下移
* 要查找数字比左上角数字小时，左移
*/
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;
}
}

# 替换空格

 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>();
//使用递归实现
if (listNode != null) {
}
return arrayList;
}
}

import java.util.ArrayList;
import java.util.Stack;

public class Solution {

Stack<Integer> stack = new Stack<>();
while (listNode != null) {
stack.push(listNode.val);
listNode = listNode.next;
}

ArrayList<Integer> list = new ArrayList<>();
while (!stack.isEmpty()) {
}
return list;
}
}

# 重建二叉树

/**
* 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;
}
}

# 用两个栈实现队列

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.csdn.net/mine_song/article/details/63322097

# 旋转数组的最小数字

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];
}
}

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];
}
}

# 斐波那契数列

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;
}

}

# 跳台阶

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;
}
}

N=1时，只有一种
N=2时，有两种：一次2级；两次1级

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;
}

# 变态跳台阶

:

	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);
}
}
}

# 二进制中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;
}
}

# 数值的整数次方

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;
}
}

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个结点

public class ListNode {
int val;
ListNode next = null;

ListNode(int val) {
this.val = val;
}
}

public class Solution {
public ListNode FindKthToTail(ListNode head, int k) {
int i = 0;
for (; front != null && i < k; i++) {
front = front.next;

}
// 如果k大于链表的长度或者k小于0，返回null；
if (i != k)
return null;
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 {
return null;
ListNode pre = null;
ListNode next = null;
// 即pre让节点可以反转所指方向，但反转之后如果不用next节点保存next1节点的话，此单链表就此断开了
// 所以需要用到pre和next两个节点
// 1->2->3->4->5
// 1<-2<-3 4->5
// 做循环，如果当前节点不为空的话，始终执行此循环，此循环的目的就是让当前节点从指向next到指向pre
// 如此就可以做到反转链表的效果
}
// 直接输出pre就是我们想要得到的反转后的链表
return pre;
}
}

public class Solution {
return null;
ListNode pre = null;
ListNode pNext = null;
while (pNode != null) {
pNext = pNode.next;
if (pNext == null)
pNode.next = pre;
pre = pNode;
pNode = pNext;
}

}
}

# 合并两个排序的链表

比较两个链表的首结点，哪个小的的结点则合并到第三个链表尾结点，并向前移动一个结点。
步骤一结果会有一个链表先遍历结束，或者没有
第三个链表尾结点指向剩余未遍历结束的链表
返回第三个链表首结点

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;
}
}

# 树的子结构

思路：参考剑指offer
1、首先设置标志位result = false，因为一旦匹配成功result就设为true，剩下的代码不会执行，如果匹配不成功，默认返回false

2、递归思想，如果根节点相同则递归调用DoesTree1HaveTree2（），如果根节点不相同，则判断tree1的左子树和tree2是否相同，再判断右子树和tree2是否相同

3、注意null的条件，HasSubTree中，如果两棵树都不为空才进行判断，DoesTree1HasTree2中，如果Tree2为空，则说明第二棵树遍历完了，即匹配成功，tree1为空有两种情况：

（1）如果tree1为空&&tree2不为空说明不匹配，

（2） 如果tree1为空，tree2为空，说明匹配。

public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;

public TreeNode(int val) {
this.val = val;
}
}

public class Solution {

public boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
// 当Tree1和Tree2都不为零的时候，才进行比较。否则直接返回false
if (root2 != null && root1 != null) {
// 如果找到了对应Tree2的根节点的点
if (root1.val == root2.val) {
// 以这个根节点为为起点判断是否包含Tree2
result = doesTree1HaveTree2(root1, root2);
}
// 如果找不到，那么就再去root的左儿子当作起点，去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.left, root2);
}
// 如果还找不到，那么就再去root的右儿子当作起点，去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.right, root2);
}
}
// 返回结果
return result;
}

public boolean doesTree1HaveTree2(TreeNode root1, TreeNode root2) {
// 如果Tree2已经遍历完了都能对应的上，返回true
if (root2 == null) {
return true;
}
// 如果Tree2还没有遍历完，Tree1却遍历完了。返回false
if (root1 == null) {
return false;
}
// 如果其中有一个点没有对应上，返回false
if (root1.val != root2.val) {
return false;
}
// 如果根节点对应的上，那么就分别去子节点里面匹配
return doesTree1HaveTree2(root1.left, root2.left) &&
doesTree1HaveTree2(root1.right, root2.right);
}
}

# 顺时针打印矩阵

public ArrayList<Integer> printMatrix(int[][] array) {
ArrayList<Integer> result = new ArrayList<Integer>();
if (array.length == 0)
return result;
int n = array.length, m = array[0].length;
if (m == 0)
return result;
// 此种方法关键点--求圈数
int layers = (Math.min(n, m) - 1) / 2 + 1;
// 要打印的圈数
for (int i = 0; i < layers; i++) {
// 打印每圈
for (int k = i; k < m - i; k++)
for (int j = i + 1; j < n - i; j++)
result.add(array[j][m - i - 1]);// 右上至右下
// 注意k,j开始的下标
for (int k = m - i - 2; (k >= i) && (n - i - 1 != i); k--)
result.add(array[n - i - 1][k]);// 右至左
for (int j = n - i - 2; (j > i) && (m - i - 1 != i); j--)
}
return result;
}

public ArrayList<Integer> printMatrix(int[][] matrix) {
// 得到矩阵的长度和宽度
int rows = matrix.length;
int columns = matrix[0].length;
ArrayList<Integer> list = new ArrayList<Integer>();
if (rows == 0 && columns == 0) {
return list;
}
// start标志着每一次遍历一圈的起始下标
int start = 0;
while (rows > start * 2 && columns > start * 2) {
printNumber(list, matrix, rows, columns, start);
start++;
}
return list;
}

public ArrayList<Integer> printNumber(ArrayList<Integer> list, int[][] matrix, int rows, int columns,
int start) {
// 先打印行，从左到右
for (int i = start; i <= columns - start - 1; i++) {
}
// 打印列，从上到下
for (int j = start + 1; j <= rows - start - 1; j++) {
}
// 打印行，从左到右打印
for (int m = columns - start - 2; m >= start && rows - start - 1 > start; m--) {
}
// 打印列，从下到上，columns-start-1>start 避免当只有一列时重新打印
for (int k = rows - start - 2; k >= start + 1 && columns - start - 1 > start; k--) {
}
return list;
}
}

# 二叉树的镜像

二叉树的镜像定义：源二叉树
8
/  \
6   10
/ \  / \
5  7 9 11
镜像二叉树
8
/  \
10   6
/ \  / \
11 9 7  5
public class Solution {
public void Mirror(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
//左右子树不空交换
if (node.left != null || node.right != null) {
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}

if (node.left != null)
stack.push(node.left);
if (node.right != null)
stack.push(node.right);
}
}
}

public class Solution {
// 二叉树的镜像如果二叉树为空，直接返回。
// 如果二叉树左右子数都为空，也直接返回。
// 把根节点的左右子树交换。
// 如果根节点左子树不为空，递归调用镜像函数
// 如果根节点右子树不为空，递归调用镜像函数
public void Mirror(TreeNode root) {
if (root == null)
return;
if (root.left == null && root.right == null)
return;
TreeNode pTemp = root.left;
root.left = root.right;
root.right = pTemp;
if (root.left != null)
Mirror(root.left);
if (root.right != null)
Mirror(root.right);
}
}

# 包含min函数的栈

         /*
* 思路：用一个栈stack保存数据，用另外一个栈min保存依次入栈最小的数
* 比如，stack中依次入栈，5, 4, 3, 8, 10,11,12,1
* 则min依次入栈，5, 4, 3, 3, 3, 3, 3, 1
* 每次入栈的时候，如果入栈的元素比min中的栈顶元素小或等于则入栈，否则入stack的栈顶元素。
* 保持stack中和min中保持相同个数的元素
*/
Stack<Integer> stack = new Stack<>();
Stack<Integer> minStack = new Stack<>();

public void push(int node) {
stack.push(node);
// 如果min为空或者node比min栈中的元素小，则入min栈
if (minStack.size() == 0 || minStack.peek() > node) {
minStack.push(node);
}
// 否则把min栈中的顶部元素入栈
else minStack.push(minStack.peek());
}

public void pop() {
if (!stack.isEmpty()) {
stack.pop();
minStack.pop();
}
}

public int top() {
return stack.peek();
}

public int min() {
return minStack.peek();
}

# 栈的压入、弹出序列

举例：
入栈1,2,3,4,5
出栈4,5,3,2,1
首先1入辅助栈，此时栈顶1≠4，继续入栈2
此时栈顶2≠4，继续入栈3
此时栈顶3≠4，继续入栈4
此时栈顶4＝4，出栈4，弹出序列向后一位，此时为5，,辅助栈里面是1,2,3
此时栈顶3≠5，继续入栈5
此时栈顶5=5，出栈5,弹出序列向后一位，此时为3，,辅助栈里面是1,2,3
….
依次执行，最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。

import java.util.Stack;
public class Solution {
public boolean IsPopOrder(int[] pushA, int[] popA) {
if (pushA == null || popA == null || pushA.length == 0 || popA.length == 0)
return false;
// 用于标识弹出序列的位置
int index = 0;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < pushA.length; i++) {
stack.push(pushA[i]);
// 如果栈不为空，且栈顶元素等于弹出序列
while (!stack.isEmpty()) {
if (stack.peek() != popA[index])
break;
stack.pop();
index++;
}
}
return stack.isEmpty();
}
}

# 二叉搜索树的后序遍历序列

1、确定root；
2、遍历序列（除去root结点），找到第一个大于root的位置，则该位置左边为左子树，右边为右子树；
3、遍历右子树，若发现有小于root的值，则直接返回false；
4、分别判断左子树和右子树是否仍是二叉搜索树（即递归步骤1、2、3）。

public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0)
return false;
// 调用函数，java没有指针，要用下标模拟指针，新建函数判断
return IsTreeBST(sequence, 0, sequence.length - 1);
}

private boolean IsTreeBST(int[] sequence, int start, int end) {
// index是指示找到第一个大于左子树的结点
int index = start;
for (; index < end; index++)
if (sequence[index] > sequence[end])
break;
// 若右子树有小于跟结点的值，返回false
for (int i = index; i < end; i++)
if (sequence[i] < sequence[end])
return false;
return IsTreeBST(sequence, start, index - 1) && IsTreeBST(sequence, index, end - 1);
}

// 非递归
// 非递归也是一个基于递归的思想：
// 左子树一定比右子树小，因此去掉根后，数字分为left，right两部分，right部分的
// 最后一个数字是右子树的根他也比左子树所有值大，因此我们可以每次只看有子树是否符合条件即可，
// 即使到达了左子树左子树也可以看出由左右子树组成的树还想右子树那样处理
// 对于左子树回到了原问题，对于右子树，左子树的所有值都比右子树的根小可以暂时把他看出右子树的左子树
// 只需看看右子树的右子树是否符合要求即可
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0)
return false;
int len = sequence.length;
while (--len > 0) {
int i = 0;
while (sequence[i] < sequence[len])
i++;
while (sequence[i] > sequence[len])
i++;
if (i != len)
return false;

}
return true;
}


# 二叉树中和为某一值的路径

import java.util.ArrayList;
public class Solution {
private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> list = new ArrayList<Integer>();

public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
if (root == null)
return listAll;
target -= root.val;
if (target == 0 && root.left == null && root.right == null)
/*
* if (root.left != null) { FindPath(root.left, target);
* list.remove(list.size() - 1); } if (root.right != null) {
* FindPath(root.right, target); list.remove(list.size() - 1); }
*/
// 继续遍历左右结点
FindPath(root.left, target);
FindPath(root.right, target);
// 在返回父节点之前，在路径上删除该结点
list.remove(list.size() - 1);
return listAll;
}
}

# 复杂链表的复制

	         /**解题思路：
* *1、遍历链表，复制每个结点，如复制结点A得到A1，将结点A1插到结点A后面；
* *2、重新遍历链表，复制老结点的随机指针给新结点，
* 如A1.random = A.random.next;
* 3、拆分链表，将链表拆分为原链表和复制后的链表
* */
return null;
// 复制next 如原来是A->B->C 变成A->A'->B->B'->C->C'
while (pCur != null) {
RandomListNode node = new RandomListNode(pCur.label);
node.next = pCur.next;
pCur.next = node;
pCur = node.next;
}
// 复制random pCur是原来链表的结点 pCur.next是复制pCur的结点
while (pCur != null) {
if (pCur.random != null)
pCur.next.random = pCur.random.next;
pCur = pCur.next.next;
}
RandomListNode pCloneNode = null;
// 初始化，让pcur指向pCloneNode的下一个结点,避免空指针
if (pCur != null) {
pCur.next = pCloneNode.next;
pCur = pCur.next;
// pCur = pCloneNode.next;
}
while (pCur != null) {
pCloneNode.next = pCur.next;
pCloneNode = pCloneNode.next;
pCur.next = pCloneNode.next;
pCur = pCur.next;
}
}

# 连续子数组的最大和

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢？例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住？(子向量的长度至少是1)

/*
算法时间复杂度O（n）
用total记录累计值，maxSum记录和最大
基于思想：对于一个数A，若是A的左边累计数非负，那么加上A能使得值不小于A，认为累计值对
整体和是有贡献的。如果前几项累计值负数，则认为有害于总和，total记录当前值。
此时 若和大于maxSum 则用maxSum记录下来
*/
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if (array.length == 0)
return 0;
else {
int total = array[0], maxSum = array[0];
for (int i = 1; i < array.length; i++) {
if (total >= 0)
total += array[i];
else
total = array[i];
if (total > maxSum)
maxSum = total;
}
return maxSum;
}
}
}

public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
if (array.length == 0)
return 0;
int res = Integer.MIN_VALUE; // 记录当前所有子数组的和的最大值
int tempMax = 0; // 包含array[i]的连续数组最大值
for (int i = 0; i < array.length; i++) {
tempMax = Math.max(tempMax + array[i], array[i]);
res = Math.max(tempMax, res);
}
return res;
}
}

# 字符串的排列

/**
* 对于无重复值的情况** 固定第一个字符，递归取得首位后面的各种字符串组合；*
* 再把第一个字符与后面每一个字符交换，并同样递归获得首位后面的字符串组合；
* *递归的出口，就是只剩一个字符的时候，递归的循环过程，就是从每个子串的第二个字符开始依次与第一个字符交换，然后继续处理子串。* *
* 假如有重复值呢？*
* *由于全排列就是从第一个数字起，每个数分别与它后面的数字交换，我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。 *
* 例如abb，第一个数与后面两个数交换得bab，bba。然后abb中第二个数和第三个数相同，就不用交换了。* 但是对bab，第二个数和第三个数不
* 同，则需要交换，得到bba。* 由于这里的bba和开始第一个数与第三个数交换的结果相同了，因此这个方法不行。**
* 换种思维，对abb，第一个数a与第二个数b交换得到bab，然后考虑第一个数与第三个数交换，此时由于第三个数等于第二个数，*
* 所以第一个数就不再用与第三个数交换了。再考虑bab，它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕！** * @param
*/
public ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<String>();
if (str != null && str.length() > 0) {
PermutationHelper(str.toCharArray(), 0, list);
// 按照字典序输出
Collections.sort(list);
}
return list;
}
private void PermutationHelper(char[] chars, int i, ArrayList<String> list) {
if (i == chars.length) {
return;
}
// 用于结果去重，可以用list
Set<Character> charSet = new HashSet<Character>();
for (int j = i; j < chars.length; ++j) {
if (!charSet.contains(chars[j])) {
swap(chars, i, j);
PermutationHelper(chars, i + 1, list);
swap(chars, i, j);
}
}
}
private void swap(char[] cs, int i, int j) {
char temp = cs[i];
cs[i] = cs[j];
cs[j] = temp;
}

# 二叉树与双向链表

// 1.核心是中序遍历的非递归算法。
//2.修改当前遍历节点与前一遍历节点的指针指向。
public TreeNode ConvertBSTToBiList(TreeNode root)

{
if (root == null)
return null;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode p = root;
// 用于保存中序遍历序列的上一节点
TreeNode pre = null;
boolean isFirst = true;
while (p != null || !stack.isEmpty()) {
while (p != null) {
stack.push(p);
p = p.left;
}
p = stack.pop();
if (isFirst) {
// 将中序遍历序列中的第一个节点记为root
root = p;
pre = root;
isFirst = false;
} else {
pre.right = p;
p.left = pre;
pre = p;
}
p = p.right;
}
return root;
}

// 直接用中序遍历

public TreeNode Convert(TreeNode pRootOfTree) {
ConvertSub(pRootOfTree);
}

private void ConvertSub(TreeNode pRootOfTree) {
if (pRootOfTree == null)
return;
ConvertSub(pRootOfTree.left);
} else {
}
ConvertSub(pRootOfTree.right);
}

# 字符串的组合

import java.util.*;
public class Solution {
static List<String> retList = new ArrayList<>();

public static void combiantion(char chs[]) {
if (chs.length == 0)
return;
Stack<Character> stack = new Stack<Character>();
for (int i = 1; i <= chs.length; i++) {
combine(chs, 0, i, stack);
}
}

private static void combine(char[] chs, int begin, int number, Stack<Character> stack) {
if (number == 0 && !retList.contains(stack.toString())) {
return;
}
if (begin == chs.length) {
return;
}
stack.push(chs[begin]);
combine(chs, begin + 1, number - 1, stack);
stack.pop();
combine(chs, begin + 1, number, stack);
}
}

# 数组中出现次数超过一半的数字

      /*
* 采用阵地攻守的思想：第一个数字作为第一个士兵，守阵地；times = 1；
* 遇到相同元素，times++;遇到不相同元素，即为敌人，同归于尽,times--；
* 当遇到times为0的情况，又以新的i值作为守阵地的士兵，继续下去， 到最后还留在阵地上的士兵，有可能是主元素。
* 再加一次循环，记录这个士兵的个数看是否大于数组一般即可。
*/
public  int MoreThanHalfNum_Solution(int[] array) {
if (array.length <= 0)
return 0;
int res = array[0];
int times = 1;
for (int i = 1; i < array.length; i++) {
if (times == 0) {
res = array[i];
times = 1;
} else if (array[i] == res)
times++;
else
times--;
}

times = 0;
for (int i = 0; i < array.length; i++)
if (res == array[i])
times++;
if (times * 2 > array.length)
return res;
else
return 0;
}
        // 数组排序后，如果符合条件的数存在，则一定是数组中间那个数
// 要注意此数的出现的次数大于数组长度一半才可以
// 涉及到快排sort，其时间复杂度为O(NlogN)
public  int MoreThanHalfNum_Solution(int[] array) {
Arrays.sort(array);
int count = 0;
int len = array.length;
// 统计array[len / 2]出现的次数
for (int i = 0; i < len; i++)
if (array[i] == array[len / 2])
count++;
return count > len / 2 ? array[len / 2] : 0;
}
       //基于快速排序的思想
public int MoreThanHalfNum_Solution(int[] array) {
if (array.length <= 0)
return 0;
int mid = array.length >> 1;
int start = 0;
int end = array.length - 1;
int index = Partition(array, start, end);
while (index != mid) {
if (index > mid)
index = Partition(array, start, index - 1);
else
index = Partition(array, index + 1, end);
}
int res = array[mid];
int times = 0;
for (int i = 0; i < array.length; i++)
if (res == array[i])
times++;
if (times * 2 > array.length)
return res;
else
return 0;
}

private int Partition(int[] arr, int start, int end) {
// arr[start]为挖的第一个坑
int key = arr[start];
while (start < end) {
while (arr[end] >= key && end > start)
end--;
arr[start] = arr[end];
while (arr[start] <= key && end > start)
start++;
arr[end] = arr[start];
}
arr[start] = key;
return start;
}


# 把数组排成最小的数

* 解释说明：* 比如 "3" < "31"但是 "331" > "313"，所以要将二者拼接起来进行比较

import java.util.Arrays;
import java.util.Comparator;
public class Solution {
// 使用String.valueOf(int)来变换整型到字符串
// 使用StringBuilder来拼接字符串
public String PrintMinNumber(int[] numbers) {
if (numbers == null || numbers.length == 0)
return "";
int len = numbers.length;
String[] str = new String[len];
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
str[i] = String.valueOf(numbers[i]);
}
// comparator 外部比较器
Arrays.sort(str, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
String c1 = s1 + s2;
String c2 = s2 + s1;
return c1.compareTo(c2);
}
});
for (int i = 0; i < len; i++) {
sb.append(str[i]);
}
return sb.toString();
}
}

	// jdk1.7 实现value为String封装数组
private final char value[];

public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
// 获取到两个字符串的较短的长度
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;

int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
// 如果两个字符的ASC不相同，则直接返回
if (c1 != c2) {
return c1 - c2;
}
k++;
}
// 如果都一样，返回两个字符串的长度查
return len1 - len2;
}

# 第一个只出现一次的字符

import java.util.LinkedHashMap;
public class Solution {
// 使用map存储，key为字符，value为出现的次数
//扫描map，取第一个value为1的key,返回下标
public int FirstNotRepeatingChar(String str) {
for (int i = 0; i < str.length(); i++)
if (!map.containsKey(str.charAt(i)))
map.put(str.charAt(i), 1);
else
map.put(str.charAt(i), map.get(str.charAt(i)) + 1);

for (int index = 0; index < str.length(); index++)
if (map.get(str.charAt(index)) == 1)
return index;

return -1;
}

# 数组中的逆序对

即输出P%1000000007

1,2,3,4,5,6,7,0

7

public class Solution {
public int InversePairs(int[] array) {
if (array == null || array.length == 0) {
return 0;
}
int[] copy = new int[array.length];
// for复制？？？可省
for (int i = 0; i < array.length; i++) {
copy[i] = array[i];
}
// 数值过大求余
int count = InversePairsCore(array, copy, 0, array.length - 1);
return count;
}

private int InversePairsCore(int[] array, int[] copy, int low, int high) {
if (low == high) {
return 0;
}
// mid属于前半部分最后一个数字
int mid = (low + high) >> 1;
int leftCount = InversePairsCore(array, copy, low, mid) % 1000000007;
int rightCount = InversePairsCore(array, copy, mid + 1, high) % 1000000007;
int count = 0;
// i初始前半部分最后一个数字
int i = mid;
// j初始后半部分最后一个数字
int j = high;
// indexCopy记录copy数组的下标
int locCopy = high;
while (i >= low && j > mid) {
if (array[i] > array[j]) {
// j-mid~j的数都小于等于j的(排序)，j的数字小于i
count += j - mid;
copy[locCopy--] = array[i--];
if (count >= 1000000007)// 数值过大求余
count %= 1000000007;
} else {
copy[locCopy--] = array[j--];
}
}
for (; i >= low; i--)

{
copy[locCopy--] = array[i];
}
// 剩余部分依次放入临时数组
for (; j > mid; j--) {
copy[locCopy--] = array[j];
}
for (int s = low; s <= high; s++) {
array[s] = copy[s];
}
return (leftCount + rightCount + count) % 1000000007;
}
}


# 两个链表的第一个公共结点

找出2个链表的长度，然后让长的先走两个链表的长度差，然后再一起走（因为2个链表用公共的尾部）
public class Solution {
// 先在长链表上走上几步，在同时在两个链表上遍历
if (len1 - len2 > 0)
current1 = walkStep(current1, len1 - len2);
else
current2 = walkStep(current2, len2 - len1);
while (current1 != null && current2 != null) {
if (current1 == current2)
return current1;
current1 = current1.next;
current2 = current2.next;
}
return null;
}

private ListNode walkStep(ListNode cur, int step) {
// 从step~1
while (step-- > 0)
cur = cur.next;
return cur;
}

// 计算链表长度
return 0;
int length = 0;
while (cur != null) {
length++;
cur = cur.next;
}
return length;
}
}

# 数字在排序数组中出现的次数

public class Solution {
public int GetNumberOfK(int[] array, int k) {
int first, last;
first = getFirstKIndex(array, 0, array.length - 1, k);
last = getLastIndex(array, 0, array.length - 1, k);
if (first > -1 && last > -1)
return last - first + 1;
return 0;
}
private int getLastIndex(int[] array, int start, int end, int k) {
int mid;
// 一定要等有=！！！！！！
while (start <= end) {
mid = (start + end) / 2;
if (k == array[mid]) {
// k在中间或者结尾，找到
if (mid == end || array[mid + 1] != k) {
return mid;
} else {
start = mid + 1;
}
} else if (k < array[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return -1;
}

private int getFirstKIndex(int[] array, int start, int end, int k) {
int mid;
// 一定要等有=！！！！！！
while (start <= end) {
mid = (start + end) / 2;
if (k == array[mid]) {
if (mid == start || array[mid - 1] != k) {
return mid;
} else {
end = mid - 1;
}
} else if (k < array[mid]) {
end = mid - 1;
} else {
start = mid + 1;
}
}
return -1;
}
}


# 二叉树的深度及判断是否为平衡二叉树

public class Solution {
// 注意使用全局变量
boolean isBalance = true;
public boolean IsBalanced_Solution(TreeNode root) {
lengthOfTree(root);
return isBalance;
}

private int lengthOfTree(TreeNode root) {
if (root == null)
return 0;
int left = lengthOfTree(root.left);
int right = lengthOfTree(root.right);
if (Math.abs(left - right) > 1)
isBalance = false;
return Math.max(left, right) + 1;

}

// 每个结点被遍历多次的解法
public boolean IsBalancedTree(TreeNode root) {
// 空树为true
if (root == null)
return true;
int leftDepth = TreeDepth(root.left);
int rightDepth = TreeDepth(root.right);
if (Math.abs(leftDepth - rightDepth) > 1)
return false;
return IsBalancedTree(root.left)
&& IsBalancedTree(root.right);
}

// 计算树的深度，注意加1
public int TreeDepth(TreeNode root) {
if (root == null)
return 0;
// 注意最后加1，因为左右子树的深度大的+根节点的深度1
return Math.max(TreeDepth(root.left),
TreeDepth(root.right)) + 1;
}
}

# 数组中只出现一次的数字

## 使用map

// num1,num2分别为长度为1的数组。传出参数
// 将num1[0],num2[0]设置为返回结果
// 因为题目要求最多出现两次，可以用list，如果存在就删除，最后list剩下的就是两个数字。
// 但是要判断list的长度必须大于1，才符合要求、
public void FindNumsAppearOnce1(int[] array, int num1[], int num2[]) {
java.util.HashMap<Integer, Integer> map = new java.util.HashMap<>();
for (int num : array) {
if (!map.containsKey(num))
map.put(num, 1);
else
map.put(num, map.get(num) + 1);
}
int i = 0;
for (int num : array) {
if (map.get(num) == 1) {
if (i == 1) {
num2[0] = num;
break;
} else {
num1[0] = num;
i++;
}
}

}
}

依照这个思路，我们来看两个数（我们假设是AB）出现一次的数组。我们首先还是先异或，剩下的数字肯定是A、B异或的结果，这个结果的二进制中的1，表现的是A和B的不同的位。我们就取第一个1所在的位数，假设是第3位，接着把原数组分成两组，分组标准是第3位是否为1。如此，相同的数肯定在一个组，因为相同数字所有位都相同，而不同的数，肯定不在一组。然后把这两个组按照最开始的思路，依次异或，剩余的两个结果就是这两个只出现一次的数字。

## 使用异或

public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
if (array == null || array.length < 2)
return;
int bitResult = 0;
for (int i = 0; i < array.length; i++)
bitResult ^= array[i];
int index = findFirstBitIs1(bitResult);
for (int i = 0; i < array.length; i++)
if (isBit1(array[i], index))
num1[0] ^= array[i];
else
num2[0] ^= array[i];
}

// 判断target的右侧index位是否为1
private boolean isBit1(int target, int index) {
return ((target >> index) & 1) == 1;
}

// 查找num右侧第一个1的下标
private int findFirstBitIs1(int num) {
int indexBit = 0;
// 注意判断位数合法性
while ((num & 1) == 0 && indexBit < 32) {
indexBit++;
num >>= 1;
}
return indexBit;
}

# 和为S的连续正数序列

import java.util.ArrayList;
/*
*初始化small=1，big=2;
*small到big序列和小于sum，big++;大于sum，small++;
*当small增加到(1+sum)/2是停止
*/
public class Solution {
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
// 不重复，最少有2个数字
if (sum < 3)
return ret;
// 初始化small=1，big=2;
int small = 1;
int big = 2;
while (small != (sum + 1) / 2) {
int curSum = sumOfList(small, big);
if (curSum == sum) {
// 注意此时list不能使用全局，并clear，因为是引用！只有最后一个结果！！！
ArrayList<Integer> list = new ArrayList<>();
for (int i = small; i <= big; i++)
big++;
} else if (curSum < sum)
big++;
else
small++;
}
return ret;
}
// 计算list内数字之和
private int sumOfList(int small, int big) {
int sum = 0;
for (int i = small; i <= big; i++)
sum += i;
return sum;
}
}

# 和为S的两个数字

import java.util.ArrayList;
public class Solution {
// 数列满足递增，设两个头尾两个指针i和j，
// 若ai + aj == sum，就是答案（和一定，两个数字相差越远乘积越小）
// 若ai + aj > sum，aj肯定不是答案之一（前面已得出 i 前面的数已是不可能），j -= 1
// 若ai + aj < sum，ai肯定不是答案之一（前面已得出 j 后面的数已是不可能），i += 1
// O(n)
// 已经排好序，运用数学上的夹逼
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
ArrayList<Integer> list = new ArrayList<>();
if (array == null || array.length < 2)
return list;
int i = 0;
int j = array.length - 1;
while (i < j) {
int small = array[i];
int big = array[j];
if (small + big == sum) {
break;
} else if (small + big > sum)
j--;
else
i++;
}
return list;
}
}

# 左旋转字符串

public class Solution {
public String LeftRotateString(String str, int n) {
if (str == null || str.trim().length() == 0)
return str;
int len = str.length();
n = n % len;
char[] charStrs = str.toCharArray();
// 翻转字符串前n个字符
reverse(charStrs, 0, n - 1);
// 翻转字符串后面部分
reverse(charStrs, n, len - 1);
// 翻转整个字符串
reverse(charStrs, 0, len - 1);
return String.valueOf(charStrs);
}

private void reverse(char[] charStrs, int i, int j) {
while (i < j) {
char temp = charStrs[i];
charStrs[i] = charStrs[j];
charStrs[j] = temp;
i++;
j--;
}
}
}

# 翻转单词顺序列

public class Solution {
public String ReverseSentence(String str) {
// 注意trim
if (str == null || str.trim().length() == 0)
return str;
String[] strs = str.split(" ");
// 借用StringBuffer，从后往前append
StringBuffer sb = new StringBuffer();
for (int i = strs.length - 1; i >= 0; i--) {
sb.append(strs[i]);
if (i > 0)
sb.append(" ");
}
return sb.toString();
}
}

# 扑克牌顺子

LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿！！“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子.....LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何。为了方便起见,你可以认为大小王是0。

public class Solution {
// 考略到顺子的特性，最大值和最小值之差绝对为4，
// 然而又有大小王的存在，所以a[4]-a[index] <=4
// 此题关键是去重和0的个数，还有最大最小的差值
public boolean isContinuous(int[] numbers) {
if (numbers == null || numbers.length != 5)
return false;
Arrays.sort(numbers);
// 大小王看成0，最多4个
// index为0的个数，也是第一个不为0的数字下标
int index = 0;
while (numbers[index] == 0)
index++;
for (int i = index; i < numbers.length - 1; i++)
// 判断有没有重复扑克
if (numbers[i] == numbers[i + 1])
return false;
return numbers[4] - numbers[index] <= 4
&& index < 5;
}
}

# 孩子们的游戏(圆圈中最后剩下的数)

## 递推公式

k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。

k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1

f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)

import java.util.LinkedList;
public class Solution {
// 第一种方法使用递推公式
public int LastRemaining_Solution(int n, int m) {
if (m < 1 || n < 1)
return -1;
int last = 0;
// i代表有目前有个人
for (int i = 2; i <= n; i++)
last = (last + m) % i;
return last;
}
// 第二种方法使用循环链表过程
public int LastRemaining(int n, int m) {
for (int i = 0; i < n; i++)
int bt = 0;
while (list.size() > 1) {
// 删除第m个人，即报数为m-1的人（从0开始）
bt = (bt + m - 1) % list.size();
list.remove(bt);
}
return list.size() == 1 ? list.get(0) : -1;
}
}

# 求1~n的和（不使用乘除）

public class Solution {
// &&两侧的表达式结果必须为boolean型，
// 所有&&右侧要用一个无关变量a判断是否与result相等，
// 让右侧的表达式返回boolean型。不管返回的是true还是false，
// 我们的目的仅仅是让&&右侧的表达式执行。
// &&连接的表达式，必须要将最终的boolean结果赋给变量，否则编译报错！
public int Sum_Solution(int n) {
int result = 0;
int temp = 0;
// 借用&&的短路功能
boolean flag = (n > 0) && temp == (result += Sum_Solution(n - 1));
// return ((int) Math.pow(n, 2) + n) >> 1;
return result;
}
}

# 不用加减乘除做加法

首先看十进制是如何做的： 5+7=12，三步走
第一步：相加各位的值，不算进位，得到2。
第二步：计算进位值，得到10. 如果这一步的进位值为0，那么第一步得到的值就是最终结果。
第三步：重复上述两步，只是相加的值变成上述两步的得到的结果2和10，得到12。
同样我们可以用三步走的方式计算二进制值相加： 5-101，7-111 第一步：相加各位的值，不算进位，得到010，二进制每位相加就相当于各位做异或操作，101^111。
第二步：计算进位值，得到1010，相当于各位做与操作得到101，再向左移一位得到1010，(101&111)<<1。
第三步重复上述两步， 各位相加 010^1010=1000，进位值为100=(010&1010)<<1。
继续重复上述两步：1000^100 = 1100，进位值为0，跳出循环，1100为最终结果。

public class Solution {
// 使用递推实现
public int AddF(int num1, int num2) {
int sum = 0;
int carry = 0;
do {
sum = num1 ^ num2;
carry = (num1 & num2) << 1;
num1 = sum;
num2 = carry;
// 注意使用do while
} while (num2 != 0);
return sum;
}

// 使用递归实现
public int Add(int num1, int num2) {
if (num2 == 0)
return num1;
return Add(num1 ^ num2, (num1 & num2) << 1);
}
}

# 字符串转数字

public class Solution {
// 方法1简单高效
public int StrToIntWithExtion(String str) {
int res = 0;
try {
res = Integer.valueOf(str);
} catch (NumberFormatException e) {
return 0;
}
return res;
}

// 方法2
public int StrToInt(String str) {
if (str == null || str.length() == 0)
return 0;
char[] charArr = str.trim().toCharArray();
for (int i = 0; i < str.length(); i++) {
if ((charArr[0] == '+' || charArr[0] == '-')
// 避免数组长度为1，只有'+''-'情况
&& charArr.length > 1)
continue;
if (!Character.isDigit(charArr[i]))
return 0;
}
return Integer.parseInt(str);
}
}


# 数组中重复的数字

import java.util.ArrayList;
import java.util.Arrays;
public class cho重复的数字 {
public boolean duplicate2(int numbers[],
int length, int[] duplication) {
// 合法性校验
if (numbers == null || numbers.length == 0
|| length <= 0) {
duplication[0] = -1;
return false;
}
for (int i = 0; i < length; i++)
if (numbers[i] < 0 || numbers[i] > length - 1) {
duplication[0] = -1;
return false;
}
for (int i = 0; i < length; i++) {
while (numbers[i] != i) {
if (numbers[i] == numbers[numbers[i]]) {
duplication[0] = numbers[i];
return true;
}
// 交换两个数字
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
//使用额外空间
public boolean duplicateWithList(int numbers[],
int length, int[] duplication) {
if (numbers == null || numbers.length == 0) {
duplication[0] = -1;
return false;
}
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < length; i++) {
if (list.contains(numbers[i])) {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
//使用排序
public boolean duplicateWithSort(int numbers[],
int length, int[] duplication) {
if (numbers == null || numbers.length == 0) {
duplication[0] = -1;
return false;
}
Arrays.sort(numbers);
for (int i = 0; i < length - 1; i++)
if (numbers[i] == numbers[i + 1]) {
duplication[0] = numbers[i];
return false;
}
return true;
}
}

# 构建乘积数组

public class Solution {
// 简单粗暴的一种方式是O（N^2）
public int[] multiplyWithFor(int[] A) {
int len = A.length;
// 定义一个结果对象
int[] result = new int[len];
// 定义一个基本的量
int rst = 1;
for (int i = 0; i < len; i++) {
// 如果相同，就路过继续
for (int j = 0; j < len; j++) {
if (i == j) {
continue;
}
// 如果不同，就相乘
rst *= A[j];
}
result[i] = rst;
rst = 1; // 还原基本的量
}
return result;
}
// 新建一个新数组B， 对A数组i项左侧自上往下累乘，
// 对A数组i项右侧自下往上累乘 时间复杂度O(n)
public int[] multiply(int[] A) {
// 将B拆分为A[0] *...* A[i-1]和A[n-1]*...*A[i+1] 两部分
if (A == null || A.length == 0)
return A;
int len = A.length;
int[] B = new int[len];
B[0] = 1;
// 先计算左下三角形，此时B[0]只有一个元素，舍为1，
// B[0]不包括A[0]
for (int i = 1; i < len; i++)
B[i] = B[i - 1] * A[i - 1];
// 只需要保留上一个计算结果，不需要数组保存
int tmp = 1;

/*
* for (int i = len - 2; i >= 0; i--) {
*  tmp *= A[i+ 1];
*  B[i] *= tmp; }
*/
// 计算右上三角形
for (int i = len - 1; i >= 0; i--) {
// B[i]最终结果是左侧和右侧的乘积
B[i] *= tmp;
tmp *= A[i];
}
System.out.println(Arrays.toString(B));
return B;
}
}

# 正则表达式匹配

/*

1、如果字符串第一个字符和模式中的第一个字符相匹配，
那么字符串和模式都后移一个字符，然后匹配剩余的。
2、如果字符串第一个字符和模式中的第一个字符相不匹配，直接返回false。
而当模式中的第二个字符是“*”时：
如果字符串第一个字符跟模式第一个字符不匹配，则模式后移2个字符，继续匹配。
如果字符串第一个字符跟模式第一个字符匹配，可以有3种匹配方式：
1、模式后移2字符，相当于x*被忽略；
2、字符串后移1字符，模式后移2字符；
3、字符串后移1字符，模式不变，即继续匹配字符下一位，因为*可以匹配多位；
这里需要注意的是：Java里，要时刻检验数组是否越界。*/
public class Solution {
public boolean match(char[] str, char[] pattern) {
if (str == null || pattern == null) {
return false;
}
int strIndex = 0;
int patternIndex = 0;
return matchCore(str, strIndex, pattern, patternIndex);
}

public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
// 有效性检验：str到尾，pattern到尾，匹配成功
if (strIndex == str.length && patternIndex == pattern.length)
return true;
// pattern先到尾，匹配失败
if (strIndex != str.length && patternIndex == pattern.length)
return false;
// 模式第2个是*，且字符串第1个跟模式第1个匹配,分3种匹配模式；如不匹配，模式后移2位
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (pattern[patternIndex] == '.' && strIndex != str.length)) {
return // 模式后移2，视为x*匹配0个字符
matchCore(str, strIndex, pattern, patternIndex + 2)
// 视为模式匹配1个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex + 2)
// *匹配1个，再匹配str中的下一个
|| matchCore(str, strIndex + 1, pattern, patternIndex);

} else {
return matchCore(str, strIndex, pattern, patternIndex + 2);
}
} // 模式第2个不是*，且字符串第1个跟模式第1个匹配，则都后移1位，否则直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
}
return false;
}
}

# 字符流中第一个不重复的字符

import java.util.ArrayList;
import java.util.HashMap;

public class Solution {
// 使用map记录出现的次数
HashMap<Character, Integer> map = new HashMap<>();
// 使用list记录当前的所有输入的字符，不能使用set等
ArrayList<Character> list = new ArrayList<Character>();

// Insert one char from stringstream
public void Insert(char ch) {
if (map.containsKey(ch))
map.put(ch, map.get(ch) + 1);
else
map.put(ch, 1);
}

// return the first appearence once char in current stringstream
public char FirstAppearingOnce() {
for (Character c : list)
if (map.get(c) == 1)
return c;
return '#';
}
}

# 链表中环的入口结点

//使用set
HashSet<ListNode> set = new HashSet<>();
}
return null;
}

/*算法思想：
第一步，找环中相汇点。分别用p1，p2指向链表头部，
* p1每次走一步，p2每次走二步，直到p1==p2找到在环中的相汇点。
* 第二步，找环的入口。
* 接上步，当p1==p2时，p2所经过节点数为2x,p1所经过节点数为x,
* 设环中有n个节点,p2比p1多走一圈有2x=n+x; n=x;
* 可以看出p1实际走了一个环的步数，再让p2指向链表头部，p1位置不变，p1,p2每次走一步直到p1==p2;
* 此时p1指向环的入口。
* 比剑指offer上更为简洁。*/
public class Solution {
return null;
if (meetNode == null)
return null;
ListNode fast = meetNode;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast)
return slow;

}
return null;
}
}

# 删除链表中重复的结点

## 重复结点不保留

	public ListNode deleteDuplication(ListNode pHead) {
return null;
int myfirst = -1;
myfirst = -2;
// 新建一个节点，防止头结点要被删除
while (q != null) {
// 此while跳过重复结点
while (q.next != null && q.val == q.next.val)
q = q.next;
// 如果指针移动了
if (p.next != q) {
q = q.next;
// 让p指向非重复结点后的第一个
p.next = q;
} else {
p = q;
q = q.next;
}

}
}

## 重复结点保留

public ListNode deleteDuplicates(ListNode head) {
return null;
while (cur.next != null) {
if (cur.next.val == cur.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
}


# 表示数值的字符串

public class Solution {
public boolean isNumeric(char[] str) {
if (str == null)
return false;
int length = str.length;
if (length == 0)
return true;
int idx = 0;
if (str[0] == '+' || str[0] == '-')
idx++;
int num = 0;
int dot = 0;
int e = 0;
while (idx < length) {
if (str[idx] >= '0' && str[idx] <= '9') {
idx++;
num = 1;
// .前面可以没有数字
} else if (str[idx] == '.') {
// e后面不能有.,e的个数不能大于1
if (dot > 0 || e > 0)
return false;

dot++;
idx++;
} else if (str[idx] == 'e' || str[idx] == 'E') {
// 重复e或者e前面没有数字
if (e > 0 || num == 0) {
return false;
}
e++;
idx++;
// 符号不能在最后一位
if (idx < length &&
(str[idx] == '-' || str[idx] == '+')) {
idx++;
}
// 表示e或者符号在最后一位
if (idx == length) {
return false;
}
} else {
return false;
}

}
return true;
}
// "-.123" 为true
public boolean isNumericWithMatchs(char[] str) {
String res = String.valueOf(str);
return res.matches("[+-]?[0-9]{0,}(\\.?[0-9]{1,})?([Ee][+-]?[0-9]{1,})?");
}
}

# 二叉树的下一个结点

class TreeLinkNode {
int val;
// 父节点

this.val = val;
}
}
public class Solution {
if (pNode == null)
return null;
// 如果有右子树，则找右子树的最左节点
if (pNode.right != null) {
// 如果此时pNode没有左子树，那么它就是下一个结点
pNode = pNode.right;
while (pNode.left != null)
pNode = pNode.left;
return pNode;
}
// 非跟结点，并且没有右子树
while (pNode.next != null) {
// 找到一个结点是该其父亲的左孩子
if (pNode.next.left == pNode)
return pNode.next;
pNode = pNode.next;
// 找到返回父节点,此时pNode.next也有可能为空
}
// 是跟结点且没有左子树
return null;
}
}

# 对称的二叉树

二叉树是否对称，只要采用前序、中序、后序、层次遍历等任何一种遍历方法，分为先左后右和先右后左两种方法，只要两次结果相等就说明这棵树是一颗对称二叉树。
import java.util.Stack;
public class Solution{
// 迭代版本
boolean isSymmetricalWithIter(TreeNode pRoot) {
Stack<TreeNode> s = new Stack<>();
if (pRoot == null)
return true;
s.push(pRoot.left);
s.push(pRoot.right);
while (!s.isEmpty()) {
TreeNode tmp1 = s.pop();
TreeNode tmp2 = s.pop();
// 注意if顺序，及其条件
if ((tmp1 != null && tmp2 == null) || (tmp1 == null && tmp2 != null))
return false;
if (tmp1 != null && tmp2 != null) {
if (tmp1.val != tmp2.val)
return false;
s.push(tmp1.left);
s.push(tmp2.right);
s.push(tmp1.right);
s.push(tmp2.left);
}
}
return true;
}

// 递归版本
boolean isSymmetrical(TreeNode pRoot) {
return isSymmetrical(pRoot, pRoot);
}

private boolean isSymmetrical(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null)
return true;
if (root1 == null || root2 == null)
return false;
// 类似前序遍历
if (root1.val != root2.val)
return false;
// 避免数字重复，要遍历空节点
return isSymmetrical(root1.left, root2.right) && isSymmetrical(root1.right, root2.left);
}
}


# 把二叉树打印成多行

import java.util.*;
public class Solution{
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
if (pRoot == null)
return ans;
q.offer(pRoot);
// 下一层需要打印的结点数
int nextLevel = 0;
// 当前层需要打印的结点数
ArrayList<Integer> tmp = new ArrayList<>();
int toBePrinted = 1;
while (!q.isEmpty()) {
TreeNode node = q.poll();
if (node.left != null) {
q.offer(node.left);
nextLevel++;
}
if (node.right != null) {
q.offer(node.right);
nextLevel++;
}
toBePrinted--;
if (toBePrinted == 0) {
tmp.clear();
// 下次打印的是q的所有结点
// or toBePrinted=q.size();省去nextLevel变量
toBePrinted = nextLevel;
nextLevel = 0;
}
}
return ans;
}
}


# 之字形打印二叉树

import java.util.*;
public class Solution{
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
if (pRoot == null)
return ans;
Stack<TreeNode> stack = new Stack<>();
Stack<TreeNode> nextStack = new Stack<>();
int flag = 0;
ArrayList<Integer> lay = new ArrayList<>();
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
// 如果当前是从左到右遍历，按左子树右子树的顺序添加
if (flag == 0) {
if (node.left != null)
if (node.right != null)
} else// 如果当前是从右到左遍历，按右子树左子树的顺序添加
{
if (node.right != null)
if (node.left != null)
}
if (stack.isEmpty()) {
// 交换两个栈
Stack<TreeNode> tmp = stack;
stack = nextStack;
nextStack = tmp;
// 标记下一层处理的方向
flag = 1 - flag;
lay.clear();
}
}
return ans;

}
}

# 序列化二叉树

public class Solution {
String Serialize(TreeNode root) {
StringBuffer sb = new StringBuffer();
if (root == null) {
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");
sb.append(Serialize(root.left));
sb.append(Serialize(root.right));
return sb.toString();
}

int index = -1;
TreeNode Deserialize(String str) {
index++;
int len = str.length();
if (index >= len)
return null;
String[] strr = str.split(",");
TreeNode node = null;
if (!strr[index].equals("#")) {
node = new TreeNode(Integer.valueOf(strr[index]));
node.left = Deserialize(str);
node.right = Deserialize(str);
}
return node;
}
}

# 二叉搜索树的第k个结点

import java.util.Stack;
public class Solution{
// 使用递归实现，java无引用注意使用全局变量
int index = 0;
TreeNode KthNode(TreeNode pRoot, int k) {
if (pRoot != null) {
TreeNode node = KthNode(pRoot.left, k);
if (node != null)
return node;

if (++index == k)
return pRoot;

node = KthNode(pRoot.right, k);
if (node != null)
return node;
}
return null;
}

// 使用栈实现
TreeNode KthNodeWithStack(TreeNode pRoot, int k) {
Stack<TreeNode> stack = new Stack<>();
while (pRoot != null || !stack.isEmpty()) {
if (pRoot != null) {
stack.push(pRoot);
pRoot = pRoot.left;
} else {
pRoot = stack.pop();
if (--k == 0)
return pRoot;
pRoot = pRoot.right;
}
}
return null;

}
}

# 数据流中的中位数

//Java的PriorityQueue 是从JDK1.5开始提供的新的数据结构接口，默认内部是自然排序，结果为小顶堆，也可以自定义排序器，比如下面反转比较，完成大顶堆。
//思路：
//为了保证插入新数据和取中位数的时间效率都高效，这里使用大顶堆+小顶堆的容器，并且满足：
//1、两个堆中的数据数目差不能超过1，这样可以使中位数只会出现在两个堆的交接处；
//2、大顶堆的所有数据都小于小顶堆，这样就满足了排序要求。
//用两个堆保存数据，保持两个堆的数据保持平衡（元素个数相差不超过1）大顶堆存放的数据要比小顶堆的数据小当两个推中元素为偶数个，将新加入元素加入到大顶堆，如果要加入的数据，比小顶堆的最小元素大，先将该元素插入小顶堆，然后将小顶堆的最小元素插入到大顶堆。当两个推中元素为奇数个，将新加入元素加入到小顶堆，如果要加入的数据，比大顶堆的最大元素小，先将该元素插入大顶堆，然后将大顶堆的最大元素插入到小顶堆。
public class Solution {
int count = 0;
PriorityQueue<Integer> minheap = new PriorityQueue<Integer>();
// 11是默认的初始容量
PriorityQueue<Integer> maxheap = new PriorityQueue<Integer>(11, new Comparator<Integer>() {
@Override
// PriorityQueue默认是小顶堆，实现大顶堆，需要反转默认排序器
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});

public void Insert(Integer num) {
count++;
if ((count & 1) == 0) {// 判断偶数
if (!maxheap.isEmpty() && num < maxheap.peek()) {
maxheap.offer(num);
num = maxheap.poll();
}
minheap.offer(num);
} else {
if (!minheap.isEmpty() && num > minheap.peek()) {
minheap.offer(num);
num = minheap.poll();
}
maxheap.offer(num);
}
}

public Double GetMedian() {
if (count == 0)
throw new RuntimeException("非法");
double median = 0;
// 总数为奇数时，大顶堆堆顶就是中位数
if ((count & 1) == 1)
median = maxheap.peek();
else
// 注意2.0
median = (maxheap.peek() + minheap.peek()) / 2.0;
return median;

}
}


# 滑动窗口的最大值

import java.util.*;

public class Solution {
public static void main(String[] args) {
int num[] = { 4, 2, 3, 2, 6, 2, 5, 1 };
System.out.println(maxInWindows(num, 4));
}
// 利用一个双向队列deque<int> dq保存最大值。
// 窗口右滑遇到新数时，首先判断若左侧滑出窗口的是最大值，则将双向队列的对手元素删除。然后依次将新数与队列末尾元素进行比较，依次删掉队尾小于新数的元素直到遇到大的元素或队列为空。将新数压入队列，保证队列中保存的元素是降序存在的，这里队列保存的就是当下滑动窗口的第一大元素、第二大元素···
// 另外，此处队列保存的的是原数组的下标，这样既可以保存数值，也可以判断队列中的数是否滑出窗口。
public static ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> maxWindows = new ArrayList<>();
if (num == null || size == 0 || num.length == 0 || num.length < size)
return maxWindows;
for (int i = 0; i < size; i++) {
while (!dq.isEmpty() && num[i] > num[dq.getLast()])
dq.removeLast();
}
//System.out.println(dq);
for (int i = size; i < num.length; i++) {
while (!dq.isEmpty() && num[i] >= num[dq.getLast()])
dq.removeLast();

if (!dq.isEmpty() && dq.getFirst() <= i - size)
dq.removeFirst();
System.out.println(i + "--" + dq);
}
return maxWindows;
}
}

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客