剑指Offer汇总:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Stack;
public class NiuKe {
public static void main(String[] args) {
NiuKe nk = new NiuKe();
ArrayList<String> arrayList = nk.Permutation("abcdcs");
int i = 1;
for (String arrayList2 : arrayList) {
System.out.println(i + ":" + arrayList2);
i++;
}
}
// 输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> ret = new ArrayList<>();
while (listNode != null) {
ret.add(0, listNode.val);
listNode = listNode.next;
}
return ret;
}
// 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
// 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
if (pre.length == 0 || in.length == 0)
return null;
return reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
public TreeNode reConstructBinaryTree(int[] pre, int p_begin, int p_end, int[] in, int i_begin, int i_end) {
if (p_end - p_begin < 0 || i_end - i_begin < 0)
return null;
TreeNode root = new TreeNode(pre[p_begin]);
int mid = 0;
for (int i = i_begin; i <= i_end; i++) {
if (in[i] == root.val) {
mid = i;
break;
}
}
int len = mid - i_begin;
root.left = reConstructBinaryTree(pre, p_begin + 1, p_begin + len, in, i_begin, mid - 1);
root.right = reConstructBinaryTree(pre, p_begin + len + 1, p_end, in, mid + 1, i_end);
return root;
}
// 用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if (!stack2.isEmpty()) {
return stack2.pop();
} else {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop());
}
return stack2.pop();
}
}
// ***
// 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
// 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。
// 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
public int minNumberInRotateArray(int[] array) {
if (array.length == 0)
return 0;
return minNumberInRotateArrayCore(array, 0, array.length - 1);
}
public int minNumberInRotateArrayCore(int[] array, int begin, int end) {
while (begin < end) {
int mid = (begin + end) / 2;
if (array[begin] < array[end])
return array[begin];
if (array[begin] == array[end]) {
begin++;
end--;
continue;
}
if (array[mid] > array[begin]) {
begin = mid + 1;
} else if (array[mid] == array[begin]) {
begin = begin + 1;
} else {
end = mid;
}
}
return array[begin];
}
// 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)。n<=39
public int Fibonacci(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n == 2)
return 1;
int sum1 = 1;
int sum2 = 1;
int ret = 0;
for (int i = 3; i <= n; i++) {
ret = sum1 + sum2;
sum1 = sum2;
sum2 = ret;
}
return ret;
}
// 一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
public int JumpFloor(int target) {
if (target == 0)
return 1;
if (target == 1)
return 1;
if (target == 2)
return 2;
int ret = 2;
for (int i = 3; i <= target; i++) {
ret = ret * 2;
}
return ret;
}
// 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
public int RectCover(int target) {
if (target < 0)
return 0;
if (target == 1)
return 1;
if (target == 2)
return 2;
return RectCover(target - 1) + RectCover(target - 2);
}
// 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
public int NumberOf1(int n) {
int ret = 0;
for (int i = 0; i < 32; i++) {
if ((n & 1) == 1) {
ret++;
}
n = n >> 1;
}
return ret;
}
// ***
// 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
public double Power(double base, int exponent) {
if (base == 0 && exponent == 0)
return -1;
if (exponent == 0)
return 1;
if (exponent < 0)
return 1.0 / Power(base, -1 * exponent);
return PowerCore(base, exponent);
}
public double PowerCore(double base, int exponent) {
if (exponent == 1)
return base;
if ((exponent & 1) == 0) {
double temp = PowerCore(base, exponent >> 1);
return temp * temp;
} else {
double temp = PowerCore(base, exponent >> 1);
return temp * temp * base;
}
}
// 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,
// 所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
public void reOrderArray(int[] array) {
int ji_index = 0;
int[] s = new int[array.length];
int s_index = 0;
for (int i = 0; i < array.length; i++) {
// 偶数要进行保存
if (array[i] % 2 == 0) {
s[s_index] = array[i];
s_index++;
}
if (array[i] % 2 != 0) {
array[ji_index] = array[i];
ji_index++;
}
}
// 将偶数存回去
// 0--s_index
int j = 0;
while (j < s_index) {
array[ji_index] = s[j];
j++;
ji_index++;
}
}
// ***
// 递归。。。。。
// 输入一个链表,反转链表后,输出新链表的表头。
public ListNode ReverseList(ListNode head) {
// 终止条件
if (head == null || head.next == null)
return head;
// 本层做什么
ListNode next = ReverseList(head.next);
head.next.next = head;
head.next = null;
// 返回上一层什么
return next;
}
class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
// 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
if (root1 == null || root2 == null)
return false;
if (root1.val == root2.val) {
if (HasSubtreeCore(root1, root2)) {
return true;
}
}
boolean left = false;
;
boolean right = false;
if (root1.left != null)
left = HasSubtree(root1.left, root2);
if (root1.right != null)
right = HasSubtree(root1.right, root2);
return (left || right);
}
public boolean HasSubtreeCore(TreeNode root1, TreeNode root2) {
if (root2 == null)
return true;
if (root1 == null)
return false;
if (root1.val == root2.val) {
return HasSubtreeCore(root1.left, root2.left) && HasSubtreeCore(root1.right, root2.right);
} else
return false;
}
// 操作给定的二叉树,将其变换为源二叉树的镜像。
public void Mirror(TreeNode root) {
if (root == null)
return;
if (root.left == null && root.right == null)
return;
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
if (root.left != null)
Mirror(root.left);
if (root.right != null)
Mirror(root.right);
}
// 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
// 假设压入栈的所有数字均不相等。
// 例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,
// 但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
public boolean IsPopOrder(int[] pushA, int[] popA) {
Stack<Integer> s = new Stack<>();
int j = 0;
for (int i = 0; i < popA.length; i++) {
if (!s.isEmpty()) {
if (s.peek() == popA[i]) {
s.pop();
continue;
}
}
for (; j < pushA.length; j++) {
if (pushA[j] != popA[i]) {
s.push(pushA[j]);
} else {
break;
}
}
// 此处有问题,有可能push完之后,j也会==pushA.length
if (j < pushA.length && pushA[j] == popA[i]) {
j++;
continue;
} else
return false;
}
if (s.isEmpty())
return true;
else
return false;
}
// 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence.length <= 1)
return true;
return VerifySquenceOfBSTCore(sequence, 0, sequence.length - 1);
}
public boolean VerifySquenceOfBSTCore(int[] sequence, int begin, int end) {
int len = end - begin + 1;
if (len == 0 || len == 1)
return true;
int root = sequence[end];
int mid = end;
for (int i = begin; i < end; i++) {
if (sequence[i] > root) {
mid = i;
break;
}
}
for (int k = mid + 1; k < end; k++) {
if (sequence[k] < root)
return false;
}
return VerifySquenceOfBSTCore(sequence, begin, mid - 1) && VerifySquenceOfBSTCore(sequence, mid, end - 1);
}
// ***
// 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
// 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
ArrayList<ArrayList<Integer>> ret1 = new ArrayList<>();
ArrayList<Integer> temp = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
if (root == null)
return ret1;
target -= root.val;
temp.add(root.val);
if (target == 0 && root.left == null && root.right == null) {
ret1.add(new ArrayList<>(temp));
} else {
FindPath(root.left, target);
FindPath(root.right, target);
}
temp.remove(temp.size() - 1);
return ret1;
}
// 输入一个字符串,按字典序打印出该字符串中字符的所有排列。
// 例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
ArrayList<String> res = new ArrayList<String>();
public ArrayList<String> Permutation(String str) {
if (str == null)
return res;
PermutationHelper(str.toCharArray(), 0);
Collections.sort(res);
return res;
}
public void PermutationHelper(char[] str, int i) {
HashSet<Character> s = new HashSet<>();
if (i == str.length - 1) {
res.add(String.valueOf(str));
} else {
for (int j = i; j < str.length; j++) {
if (s.contains(str[j])) {
continue;
}
s.add(str[j]);
swap(str, i, j);
PermutationHelper(str, i + 1);
swap(str, i, j);
}
}
}
public void swap(char[] str, int i, int j) {
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
// 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),
// 返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null)
return pHead;
RandomListNode head = pHead;
while (head != null) {
RandomListNode head2 = new RandomListNode(head.label);
head2.next = head.next;
head.next = head2;
head = head.next.next;
}
head = pHead;
while (head != null) {
if (head.random != null)
head.next.random = head.random.next;
else
head.next.random = null;
head = head.next.next;
}
RandomListNode ret = new RandomListNode(0);
RandomListNode retu = new RandomListNode(0);
retu.next = ret;
head = pHead;
while (head != null) {
ret.next = head.next;
head.next = head.next.next;
head = head.next;
ret = ret.next;
}
return retu.next.next;
}
// 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
TreeNode head;
TreeNode end;
public TreeNode Convert(TreeNode pRootOfTree) {
if (pRootOfTree == null)
return null;
ConvertCore(pRootOfTree);
return head;
}
public void ConvertCore(TreeNode pRootOfTree) {
if (pRootOfTree == null)
return;
ConvertCore(pRootOfTree.left);
if (head == null) {
head = pRootOfTree;
end = pRootOfTree;
} else {
end.right = pRootOfTree;
pRootOfTree.left = end;
end = pRootOfTree;
}
ConvertCore(pRootOfTree.right);
}
// 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
// 例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
public int MoreThanHalfNum_Solution(int[] array) {
if (array.length == 0)
return 0;
if (array.length == 1)
return array[0];
int temp = array[0];
int count = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] == temp) {
count++;
} else {
count--;
if (count == 0) {
temp = array[i];
count = 1;
}
}
}
count = 0;
for (int i = 0; i < array.length; i++) {
if (temp == array[i]) {
count++;
}
}
if (count > array.length / 2)
return temp;
else
return 0;
}
// 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> ret = new ArrayList<>();
PriorityQueue<Integer> q = new PriorityQueue<>();
if (input.length < k)
return ret;
for (int i = 0; i < input.length; i++) {
q.offer(input[i]);
}
for (int i = k; i < k; i++) {
ret.add(q.poll());
}
return ret;
}
// 输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
public int FindGreatestSumOfSubArray(int[] array) {
if (array.length == 0)
return 0;
int ret = Integer.MIN_VALUE;
int sum = 0;
for (int i = 0; i < array.length; i++) {
if (sum >= 0) {
sum = sum + array[i];
} else {
if (array[i] < ret) {
continue;
} else {
sum = array[i];
}
}
if (sum > ret)
ret = sum;
}
return ret;
}
// 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
public String PrintMinNumber(int[] numbers) {
String ret = "";
if (numbers.length == 0)
return ret;
if (numbers.length == 1)
return String.valueOf(numbers[0]);
String[] s = new String[numbers.length];
for (int i = 0; i < numbers.length; i++) {
s[i] = String.valueOf(numbers[i]);
}
Arrays.sort(s, new Comparator<String>() {
public int compare(String a, String b) {
String c1 = a + b;
String c2 = b + a;
return c1.compareTo(c2);
}
});
for (int i = 0; i < s.length; i++) {
ret = ret + s[i];
}
return ret;
}
// 把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。
// 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
public int GetUglyNumber_Solution(int index) {
if(index == 0)
return 0;
if(index == 1)
return 1;
int index_2 = 0;
int index_3 = 0;
int index_5 = 0;
int[] ret = new int[index];
ret[0] = 1;
for(int i=1; i<index; i++) {
ret[i] = Math.min(ret[index_2]*2, ret[index_3]*3);
ret[i] = Math.min(ret[i], ret[index_5]*5);
if(ret[i] == ret[index_2] * 2) {
index_2++;
}
if(ret[i] == ret[index_3] * 3) {
index_3++;
}
if(ret[i] == ret[index_5] * 5) {
index_5++;
}
}
return ret[index-1];
}
// 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
public int FirstNotRepeatingChar(String str) {
int len = str.length();
if (len == 0)
return -1;
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < len; i++) {
if (map.containsKey(str.charAt(i))) {
int value = map.get(str.charAt(i));
map.put(str.charAt(i), value + 1);
} else {
map.put(str.charAt(i), 1);
}
}
for (int i = 0; i < len; i++) {
if (map.get(str.charAt(i)) == 1)
return i;
}
return -1;
}
// 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
// 输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
public int InversePairs(int[] array) {
if (array.length <= 1)
return 0;
return InversePairsCore(array, 0, array.length - 1);
}
public int InversePairsCore(int[] array, int begin, int end) {
if (begin >= end)
return 0;
int mid = (begin + end) >> 1;
int left = InversePairsCore(array, begin, mid);
int right = InversePairsCore(array, mid + 1, end);
// 合并并计数
int currentCount = 0;
int i = mid;
int j = end;
int k = end - begin + 1;
int[] temp = new int[k];
k--;
while (i >= begin && j > mid) {
if (array[i] > array[j]) {
temp[k--] = array[i--];
currentCount += (j - mid);
if (currentCount >= 1000000007)
currentCount = currentCount % 1000000007;
} else {
temp[k--] = array[j--];
}
}
while (i >= begin) {
temp[k--] = array[i--];
}
while (j > mid) {
temp[k--] = array[j--];
}
for (k = 0; k < temp.length; k++) {
array[k + begin] = temp[k];
}
int current2 = (left + right) % 1000000007;
return (current2 + currentCount) % 1000000007;
}
// 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
public int TreeDepth(TreeNode root) {
if (root == null)
return 0;
if (root.left == null && root.right == null)
return 1;
return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;
}
// 输入一棵二叉树,判断该二叉树是否是平衡二叉树。
private class ReturnNode {
boolean isB;
int depth;
public ReturnNode(int depth, boolean isB) {
this.isB = isB;
this.depth = depth;
}
}
public boolean IsBalanced_Solution(TreeNode root) {
if (root == null)
return false;
return IsBalanced_SolutionCore(root).isB;
}
public ReturnNode IsBalanced_SolutionCore(TreeNode root) {
if (root == null)
return new ReturnNode(0, true);
if (root.left == null && root.right == null)
return new ReturnNode(1, true);
ReturnNode left = IsBalanced_SolutionCore(root.left);
ReturnNode right = IsBalanced_SolutionCore(root.right);
if (left.isB && right.isB) {
if (Math.abs(left.depth - right.depth) <= 1) {
return new ReturnNode(Math.max(left.depth, right.depth) + 1, true);
}
}
return new ReturnNode(0, false);
}
// 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
// 思路:所有的数之间异或之后,结果为所求的两个数的异或
// 取异或结果最右边的一个1,然后遍历数组,将其中该位置为0的进行异或,结果为所求的一个。然后将为1的进行异或,结果为所求的另一个。
public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) {
int num = 0;
for (int i = 0; i < array.length; i++) {
num = num ^ array[i];
}
int target = num & (~num + 1);
for (int i = 0; i < array.length; i++) {
if ((array[i] & (~array[i] + 1)) == target) {
num1[0] = num1[0] ^ array[i];
} else {
num2[0] = num2[0] ^ array[i];
}
}
}
// 小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。
// 但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。
// 没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。
// 现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
// 思路:sum依次除以sum/2 sum/3 。。。一直除到2
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
if (sum < 3)
return ret;
FindContinuousSequenceCore(ret, sum);
return ret;
}
public void FindContinuousSequenceCore(ArrayList<ArrayList<Integer>> ret, int sum) {
int min = 1;
int max = 2;
int end = sum / 2;
int currentSum = 1 + 2;
while (min <= end) {
if (currentSum == sum) {
add(ret, min, max);
currentSum -= min;
min++;
max++;
currentSum += max;
} else if (currentSum < sum) {
max++;
currentSum += max;
} else {
currentSum -= min;
min++;
}
}
}
public void add(ArrayList<ArrayList<Integer>> ret, int min, int max) {
ArrayList<Integer> r = new ArrayList<>();
for (int i = min; i <= max; i++) {
r.add(i);
}
ret.add(r);
}
// 汇编语言中有一种移位指令叫做循环左移(ROL),
// 现在有个简单的任务,就是用字符串模拟这个指令的运算结果。
// 对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。
// 例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
public String LeftRotateString(String str, int n) {
if (n < 0)
return null;
int len = str.length();
n = n % len;
String s1 = str.substring(0, n);
String s2 = str.substring(n, len);
return s2 + s1;
}
// 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。
// 同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。
// 例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,
// 正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
// 思路:先整体进行字符串翻转,然后以空格为间隙,翻转每一个子串
public String ReverseSentence(String str) {
if (str.trim().length() <= 1)
return str;
StringBuilder s = new StringBuilder(str);
s = s.reverse();
String ret = s.toString();
String[] split = ret.split(" ");
StringBuilder retu = new StringBuilder();
for (int i = 0; i < split.length; i++) {
StringBuilder ss2 = new StringBuilder(split[i]);
retu.append(ss2.reverse());
if (i != split.length - 1)
retu.append(" ");
}
return retu.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的运气如何,
// 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
public boolean isContinuous(int[] numbers) {
int zero = 0, dis = 0;
if (numbers.length != 5)
return false;
Arrays.sort(numbers);
for (int i = 0; i < 4; i++) {
if (numbers[i] == 0) {
zero++;
continue;
}
if (numbers[i] == numbers[i + 1])
return false;
if (numbers[i + 1] - numbers[i] > 1) {
dis += numbers[i + 1] - numbers[i] - 1;
}
}
if (zero >= dis)
return true;
else
return false;
}
// 有个游戏是这样的:首先,让小朋友们围成一个大圈
// 然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列,并且不再回到圈中,
// 从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友(注:小朋友的编号是从0到n-1)
public int LastRemaining_Solution(int n, int m) {
if (m < 1 || n < 1)
return -1;
if (n == 1)
return 0;
LinkedList<Integer> ret = new LinkedList<>();
for (int i = 0; i < n; i++) {
ret.add(i);
}
int index = -1;
while (ret.size() > 1) {
index = (index + m) % ret.size();
ret.remove(index);
index--;
}
return ret.get(0);
}
// 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
public int Sum_Solution(int n) {
if (n < 0)
return -1;
if (n == 0)
return 0;
if (n == 1)
return 1;
return n + Sum_Solution(n - 1);
}
// 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
public int Add(int num1, int num2) {
// 先忽略进位,进行求和
while (num2 != 0) {
int sum = num1 ^ num2;
int carry = (num1 & num2) << 1;
num1 = sum;
num2 = carry;
}
return num1;
}
// 在一个长度为n的数组里的所有数字都在0到n-1的范围内。
// 数组中某些数字是重复的,但不知道有几个数字是重复的。
// 也不知道每个数字重复几次。
// 请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
public boolean duplicate(int numbers[], int length, int[] duplication) {
HashSet<Integer> h = new HashSet<>();
for (int i = 0; i < length; i++) {
if (!h.add(numbers[i])) {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
// 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。
public int[] multiply(int[] A) {
int[] ret = new int[A.length];
if (A.length == 0)
return ret;
int temp1 = 1;
ret[0] = 1;
for (int i = 1; i < A.length; i++) {
ret[i] = temp1 * A[i - 1];
temp1 = temp1 * A[i - 1];
}
temp1 = A[A.length - 1];
for (int i = A.length - 2; i >= 0; i--) {
ret[i] = ret[i] * temp1;
temp1 = temp1 * A[i];
}
return ret;
}
// 请实现一个函数用来匹配包括'.'和'*'的正则表达式。
// 模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。
// 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
public boolean match(char[] str, char[] pattern) {
if (str.length == 0 && pattern.length == 0)
return true;
return matchCore(str, 0, pattern, 0);
}
public boolean matchCore(char[] str, int begin1, char[] pattern, int begin2) {
if (begin1 == str.length && begin2 == pattern.length)
return true;
if (begin1 != str.length && begin2 == pattern.length)
return false;
if (begin2 + 1 < pattern.length && pattern[begin2 + 1] == '*') {
if (begin1 < str.length && (pattern[begin2] == '.' || str[begin1] == pattern[begin2])) {
return matchCore(str, begin1, pattern, begin2 + 2) || matchCore(str, begin1 + 1, pattern, begin2)
|| matchCore(str, begin1 + 1, pattern, begin2 + 2);
} else {
return matchCore(str, begin1, pattern, begin2 + 2);
}
}
if (begin1 < str.length && begin2 < pattern.length
&& (pattern[begin2] == '.' || str[begin1] == pattern[begin2])) {
return matchCore(str, begin1 + 1, pattern, begin2 + 1);
} else
return false;
}
// 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead.next == null || pHead.next.next == null)
return null;
ListNode slow = pHead.next;
ListNode fast = pHead.next.next;
while (fast != null) {
if (fast == slow) {
fast = pHead;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
slow = slow.next;
fast = fast.next.next;
}
return null;
}
// 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5
// 处理后为 1->2->5
public ListNode deleteDuplication(ListNode pHead) {
ListNode retu = new ListNode(0);
ListNode ret = new ListNode(0);
retu.next = ret;
if (pHead == null || pHead.next == null)
return pHead;
ListNode head = pHead;
ListNode next = pHead.next;
while (head != null && next != null) {
if (head.val != next.val) {
ret.next = head;
ret = ret.next;
head = head.next;
if (head == null)
break;
next = head.next;
} else {
do {
head = head.next;
} while (head != null && head.val == next.val);
if (head == null) {
ret.next = head;
break;
}
next = head.next;
}
}
if (head != null)
ret.next = head;
return retu.next.next;
}
// 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
public TreeLinkNode GetNext(TreeLinkNode pNode) {
if (pNode == null) {
return null;
}
if (pNode.right != null) {
TreeLinkNode node = pNode.right;
while (node.left != null) {
node = node.left;
}
return node;
}
while (pNode.next != null) {
TreeLinkNode root = pNode.next;
if (pNode == root.left)
return root;
pNode = root;
}
return null;
}
// 请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
boolean isSymmetrical(TreeNode pRoot) {
if (pRoot == null)
return true;
if (pRoot.left == null && pRoot.right == null)
return true;
if (pRoot.left == null || pRoot.right == null)
return false;
return isSymmetricalCore(pRoot.left, pRoot.right);
}
boolean isSymmetricalCore(TreeNode left, TreeNode right) {
if (left == null && right == null)
return true;
if (left == null || right == null)
return false;
if (left.val == right.val) {
return isSymmetricalCore(left.left, right.right) && isSymmetricalCore(left.right, right.left);
} else
return false;
}
// 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> retu = new ArrayList<>();
int index = 1;
if (pRoot == null)
return retu;
LinkedList<TreeNode> q = new LinkedList<>();
Stack<Integer> s = new Stack<>();
q.add(pRoot);
while (!q.isEmpty()) {
int len = q.size();
ArrayList<Integer> ret1 = new ArrayList<>();
for (int i = 0; i < len; i++) {
TreeNode top = q.poll();
if (index % 2 == 1)
ret1.add(top.val);
else {
s.push(top.val);
}
if (top.left != null) {
q.add(top.left);
}
if (top.right != null) {
q.add(top.right);
}
}
index++;
while (!s.isEmpty()) {
ret1.add(s.pop());
}
retu.add(ret1);
}
return retu;
}
// 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
ArrayList<ArrayList<Integer>> Print2(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> retu = new ArrayList<>();
if (pRoot == null)
return retu;
LinkedList<TreeNode> q = new LinkedList<>();
q.add(pRoot);
while (!q.isEmpty()) {
int len = q.size();
ArrayList<Integer> ret1 = new ArrayList<>();
for (int i = 0; i < len; i++) {
TreeNode top = q.poll();
ret1.add(top.val);
if (top.left != null) {
q.add(top.left);
}
if (top.right != null) {
q.add(top.right);
}
}
retu.add(ret1);
}
return retu;
}
// ***
// 请实现两个函数,分别用来序列化和反序列化二叉树
// 先序遍历
String Serialize(TreeNode node) {
StringBuilder ret = new StringBuilder();
if (node == null)
ret.append("$,");
else {
ret.append(node.val + ",");
ret.append(Serialize(node.left));
ret.append(Serialize(node.right));
}
return ret.toString();
}
TreeNode Deserialize(String str) {
TreeNode ret = null;
if (str.length() == 0)
return ret;
return DeserializeCore(str);
}
int Index = 0;
TreeNode DeserializeCore(String str) {
String[] nodes = str.split(",");
TreeNode root = null;
if (Index >= nodes.length)
return root;
// if(nodes[Index] != "$") {
if (!nodes[Index].equals("$")) {
root = new TreeNode(Integer.valueOf(nodes[Index]));
Index++;
root.left = DeserializeCore(str);
Index++;
root.right = DeserializeCore(str);
}
return root;
}
// 给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
int sum = 0;
TreeNode ret = null;
TreeNode KthNode(TreeNode pRoot, int k) {
if (pRoot == null)
return pRoot;
if (k < 0)
return null;
KthNode(pRoot.left, k);
if (pRoot != null) {
sum++;
}
if (sum == k) {
ret = pRoot;
}
KthNode(pRoot.right, k);
return ret;
}
// 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
// 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
// 我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
// 左大跟堆,右小根堆
PriorityQueue<Integer> l_max = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2 - o1;
}
});
PriorityQueue<Integer> r_min = new PriorityQueue<>();
public void Insert(Integer num) {
if (l_max.size() == 0) {
l_max.offer(num);
} else {
if (l_max.size() == r_min.size()) {
if (num > r_min.peek()) {
r_min.offer(num);
int temp = r_min.poll();
l_max.offer(temp);
} else {
l_max.offer(num);
}
} else {
if (num < l_max.peek()) {
l_max.offer(num);
int temp = l_max.poll();
r_min.offer(temp);
} else {
r_min.offer(num);
}
}
}
}
public Double GetMedian() {
if (l_max.size() == r_min.size()) {
return (l_max.peek() + r_min.peek()) / 2.0;
} else {
return l_max.peek() / 1.0;
}
}
// 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
// 例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,
// 他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个:
// {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1},
// {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
public ArrayList<Integer> maxInWindows(int[] num, int size) {
ArrayList<Integer> ret = new ArrayList<>();
if (num.length == 0 || size < 0)
return ret;
if (num.length < size)
return ret;
LinkedList<Integer> l = new LinkedList<>();
ArrayList<Integer> l_index = new ArrayList<>();
for (int i = 0; i < size; i++) {
if (l.size() == 0) {
l.add(num[i]);
l_index.add(i);
} else {
if (!l.isEmpty() && num[i] > l.get(l.size() - 1)) {
do {
l.remove(l.size() - 1);
l_index.remove(l_index.size() - 1);
} while (!l.isEmpty() && num[i] > l.get(l.size() - 1));
}
if (l.size() < size) {
l.add(num[i]);
l_index.add(i);
}
}
}
ret.add(l.get(0));
for (int i = size; i < num.length; i++) {
if (i - size >= l_index.get(0)) {
l.remove(0);
l_index.remove(0);
}
if (!l.isEmpty() && num[i] > l.get(l.size() - 1)) {
do {
l.remove(l.size() - 1);
l_index.remove(l_index.size() - 1);
} while (!l.isEmpty() && num[i] > l.get(l.size() - 1));
}
if (l.size() < size) {
l.add(num[i]);
l_index.add(i);
}
ret.add(l.get(0));
}
return ret;
}
// 思路:在保留了最大值的同时,需要保留窗口内该最大值右边的次大值(也有可能比最大值还大,此时该点成为最大值)。当最大值移出窗口时,次大值顶替最大值。
public ArrayList<Integer> maxInWindows2(int[] num, int size) {
ArrayList<Integer> ret = new ArrayList<>();
if (num.length == 0 || num.length < size || size < 1)
return ret;
if (size == 1) {
for (int i = 0; i < num.length; i++)
ret.add(num[i]);
return ret;
}
// 1.寻找windows内最大值
int max = num[0];
int max_index = 0;
int ci_max_index = 0;
boolean have_ci = false;
for (int i = 1; i < size; i++) {
if (num[i] > max) {
max = num[i];
max_index = i;
} else {
if (!have_ci) {
ci_max_index = i;
have_ci = true;
} else if (num[i] >= num[ci_max_index]) {
ci_max_index = i;
}
}
}
if (ci_max_index <= max_index) {
have_ci = false;
}
ret.add(max);
for (int i = size; i < num.length; i++) {
// 老大要退休
if (i - size >= max_index) {
// 如果有老二
if (have_ci) {
// 看老二和新人哪个当老大
// 老二当老大
if (num[ci_max_index] > num[i]) {
max_index = ci_max_index;
max = num[max_index];
// 最大的走了,次大的当上了老大,这时候发现没有当次大的了,因为次大和扫描到的该点之前有可能存在比该扫描到的点大的,而之前没有保存。故应该保存三个点。
// 对于size == 2的需要特殊注意下。此处不想改动了,故使用遍历的方式实现。
ci_max_index = i;
int third_max = num[max_index + 1];
for (int k = max_index + 1; k <= i; k++) {
if (num[k] >= third_max) {
ci_max_index = k;
third_max = num[ci_max_index];
}
}
have_ci = true;
} else { // 新人当老大
max_index = i;
max = num[max_index];
have_ci = false;
}
// 没有老二
} else { // 如果没有老二 ?应该不存在这种情况
max_index = i;
max = num[max_index];
have_ci = false;
}
ret.add(max);
continue;
}
// 老大不退休
// 新人当老大
if (num[i] >= max) {
max_index = i;
max = num[max_index];
have_ci = false;
} else { // 老大还是老大
// 如果没老二 ?应该不存在这种情况
if (!have_ci) {
ci_max_index = i;
have_ci = true;
} else { // 如果有老二,看看谁当老二
// 新人当老二
if (num[i] >= num[ci_max_index]) {
ci_max_index = i;
have_ci = true;
}
}
}
ret.add(max);
}
return ret;
}
// 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
// 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
// 如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。
// 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,
// 但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
if (matrix == null || rows < 1 || cols < 1 || str.length < 1)
return false;
boolean[] isVisited = new boolean[matrix.length];
for (boolean visit : isVisited) {
visit = false;
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (hasPathCore(matrix, rows, cols, str, i, j, 0, isVisited)) {
return true;
}
}
}
return false;
}
public boolean hasPathCore(char[] matrix, int rows, int cols, char[] str, int i, int j, int index,
boolean[] isVisited) {
if (index == str.length)
return true;
if (i >= rows || i < 0 || j >= cols || j < 0) {
return false;
}
if (matrix[i * cols + j] != str[index] || isVisited[i * cols + j] == true) {
return false;
}
isVisited[i * cols + j] = true;
boolean hasPath = false;
hasPath = hasPathCore(matrix, rows, cols, str, i - 1, j, index + 1, isVisited)
|| hasPathCore(matrix, rows, cols, str, i + 1, j, index + 1, isVisited)
|| hasPathCore(matrix, rows, cols, str, i, j - 1, index + 1, isVisited)
|| hasPathCore(matrix, rows, cols, str, i, j + 1, index + 1, isVisited);
if (!hasPath) {
isVisited[i * cols + j] = false;
}
return hasPath;
}
// 地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,
// 但是不能进入行坐标和列坐标的数位之和大于k的格子。
// 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
// 但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
public int movingCount(int threshold, int rows, int cols) {
if (threshold < 0)
return 0;
if (threshold < 1)
return 1;
boolean[] isVisited = new boolean[rows * cols];
for (boolean visit : isVisited) {
visit = false;
}
return movingCountCore(threshold, rows, cols, 0, 0, isVisited);
}
public int movingCountCore(int threshold, int rows, int cols, int i, int j, boolean[] isVisited) {
if (i >= rows || i < 0 || j >= cols || j < 0) {
return 0;
}
if (isVisited[i * cols + j] == true) {
return 0;
}
// 判断机器人能够进入方格
int sum = 0;
int i1 = i;
int j1 = j;
while (i1 != 0) {
sum += i1 % 10;
i1 = i1 / 10;
}
while (j1 != 0) {
sum += j1 % 10;
j1 = j1 / 10;
}
isVisited[i * cols + j] = true;
if (sum > threshold) {
return 0;
}
return 1 + movingCountCore(threshold, rows, cols, i, j - 1, isVisited)
+ movingCountCore(threshold, rows, cols, i, j + 1, isVisited)
+ movingCountCore(threshold, rows, cols, i - 1, j, isVisited)
+ movingCountCore(threshold, rows, cols, i + 1, j, isVisited);
}
}