import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Stack;
public class Solution {
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
/*
* 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则
*/
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head=list1;
ListNode pre=list1;
while(list1!=null&&list2!=null) {
if(list1.val<=list2.val) {
pre=list1;
list1=list1.next;
}else {
pre.next=list2;
list2=list2.next;
pre=pre.next;
pre.next=list1;
}
}
if(list2!=null) {
pre.next=list2;
}
return head;
}
public 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(root2==null) {
return false;
}
if(root1==null) {
return false;
}
if(!equal(root1,root2)) {
if(!HasSubtree(root1.left,root2)) {
if(!HasSubtree(root1.right,root2)) {
return false;
}else {
return true;
}
}else {
return true;
}
}else {
return true;
}
}
public boolean equal(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 equal(root1.left,root2.left)&&equal(root1.right,root2.right);
}
// public static 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 static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
// //如果Tree2已经遍历完了都能对应的上,返回true
// if (node2 == null) {
// return true;
// }
// //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
// if (node1 == null) {
// return false;
// }
// //如果其中有一个点没有对应上,返回false
// if (node1.val != node2.val) {
// return false;
// }
//
// //如果根节点对应的上,那么就分别去子节点里面匹配
// return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
// }
/*操作给定的二叉树,将其变换为源二叉树的镜像。
*
*/
public void Mirror(TreeNode root) {
if(root!=null) {
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
Mirror(root.left);
Mirror(root.right);
}
}
/*
*
*/
public ArrayList<Integer> printMatrix(int [][] matrix) {
//定义上下左右方向
int [][]direction= {{0,1},{1,0},{0,-1},{-1,0}};
boolean [][]visited=new boolean[matrix.length][matrix[0].length];
ArrayList<Integer>rs=new ArrayList();
//rs.add(100);
if(matrix.length==0){
return rs;
}
int i=0;
int j=-1;
int d=0;
while(rs.size()!=matrix.length*matrix[0].length) {
if(d==4) {
d=0;
}
i=i+direction[d][0];
j=j+direction[d][1];
if(i<0||i==matrix.length||j<0||j==matrix[0].length||visited[i][j]) {
i=i-direction[d][0];
j=j-direction[d][1];
d++;
}else {
rs.add(matrix[i][j]);
visited[i][j]=true;
}
}
return rs;
}
/*
* 定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
*
*/
Stack<Integer> s1=new Stack();
Stack<Integer> s2=new Stack();//辅助栈
public void push(int node) {
s1.push(node);
if(s1.isEmpty()||node<=s2.peek()) {
s2.push(node);
}
}
public void pop() {
if(s1.peek()==s2.peek()) {
s1.pop();
s2.pop();
}else {
s1.pop();
}
}
public int top() {
return s1.peek();
}
public int min() {
return s2.peek();
}
/*输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。
* 例如序列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();
s.push(pushA[0]);
int i=0;
int j=0;
//模拟压入弹出的过程
while(true) {
if(s.isEmpty()) {
s.push(pushA[j]);
j++;
}
if(s.peek()==popA[i]) {
s.pop();
i++;
if(i==popA.length) {
return true;
}
}else {
if(j==pushA.length) {
return false;
}
s.push(pushA[j]);
j++;
continue;
}
// if(j==pushA.length) {
// return false;
// }
}
}
//从上往下打印出二叉树的每个节点,同层节点从左至右打印。
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<TreeNode>tree=new ArrayList();
ArrayList<Integer>rs=new ArrayList();
if(root==null) {
return rs;
}
int i=0;
tree.add(root);
while(!tree.isEmpty()) {
TreeNode t=tree.get(0);
rs.add(tree.get(0).val);
if(tree.get(0).left!=null) {
tree.add(tree.get(0).left);
}
if(tree.get(0).right!=null) {
tree.add(tree.get(0).right);
}
tree.remove(0);
}
return rs;
}
/*
* 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
*/
//数组最后一个节点为根节点,由此可以找出左子树和右子树进行判断
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length==0) {
return false;
}
if(sequence.length==1) {
return true;
}
int root=sequence[sequence.length-1];
int i=0;
for(i=0;i<sequence.length-1;i++) {
if(sequence[i]>root) {
break;
}
}
for(int j=i;j<sequence.length;j++) {
if(sequence[j]<root) {
return false;
}
}
if(i==0) {
return VerifySquenceOfBST(Arrays.copyOfRange(sequence, i, sequence.length-1));
}else if(i==sequence.length-1) {
return VerifySquenceOfBST(Arrays.copyOfRange(sequence, 0, i));
}else {
return VerifySquenceOfBST(Arrays.copyOfRange(sequence, 0, i))&&
VerifySquenceOfBST(Arrays.copyOfRange(sequence, i, sequence.length-1));
}
}
/*
* 输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
* 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
*/
ArrayList<ArrayList<Integer>> rs=new ArrayList();
ArrayList<Integer> r=new ArrayList();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null) {
return rs;
}
target-=root.val;
r.add(root.val);
if(target==0&&root.left==null&&root.right==null) {
rs.add(new ArrayList(r));
}
FindPath(root.left,target);
FindPath(root.right,target);
r.remove(r.size()-1);
return rs;
}
/*
* 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),
* 返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
*/
//思路,先给每一个节点后面插入一个复制的节点,然后完成每个节点的random指针,在拆分开来
public 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 null;
}
RandomListNode p=pHead;
//首先在每一个点后面插入一个复制的点。
while(p!=null) {
RandomListNode node=new RandomListNode(p.label);
node.next=p.next;
p.next=node;
p=node.next;
}
RandomListNode pre=pHead;
p=pre.next;
RandomListNode rs=p;
while(p!=null) {
if(pre.random!=null) {
p.random=pre.random.next;
}else {
p.random=null;
}
pre=p.next;
if(p.next==null) {
break;
}
p=pre.next;
}
p=pHead.next;
while(p!=null) {
if(p.next==null) {
break;
}
p.next=p.next.next;
p=p.next;
}
return rs;
}
/*
* 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
*/
// >>>>>>>思考为什么将v作为create的参数就不正确了<<<<<<<<<<<<<<<<
//思路:直接利用中序遍历
TreeNode v=null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null){
return null;
}
//TreeNode v=null;
create(pRootOfTree);
TreeNode rs=pRootOfTree;
while(rs.left!=null){
rs=rs.left;
}
return rs;
}
public void create(TreeNode t) {
if(t==null) {
return;
}
create(t.left);
t.left=v;
if(v!=null){
v.right=t;
}
v=t;
create(t.right);
}
/*
* 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,
* 则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
*/
public ArrayList<String> Permutation(String str) {
return null;
}
/*
*
* 输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
*/
public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer>rs=new ArrayList();
int n=input.length;
if(k<=0||k>n) {
return rs;
}
Queue<Integer> queue = new PriorityQueue(k, Collections.reverseOrder());
for(int i=0;i<n;i++) {
if(queue.size()<k) {
queue.add(input[i]);
}else {
if(input[i]<queue.peek()) {
queue.remove();
queue.add(input[i]);
}
}
}
while(!queue.isEmpty()) {
rs.add(queue.remove());
}
return rs;
}
/*
* HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,
* 当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},
* 连续子向量的最大和为8(从第0个开始,到第3个为止)
* 。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
*/
/*
* 方法一使用dp
* dp[n]表示以第n个数结尾的最大子序列和
* dp[n]=max(0,dp[n-1])+num[n]
* 直接使用num[n]来保存dp[n]
*/
public int FindGreatestSumOfSubArray(int[] array) {
int max=Integer.MIN_VALUE;
if(array.length==1) {
return array[0];
}
for(int i=1;i<array.length;i++) {
array[i]=Math.max(0, array[i-1])+array[i];
if(array[i]>max) {
max=array[i];
}
}
return max;
}
/*
* 方法二,用sum[i]表示前i个数的和
* 则以j结尾的最大子序列为sum[j]-sum[m],设m为开始的位置,则sum[m]肯定是0-m中sum最小的
* 则只需要记录前面最小和即可
*/
public int FindGreatestSumOfSubArray2(int[] array) {
int max=Integer.MIN_VALUE;
if(array.length==1) {
return array[0];
}
int min =0;
for(int i=1;i<array.length;i++) {
array[i]=array[i]+array[i-1];
if((array[i]-min)>max) {
max=array[i]-min;
}
//这里两个if顺序注意
if(array[i]<min) {
min=array[i];
}
}
return max;
}
/*
* 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有
* 1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了
* 。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
*/
public static void main(String args[]) {
int []in= {4,5,1,6,2,7,3,8};
System.out.println(NumberOf1Between1AndN_Solution(1));
}
public static int NumberOf1Between1AndN_Solution(int n) {
int count = 0;//1的个数
int i = 1;//当前位
int current = 0,after = 0,before = 0;
while((n/i)!= 0){
current = (n/i)%10; //高位数字
before = n/(i*10); //当前位数字
after = n-(n/i)*i; //低位数字
//如果为0,出现1的次数由高位决定,等于高位数字 * 当前位数
if (current == 0)
count += before*i;
//如果为1,出现1的次数由高位和低位决定,高位*当前位+低位+1
else if(current == 1)
count += before * i + after + 1;
//如果大于1,出现1的次数由高位决定,//(高位数字+1)* 当前位数
else{
count += (before + 1) * i;
}
//前移一位
i = i*10;
}
return count;
}
/*
* 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},
* 则打印出这三个数字能排成的最小数字为321323。
*/
public String PrintMinNumber(int [] numbers) {
String rs="";
ArrayList<Integer>num=new ArrayList();
for(int i=0;i<numbers.length;i++) {
num.add(numbers[i]);
}
/*构建比较器进行排序
* 小的放在前面
*/
num.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
String s1=""+o1+o2;
String s2=""+o2+o1;
return s1.compareTo(s2);
}
});
for(int a:num) {
rs+=a;
}
return rs;
}
/*
* 把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。
* 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
*/
public int GetUglyNumber_Solution(int index) {
if(index==0) {
return 0;
}
int []num=new int[index];
num[0]=1;
int p2=0;
int p3=0;
int p5=0;
for(int i=1;i<index;i++) {
num[i]=Math.min(num[p2]*2,Math.min(num[p3]*3, num[p5]*5));
if(num[i]==num[p2]*2)p2++;
if(num[i]==num[p3]*3)p3++;
if(num[i]==num[p5]*5)p5++;
}
return num[index-1];
}
/*
* 在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,
* 并返回它的位置, 如果没有则返回 -1(需要区分大小写).
*/
public int FirstNotRepeatingChar(String str) {
char []s=str.toCharArray();
int []isOne=new int[255];
//0表示未访问,1表示前面已存在,2表示存在超过一个
for(int i=0;i<str.length();i++) {
if(isOne[s[i]]==0) {
isOne[s[i]]=1;
}else {
isOne[s[i]]=2;
}
}
for(int i=0;i<str.length();i++) {
if(isOne[s[i]]==1) {
return i;
}
}
return -1;
}
/*
* 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
* 输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
*/
public int InversePairs(int [] array) {
}
/*
* 输入两个链表,找出它们的第一个公共结点,两个链表是交叉挨一起的
* 先计算长度,让长的链表先走长度之差步,链表就能同时到达第一个公共节点
*/
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
}
/*
* 统计一个数字在排序数组中出现的次数。
*/
//由于排序好了 使用二分查找第一个与最后一个k出现的位置
public int GetNumberOfK(int [] array , int k) {
if(array.length==0){
return 0;
}
int index1=getFirstIndex(array, k,0,array.length-1);
int index2=getLastIndex(array, k,0,array.length-1);
if(index1==-1) {
return 0;
}
return index2-index1+1;
}
private int getLastIndex(int[] a, int k, int i, int j) {
// TODO Auto-generated method stub
if(i==j) {
if(a[i]==k) {
return i;
}else {
return -1;
}
}
int mid=(i+j)/2;
if(a[mid]<k) {
return getFirstIndex(a,k,mid+1,j);
}else if(a[mid]>k) {
return getFirstIndex(a,k,i,mid);
}else {
while(mid<=j&&a[mid]==k) {
mid++;
}
return mid-1;
}
}
private int getFirstIndex(int[] a, int k, int i, int j) {
// TODO Auto-generated method stub
if(i==j) {
if(a[i]==k) {
return i;
}else {
return -1;
}
}
int mid=(i+j)/2;
if(a[mid]<k) {
return getFirstIndex(a,k,mid+1,j);
}else if(a[mid]>k) {
return getFirstIndex(a,k,i,mid);
}else {
while(mid>=i&&a[mid]==k) {
mid--;
}
return mid+1;
}
}
/*
*
* 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
*/
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;
}
/*
* 输入一棵二叉树,判断该二叉树是否是平衡二叉树。
* 平衡二叉树左右子树高度不超过1
*/
public boolean IsBalanced_Solution(TreeNode root) {
if(root==null) {
return true;
}
if(Math.abs(TreeDepth(root.left)-TreeDepth(root.right))<=1) {
return IsBalanced_Solution(root.left)&&IsBalanced_Solution(root.right);
}else {
return false;
}
}
/*
* 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
*/
//使用异或首先求出两个数的异或c,在找出c位中中随便出现为1的位置,通过该位置为将原来数组分成两部分,每部分分别异或得到这两个数
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
int c=0;
for(int i=0;i<array.length;i++) {
c=c^array[i];
}
int flag=1;
while(flag!=(c&flag)) {
flag=flag*2;
}
num1[0]=0;
num2[0]=0;
for(int i=0;i<array.length;i++) {
if(flag==(flag&array[i])) {
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!
*
* 输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
*/
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> >rs=new ArrayList();
ArrayList<Integer> r=new ArrayList();
int i=0;//i表示当前最大的数
int j=0;//j表示最小的数的前一个数
int s=0;
while(i<sum/2+2) {
if(s<sum) {
i++;
}else if(s>sum) {
j++;
s=s-j;
r.remove(r.indexOf(j));
continue;
}else if(s==sum) {
if(r.size()>=2) {
rs.add(new ArrayList(r));
}
i++;
}
s=s+i;
r.add(i);
}
return rs;
}
/*
* 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
*/
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> rs=new ArrayList();
int i=0;
int j=array.length-1;
while(i<j) {
if(array[i]+array[j]<sum) {
i++;
}else if(array[i]+array[j]==sum) {
rs.add(array[i]);
rs.add(array[j]);
break;
}else {
j--;
}
}
return rs;
}
/*
* 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,
* 就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S
* ,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”
* ,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
*/
public String LeftRotateString(String str,int n) {
if(n>str.length())return str;
return str.substring(n, str.length())+str.substring(0, n);
}
/*
* 牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。
* 同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。
* 例如,“student. a am I”。
* 后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。
* Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
*/
public String ReverseSentence(String str) {
if(str=="") {
return str;
}
String []rs=str.split(" ");
String r="";
for(int i=rs.length-1;i>0;i--) {
r=r+rs[i]+" ";
}
r=r+rs[0];
return r;
}
}
未完待续