剑指offer(java版)
Posted 青春是一场不悔的旅行
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指offer(java版)相关的知识,希望对你有一定的参考价值。
之前看的剑指offer是c++版的,因此自己用java语言实现了一遍,有些代码借鉴了网上大佬们的,有不足之处请指出来。
1.二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
public class Solution { public boolean Find(int target, int [][] array) { int row = 0; int column = array[0].length-1; while(row<array.length && column>=0){ if(target == array[row][column]){ return true; } else if(target > array[row][column]){ row++; } else{ column--; } } return false; } } |
2.替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
public class Solution { public String replaceSpace(StringBuffer str) {
if(str==null && str.length()<=0){ return null; } StringBuffer newString = new StringBuffer(); for(int i=0;i<str.length();i++){ if(str.charAt(i)==‘ ‘){ newString.append(‘%‘); newString.append(‘2‘); newString.append(‘0‘); } else{ newString.append(str.charAt(i)); } } return newString.toString();
} }
|
3.从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
import java.util.ArrayList; import java.util.Stack; public class Solution { public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { Stack<ListNode> stack = new Stack<ListNode>(); while(listNode!=null){ stack.push(listNode); listNode = listNode.next; } ArrayList<Integer> list = new ArrayList<Integer>(); while(!stack.empty()){ list.add(stack.pop().val); } return list; } } |
4.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
import java.util.HashMap; public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { if(pre==null && in==null){ return null; } HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); for(int i=0;i<in.length;i++){ map.put(in[i], i); }
TreeNode root = ConstructCore(pre,0,pre.length-1,in,0,in.length-1,map);
return root; }
public TreeNode ConstructCore(int[] pre,int pstart,int pend,int[] in, int istart,int iend,HashMap<Integer, Integer> map){ if(pstart > pend){ return null; } //根据前序遍历获取根节点 int val = pre[pstart]; TreeNode root = new TreeNode(val); //根据中序遍历获取左右子树 int index = map.get(val); root.left = ConstructCore(pre,pstart+1,pstart+index-istart,in,istart,index-1,map); root.right = ConstructCore(pre,pstart+index-istart+1,pend,in,index+1,iend,map); return root; } } |
5.用两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
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.isEmpty()){ throw new RuntimeException("Queue is empty!"); } else if(stack2.empty()){ while(!stack1.empty()){ stack2.push(stack1.pop()); } } return stack2.pop(); } } |
6.旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if(array==null && array.length<=0){ return 0; } int index1 = 0; int index2 = array.length-1; int midIndex = index1; while(array[index1]>=array[index2]){ if(index2-index1==1){ midIndex = index2; break; } midIndex = (index1+index2)/2; if(array[midIndex]>=array[index1]){ index1 = midIndex; } else if(array[midIndex]<=array[index2]){ index2 = midIndex; } else if(array[index1]==array[index2]&&array[index1]==array[midIndex]){ return MinInOrder(array,index1,index2); } } return array[midIndex];
} public int MinInOrder(int [] array,int index1,int index2){ int result = array[index1]; for(int i=index1+1;i<=index2;i++){ if(result>array[i]){ result = array[i]; } } return result; } } |
7.斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39
public class Solution { public int Fibonacci(int n) { if(n<=1){ return n; } int finOne = 0; int finTwo = 1; int fin = 0; for(int i=2;i<=n;i++){ fin = finOne+finTwo; finOne = finTwo; finTwo = fin; }
return fin; } } |
8.跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution { public int JumpFloor(int target) { if(target<=2){ return target; } int finOne = 1; int finTwo = 2; int fin = 0; for(int i=3;i<=target;i++){ fin = finOne+finTwo; finOne = finTwo; finTwo = fin; }
return fin;
} } |
9.变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
public class Solution { public int JumpFloorII(int target) { if(target==1){ return 1; } int num = 1; int result = 0; for(int i=2;i<=target;i++){ result = 2*num; num = result; } return result; } } |
10.矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
public class Solution { public int RectCover(int target) { if(target<=2){ return target; } int finOne = 1; int finTwo = 2; int fin = 0; for(int i=3;i<=target;i++){ fin = finOne+finTwo; finOne = finTwo; finTwo = fin; }
return fin;
} } |
11.二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。
public class Solution { public int NumberOf1(int n) { int count = 0; while(n!=0){ count++; n = (n-1)&n; } return count;
} } |
12.数值的整数次方
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方.
public class Solution { boolean InvalidInput = false; public double Power(double base, int exponent) { if(base==0.0 && exponent<0){ InvalidInput = true; return 0.0; } int absexponent = exponent; if(exponent<0){ absexponent = (-1)*exponent; } double result = PowerWithExponent(base,absexponent); if(exponent<0){ result = 1.0/result; } return result; }
public double PowerWithExponent(double base, int exponent){ if(exponent==0){ return 1; } if(exponent==1){ return base; } double result = PowerWithExponent(base,exponent>>1); result = result*result; if((exponent & 0x1) == 1){ result = result*base; } return result; } } |
13.调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
public class Solution { //第一个思路:类似冒泡算法,前偶后奇数就交换: public static 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){ int temp = array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } } } |
import java.util.ArrayList; import java.util.List;
public class Solution { //第二个思路:以空间换时间: public static void reOrderArray(int [] array) { if(array==null && array.length==0){ return ; } List<Integer> odd = new ArrayList<Integer>(); List<Integer> even = new ArrayList<Integer>(); for(int i=0;i<array.length;i++){ int temp = array[i]; if((temp & 1)==1){ odd.add(temp); } else{ even.add(temp); } } int m = 0; for(int i=0;i<odd.size();i++){ array[m] = odd.get(i); m++; } for(int k=0;k<even.size();k++){ array[m] = even.get(k); m++; } } } |
14.链表中倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
public class Solution { public ListNode FindKthToTail(ListNode head,int k) { if(head==null || k<=0){ return null; } ListNode ahead = head; ListNode behind = null; for(int i=0;i<k-1;i++){ //如果k大于链表长度,返回空 if(ahead.next!=null){ ahead = ahead.next; } else{ return null; } } behind = head; while(ahead.next!=null){ ahead = ahead.next; behind = behind.next; } return behind; } } |
15.反转链表
输入一个链表,反转链表后,输出链表的所有元素.
public class Solution { public ListNode ReverseList(ListNode head) { if(head==null){ return null; } ListNode preNode = null; ListNode nextNode = null; ListNode currentNode = head; ListNode reverseHead = null; while(currentNode!=null){ nextNode = currentNode.next; if(nextNode == null){ reverseHead = currentNode; } currentNode.next = preNode; preNode = currentNode; currentNode = nextNode; }
return reverseHead;
} } |
16.合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则.
方法一: public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if(list1==null && list2==null){ return null; } if(list1==null){ return list2; } if(list2==null){ return list1; } ListNode head = null; if(list1.val<=list2.val){ head = list1; list1 = list1.next; } else{ head = list2; list2 = list2.next; } ListNode currentNode = head; while(list1!=null || list2!=null){ if(list1==null){ currentNode.next = list2; currentNode = currentNode.next; list2 = list2.next; } else if(list2==null){ currentNode.next = list1; currentNode = currentNode.next; list1 = list1.next; }
else if(list1!=null && list2!=null){ if(list1.val<=list2.val){ currentNode.next = list1; currentNode = currentNode.next; list1 = list1.next; } else{ currentNode.next = list2; currentNode = currentNode.next; list2 = list2.next; } } } return head; } } |
方法二:采用递归 public class Solution { public ListNode Merge(ListNode list1,ListNode list2) { if(list1==null && list2==null){ return null; } if(list1==null){ return list2; } if(list2==null){ return list1; } ListNode head = null; if(list1.val<=list2.val){ head = list1; head.next = Merge(list1.next,list2); } else{ head = list2; head.next = Merge(list1,list2.next); } return head;
} } |
17.树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
public class Solution { /** * 遍历树的根节点 */ public boolean HasSubtree(TreeNode root1,TreeNode root2) { boolean result = false; if(root1==null){ return false; } if(root2==null){ return false; } if(root1!=null && root2!=null){ if(root1.val==root2.val){ result = DoesTree1HaveTree2(root1,root2); } } if(!result){ result = HasSubtree(root1.left,root2); } if(!result){ result = HasSubtree(root1.right,root2); } return result; }
/** * 判断根节点下面的子树 */ public boolean DoesTree1HaveTree2(TreeNode root1,TreeNode root2){ if(root2 == null){ return true; } if(root1 == null){ return false; } if(root1.val!=root2.val){ return false; } return DoesTree1HaveTree2(root1.left,root2.left) && DoesTree1HaveTree2(root1.right,root2.right); } } |
18.二叉树的镜像
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
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; } 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); }
} } |
19.顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
public class Solution { public ArrayList<Integer> printMatrix(int[][] matrix) { ArrayList<Integer> arrayList = new ArrayList<Integer>(); if(matrix==null){ return null; } int rows = matrix.length; int columns = matrix[0].length; int start = 0; while(2*start<rows && 2*start<columns){ printMatrilCircle(arrayList,start,rows,columns,matrix); start++; } return arrayList; }
public void printMatrilCircle(ArrayList<Integer> arrayList,int start,int rows,int columns,int[][] matrix){ int endX = columns-1-start; int endY = rows-1-start; /** * 从左到右打印一行 */ for(int i=start;i<=endX;i++){ int num = matrix[start][i]; arrayList.add(num); }
/** * 从上到下打印(行数要大于等于两行)start<endY */ if(start<endY){ for(int i=start+1;i<=endY;i++){ int num = matrix[i][endX]; arrayList.add(num); } } /** * 从右往左打印一行(至少要有两行两列) */ if(start<endY && start<endX){ for(int i=endX-1;i>=start;i--){ int num = matrix[endY][i]; arrayList.add(num); } }
/** * 从下往上打印一列(至少要有三行两列) */ if(start<(endY-1) && start<endX){ for(int i=endY-1;i>start;i--){ int num = matrix[i][start]; arrayList.add(num); } }
} } |
20.包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
import java.util.Stack;
public class Solution {
Stack<Integer> stack = new Stack<Integer>(); Stack<Integer> stackMin = new Stack<Integer>(); public void push(int node) { stack.push(node); if(stackMin.isEmpty()){ stackMin.push(node); } else{ if(node<=stackMin.peek()){ stackMin.push(node); } else{ stackMin.push(stackMin.peek()); } } }
public void pop() { stack.pop(); stackMin.pop(); }
public int top() { return stack.peek(); }
public int min() { return stackMin.peek(); } } |
21.栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
import java.util.Stack;
public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { if(pushA==null && popA==null){ return false; } Stack<Integer> stack = new Stack<Integer>(); int j = 0; /** * 将压入序列放入数组中 */ for(int i=0;i<pushA.length;i++){ while(!stack.isEmpty() && stack.peek()==popA[j]){ stack.pop(); j++; } stack.push(pushA[i]); } while(j<popA.length){ if(stack.peek()==popA[j]){ stack.pop(); j++; } else{ return false; } } return true; } } |
22.从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印.
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> arrayList = new ArrayList<Integer>(); if(root==null){ return arrayList; } Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ TreeNode hQueue = queue.poll(); if(hQueue.left!=null){ queue.add(hQueue.left); } if(hQueue.right!=null){ queue.add(hQueue.right); } arrayList.add(hQueue.val); } return arrayList; } } |
23二叉搜索树的后序遍历序列
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
方法一: import java.util.ArrayList; public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { if(sequence==null || sequence.length<=0){ return false; } if(sequence.length==1){ return true; } int length = sequence.length; int root = sequence[length-1]; ArrayList<Integer> leftList = new ArrayList<Integer>(); ArrayList<Integer> rightList = new ArrayList<Integer>(); boolean left = true; boolean right = true; int k = 0; /** * 左子树 */ if(sequence[k]<root){ leftList.add(sequence[k]); k++; for(int i=k;i<length-1;i++){ if(sequence[i]<root){ leftList.add(sequence[i]); k++; } else{ break; } } } /** * 右子树 */ if(sequence[k]>root){ rightList.add(sequence[k]); k++; for(int i=k;i<length-1;i++){ if(sequence[i]>root){ rightList.add(sequence[i]); } else{ return false; } } } if(leftList.size()>0){ int[] leftArray = new int[leftList.size()]; for(int i=0;i<leftArray.length;i++){ leftArray[i] = leftList.get(i); } left = VerifySquenceOfBST(leftArray); } if(rightList.size()>0){ int[] rightArray = new int[rightList.size()]; for(int i=0;i<rightArray.length;i++){ rightArray[i] = rightList.get(i); } right = VerifySquenceOfBST(rightArray); }
return left&&right;
} } |
方法二: public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { int length = sequence.length; if(sequence==null || length<=0){ return false; } if(length==1){ return true; } int root = sequence[length-1]; int i=0; /** * 在二叉搜索树中左子树的节点小于根节点 */ for(;i<length-1;i++){ if(sequence[i]>root){ break; } } int leftLength = i; /** * 在二叉搜索树中右子树的节点大于根节点 */ for(int j=i;j<length-1;j++){ if(sequence[j]<root){ return false; } } //判断左子树是不是二叉搜索树 boolean left = true; if(leftLength>0){ int[] leftSequence = new int[leftLength]; for(int k=0;k<leftLength-1;k++){ leftSequence[k] = sequence[k]; } left = VerifySquenceOfBST(leftSequence); } //判断右子树是不是二叉树 boolean right = true; if(leftLength<length-1){ int[] rightSequence = new int[length-leftLength-1]; for(int k=0;k<length-leftLength-1;k++){ rightSequence[k] = sequence[k]; } right = VerifySquenceOfBST(rightSequence); }
return left&&right; } } |
24.二叉树中和为某一值的路径
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) { ArrayList<ArrayList<Integer>> pathList = new ArrayList<ArrayList<Integer>>(); if(root==null){ return pathList; } ArrayList<Integer> path = new ArrayList<Integer>(); Integer currentSum = 0; find(root,target,pathList,currentSum,path); return pathList;
}
public void find(TreeNode root,int target, ArrayList<ArrayList<Integer>> pathList,Integer currentSum,ArrayList<Integer> path){ currentSum = currentSum + root.val; path.add(root.val); boolean isLeaf = false; if(root.left==null && root.right==null){ isLeaf = true; } if(currentSum==target && isLeaf){ ArrayList<Integer> list = new ArrayList<Integer> (); for(int i=0;i<path.size();i++){ list.add(path.get(i)); } pathList.add(list);
}
if(root.left!=null){ find(root.left,target,pathList,currentSum,path); } if(root.right!=null){ find(root.right,target,pathList,currentSum,path); } path.remove(path.size()-1); } } |
25.复杂链表的复制
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
public class Solution { public RandomListNode Clone(RandomListNode pHead) { RandomListNode p=pHead; RandomListNode t=pHead; /** * 复制原始链表的任意节点n */ while(p!=null){ RandomListNode q=new RandomListNode(p.label); q.next=p.next; p.next=q; p=q.next; } /** * 复制指向的任意节点 */ while(t!=null){ RandomListNode q=t.next; if(t.random!=null) q.random=t.random.next; t=q.next;
} /** * 分离链表 */ RandomListNode rNode = pHead; RandomListNode rCloneNode = null; RandomListNode rCloneHead = null;
if(rNode!=null){ rCloneHead = rCloneNode = rNode.next; rNode.next = rCloneNode.next; rNode = rNode.next; } while(rNode!=null){ rCloneNode.next = rNode.next; rCloneNode = rCloneNode.next; rNode.next = rCloneNode.next; rNode = rNode.next; }
return rCloneHead;
} }
|
26.二叉搜索树与双向链表
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
public class Solution { public TreeNode pLastNodeInList = null; public TreeNode Convert(TreeNode pRootOfTree) { ConvertNode(pRootOfTree); TreeNode pHeadNode = pLastNodeInList; //得到头结点 while(pHeadNode!=null && pHeadNode.left!=null){ pHeadNode = pHeadNode.left; } return pHeadNode;
} public void ConvertNode(TreeNode pRootOfTree){ if(pRootOfTree==null){ return; } TreeNode pCurrentNode = pRootOfTree; //遍历左子树 if(pCurrentNode.left!=null){ ConvertNode(pCurrentNode.left); } pCurrentNode.left = pLastNodeInList; if(pLastNodeInList!=null){ pLastNodeInList.right = pCurrentNode; } pLastNodeInList = pCurrentNode; System.out.println(pLastNodeInList.val); //遍历右子树 if(pCurrentNode.right!=null){ ConvertNode(pCurrentNode.right); } } } |
27.字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public class Solution { public ArrayList<String> Permutation(String str) { ArrayList<String> list = new ArrayList<String>(); if(str==null || str.length()==0){ return list; } char[] cs = str.toCharArray(); Arrays.sort(cs); Permutation(str.toCharArray(),0,list); Collections.sort(list); return list;
} public void Permutation(char[] str,int begin,ArrayList<String> list){ if(begin==str.length-1){ list.add(String.valueOf(str)); return; } for(int index=begin;index<str.length;index++){ if(index!=begin&&(str[index-1]==str[index] ||str[index]==str[begin])){ continue; } //第一个字符和剩下的字符进行交换 char temp = str[index]; str[index] = str[begin]; str[begin] = temp;
Permutation(str,begin+1,list);
//复位 temp = str[index]; str[index] = str[begin]; str[begin] = temp; } } }
|
28.数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
方法一:利用快排思想 public class Solution { public int MoreThanHalfNum_Solution(int [] array) { if(array==null || array.length==0){ return 0; } int middle = array.length/2; int start = 0; int end = array.length-1; int index = sort(array,start,end); while(index!=middle){ if(index > middle){ end = index - 1; index = sort(array, start, end); } else{ start = index+1; index = sort(array, start, end); } } int result = array[middle]; int time = 0; for(int i=0;i<array.length;i++){ if(array[i]==result){ time++; } if(time*2 > array.length){ return result; } } if(time*2 <= array.length){ return 0; } return 0; }
public int sort(int[] array,int start,int end){ int i = start; int j = end; int current = array[i]; while(i < j){ //从右往左扫描 while(i < j && array[j]>=current){ j--; } if(i < j){ array[i] = array[j]; i++; } //从左往右扫描 while(i < j && array[i]<=current){ i++; } if(i < j){ array[j] = array[i]; j--; } } array[i] = current; return i; } } |
方法二: public class Solution { public int MoreThanHalfNum_Solution(int [] array) { //遍历数组是保存两个值:一个是数组中的数字,另一个是次数。 if(array==null || array.length==0){ return 0; } int result = array[0]; int time = 1; for(int i=1;i<array.length;i++){ //次数为0时,保存当前数字,并把次数置为1 if(time==0){ result = array[i]; time = 1; } //当前数字和之前保存的数字相同时,次数加1 else if(result == array[i]){ time++; } //当前数字和之前保存的数字不相同时,次数减1 else { time--; } } int count = 0; for(int i=0;i<array.length;i++){ if(array[i] == result){ count++; } if(count*2 > array.length){ return result; } } return 0;
} } |
29.最小的K个数
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
方法一:利用快排思想 public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> list = new ArrayList<Integer>(); if(input==null || input.length==0 || k==0 || k > input.length){ return list; } int start = 0; int end = input.length-1; int index = sort(input,start,end); while(index!=(k-1)){ if(index > (k-1)){ end = index-1; index = sort(input,start,end); } else{ start = index+1; index = sort(input,start,end); } } for(int i=0;i<=index;i++){ list.add(input[i]); }
return list;
}
public int sort(int [] input,int start,int end){ int i = start; int j = end; int current = input[i]; while(i < j){ while(i < j && input[j]>=current){ j--; } if(i < j){ input[i] = input[j]; i++; } while(i < j && input[i] <= current){ i++; } if(i < j){ input[j] = input[i]; j--; } } input[i] = current; return i; } } |
方法二:利用红黑树
|
30.连续子数组的最大和
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)
public class Solution { boolean invalidInput = false; public int FindGreatestSumOfSubArray(int[] array) { if(array==null || array.length==0){ invalidInput = false; return 0; } int curSum = 0; int greatSum = 0x80000000; //32位的int,正数的范围是(0,0x7FFFFFFF),负数(0x80000000,0xFFFFFFFF) for(int i=0;i<array.length;i++){ if(curSum<0){ curSum = array[i]; } else{ curSum = curSum + array[i]; } if(curSum > greatSum){ greatSum = curSum; } } return greatSum; } } |
31.整数中1出现的次数(从1到n整数中1出现的
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
public class Solution { public int NumberOf1Between1AndN_Solution(int n) { if(n<=0){ return 0; } String string = Integer.toString(n); char[] cs = string.toCharArray(); return NumberOf1(cs,0,cs.length);
}
public int NumberOf1(char[] cs,int start,int length){ if(cs==null || length==0 || cs[0]<‘0‘ || cs[0]>‘9‘){ return 0; } int first = cs[start]-‘0‘; if(length==1 && first==0){ return 0; } if(length==1 && first>0){ return 1; } //求最高位为1的数目 int numFirstDigit = 0; if(first>1){ numFirstDigit = PowerBase10(length-1); } else if(first==1) { //得到除开最高位的数字 char[] s = new char[length-1]; int j=0; for(int i=start+1;i<cs.length;i++){ s[j] = cs[i]; j++; } numFirstDigit = Integer.parseInt(String.valueOf(s))+1; } //除开最高位的数字 int numOtherDigits = first * (length-1) * PowerBase10(length-2);
//1-1345中的数字,递归 int numRecursive = NumberOf1(cs,start+1,length-1); return numFirstDigit + numOtherDigits + numRecursive; }
public int PowerBase10(int n){ int result = 1; for(int i=0;i<n;i++){ result = result*10; } return result; } } |
32.把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
public class Solution { public String PrintMinNumber(int [] numbers) { if(numbers==null && numbers.length==0){ return null; } int length = numbers.length; //采用冒泡排序 for(int i=0;i<length-1;i++){ for(int j=length-1;j>i;j--){ int m = numbers[j-1]; int n = numbers[j]; String mn = Integer.toString(m).concat(Integer.toString(n)); String nm = Integer.toString(n).concat(Integer.toString(m)); boolean isBig = compare(mn,nm); if(isBig){ int temp = numbers[j-1]; numbers[j-1] = numbers[j]; numbers[j] = temp; } } } StringBuilder sb = new StringBuilder(); for(int i=0;i<numbers.length;i++){ sb.append(Integer.toString(numbers[i])); } return sb.toString(); } /** * 返回true:m>n 返回false: m<n */ public boolean compare(String mn,String nm){ int i = mn.compareTo(nm); if(i>=0){ return true; } else { return false; } } } |
33.丑数
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
public class Solution { public int GetUglyNumber_Solution(int index) { if(index<=0){ return 0; } int[] num = new int[index]; num[0] = 1; int next = 1; int p2 = 0; //在丑数基础上乘以2 int p3 = 0; //在丑数基础上乘以3 int p5 = 0; //在丑数基础上乘以5 while(next<index){ int min = getMin(num[p2]*2, num[p3]*3, num[p5]*5); num[next] = min; while(num[p2]*2<=num[next]){ p2++; } while(num[p3]*3<=num[next]){ p3++; } while(num[p5]*5<=num[next]){ p5++; } next++; } int ugly = num[next-1]; return ugly; }
public int getMin(int number1,int number2,int number3){ int number = number1<number2 ? number1:number2; return number<number3 ? number:number3; } } |
34.第一个只出现一次的字符
在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置
import java.util.HashMap; import java.util.Map; public class Solution { public int FirstNotRepeatingChar(String str) { if(str==null || str.length()==0){ return -1; } Map<Character, Integer> map = new HashMap<Character, Integer>(); for(int i=0;i<str.length();i++){ if(map.containsKey(str.charAt(i))){ int time = map.get(str.charAt(i)); time++; map.put(str.charAt(i), time); } else { map.put(str.charAt(i), 1); } } for(int i=0;i<str.length();i++){ int time = map.get(str.charAt(i)); if(time==1){ return i; } } return -1; } } |
35.归并排序
public class Solution { public void Merge(int[] array,int start,int middle,int end){ int leftLength = middle-start+1; int rightLength = end-middle; int[] L = new int[middle-start+1]; int[] R = new int[end-middle]; int lIndex; int rIndex; int k; for(k=start,lIndex=0;lIndex<leftLength;k++){ L[lIndex] = array[k]; lIndex++; } for(k=middle+1,rIndex=0;rIndex<rightLength;k++){ R[rIndex] = array[k]; rIndex++; } for(k=start,lIndex=0,rIndex=0;lIndex<leftLength && rIndex<rightLength;k++){ if(L[lIndex]<=R[rIndex]){ array[k] = L[lIndex]; lIndex++; } else { array[k] = R[rIndex]; rIndex++; } } if(lIndex<leftLength){ for(int i=lIndex;i<leftLength;i++,k++){ array[k] = L[i]; }
} if(rIndex<rightLength){ for(int i=rIndex;i<rightLength;i++,k++){ array[k] = R[i]; }
} }
public void MergeSort(int[] array,int start,int end){ if(start<end){ int middle = (start+end)/2; MergeSort(array,start,middle); MergeSort(array, middle+1, end); Merge(array, start, middle, end); } } @Test public void mytest(){ int[] array = {9,8,7,5,6,7,8,7,6,5,4,3,2,1,0,9,8,7,8}; int length = array.length; MergeSort(array,0,length-1); for(int i=0;i<length;i++){ System.out.print(array[i]+" "); } } } |
36.数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
public class Solution { public int InversePairs(int [] array) { if(array==null || array.length==0){ return -1; } int[] copy = new int[array.length]; for(int i=0;i<array.length;i++){ copy[i] = array[i]; } int count = InversePairsCore(copy,array,0,array.length-1); return count; } public int InversePairsCore(int[] copy,int[] array,int start,int end){ if(start == end){ copy[start] = array[start]; return 0; } int middle = (start+end)/2; int left = InversePairsCore(array,copy,start,middle)%1000000007; int right = InversePairsCore(array,copy,middle+1,end)%1000000007; //指向前半段的最后一个数字下标 int i = middle; //指向后半段最后一个数字的下标 int j = end; int indexCopy = end; int count = 0; while(i>=start && j>=middle+1){ if(array[i] > array[j]){ count = count + j - middle; copy[indexCopy] = array[i]; indexCopy--; i--; if(count>=1000000007)//数值过大求余 { count%=1000000007; } } else { copy[indexCopy] = array[j]; indexCopy--; j--; } } for(;i>=start;i--){ copy[indexCopy] = array[i]; indexCopy--; } for(;j>=middle+1;j--){ copy[indexCopy] = array[j]; indexCopy--; } return (left+right+count)%1000000007;
} } |
37.两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
public class Solution { public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { if(pHead1==null || pHead2==null){ return null; } int length1 = getLength(pHead1); int length2 = getLength(pHead2); int length; ListNode listNodeLong = null; ListNode listNodeShort = null; if(length1 > length2){ length = length1 - length2; listNodeLong = pHead1; listNodeShort = pHead2; } else { length = length2 - length1; listNodeLong = pHead2; listNodeShort = pHead1; } for(int i=0;i<length;i++){ listNodeLong = listNodeLong.next; } while(listNodeLong!=null && listNodeShort!=null && listNodeLong!=listNodeShort){ listNodeLong = listNodeLong.next; listNodeShort = listNodeShort.next; }
ListNode commonNode = listNodeLong; return commonNode; } public int getLength(ListNode head){ if(head==null){ return 0; } int count = 0; while(head!=null){ count++; head = head.next; } return count; } } |
38.数字在排序数组中出现的次数
统计一个数字在排序数组中出现的次数。
public class Solution { public int GetNumberOfK(int [] array , int k) { if(array==null || array.length==0){ return 0; } int number = 0; int first = getFirstK(array, k, 0, array.length-1); int last = getLastK(array, k, 0, array.length-1); if(first>=0 && last>=0){ number = last - first +1; } return number; } public int getLastK(int[] array,int k,int start,int end){ if(start > end){ return -1; } int middle = (end+start)/2; if(array[middle] > k){ end = end-1; } else if(array[middle]<k) { start = start+1; } else{ if((middle<array.length-1 && array[middle+1]!=k) || middle==array.length-1){ return middle; } else { start = middle+1; } } return getLastK(array,k,start,end);
} public int getFirstK(int[] array,int k,int start,int end){ if(start>end){ return -1; } int middle = (end+start)/2; if(array[middle]>k){ end = middle-1; } else if(array[middle]<k){ start = middle+1; } else { if((middle>0 && array[middle-1]!=k) || middle==0){ return middle; } else{ end = middle-1; } } return getFirstK(array,k,start,end); } } |
39.二叉树的深度
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
public class Solution { public int TreeDepth(TreeNode root) { if(root == null){ return 0; } int left = TreeDepth(root.left); int right = TreeDepth(root.right);
int depth = left>right ? (left+1) : (right+1); return depth; } } |
40.平衡二叉树
输入一棵二叉树,判断该二叉树是否是平衡二叉树
方法一: public class Solution { public int TreeDepth(TreeNode root) { if(root == null){ return 0; } int left = TreeDepth(root.left); int right = TreeDepth(root.right);
int depth = left>right ? (left+1) : (right+1); return depth; } public boolean IsBalanced_Solution(TreeNode root) { if(root == null){ return true; } int left = TreeDepth(root.left); int right = TreeDepth(root.right); int differ = left-right; if(differ>1 || differ<-1){ return false; } return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right); } } |
方法二:采用后序遍历 public class Solution { private boolean isBalanced = true; public boolean IsBalanced_Solution(TreeNode root) { getDepth(root); return isBalanced;
} public int getDepth(TreeNode root){ if(root==null){ return 0; } int left = getDepth(root.left); int right = getDepth(root.right); int differ = left - right; if(differ>1 || differ<-1){ isBalanced = false; } return left > right ? (left+1) : (right+1); } } |
41.数组中只出现一次的数字
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
public class Solution { public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { if(array==null || array.length==0){ return; } int resultOR = 0; // 异或结果 for(int i=0;i<array.length;i++){ resultOR = resultOR ^ array[i]; } int indexOf1 = findFirst1(resultOR); //找到第一个为1的位置 num1[0] = 0; num2[0] = 0; for(int j=0;j<array.length;j++){ //根据第一个为1的位置,分离数组 if(isBit1(array[j],indexOf1)){ num1[0] = num1[0] ^ array[j]; } else{ num2[0] = num2[0] ^ array[j]; } }
} //找到第一个出现1的位置 public int findFirst1(int num){ int indexBit = 0; while(((num & 1)==0) && (indexBit<32)){ num = num >> 1; // 右移 indexBit++; } return indexBit; } //判断第n位是否是1 public boolean isBit1(int num,int indexBit){ num = num >> indexBit; if(((num&1)==0)){ return false; } return true; } } |
43.和为S的连续正数序列
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
import java.util.ArrayList; public class Solution { public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer> > lists = new ArrayList<ArrayList<Integer> >(); if(sum < 3){ return lists; } int small = 1; int big = 2; int curSum = small + big; int middle = (sum+1)/2; while(small < middle){ if(curSum == sum){ lists.add(getNum(small, big)); } while(curSum>sum && small<middle){ curSum = curSum -small; small = small+1; if(curSum==sum){ lists.add(getNum(small, big)); } } big++; curSum = curSum+big; } return lists;
} public ArrayList<Integer> getNum(int small,int big){ ArrayList<Integer> list = new ArrayList<Integer>(); for(int i=small;i<=big;i++){ list.add(i); } return list; } } |
44.和为S的两个数字
输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
public class Solution { public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) { ArrayList<Integer> list = new ArrayList<Integer>(); if(array==null || array.length==0){ return list; } int small = 0; int big = array.length-1; int curSum; while(small<big){ curSum = array[small]+array[big]; if(curSum<sum){ small++; } else if(curSum>sum){ big--; } else { list.add(array[small]); list.add(array[big]); return list; } } return list; } } |
45.左旋转字符串
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
public class Solution { public String LeftRotateString(String str,int n) { if(str==null){ return null; } if(n>=str.length()){ return str; } char[] cs = str.toCharArray(); //全部翻转 Reverse(cs,0,cs.length-1); //翻转前部分 Reverse(cs,0,cs.length-1-n); //翻转后部分 Reverse(cs,cs.length-n,cs.length-1); return String.valueOf(cs);
} public void Reverse(char[] str,int begin,int end){ int i=begin; int j = end; char temp; while(i <= j){ temp = str[i]; str[i] = str[j]; str[j] = temp; i++; j--; } } } |
46.翻转单词顺序列
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
public class Solution { public String ReverseSentence(String str) { if(str==null){ return null; } char[] cs = str.toCharArray(); Reverse(cs,0,cs.length-1); int length = 0; int i; for(i=0;i<cs.length;i++){ if(cs[i]==‘ ‘){ int begin = i-length; int end = i-1; Reverse(cs,begin,end); length = 0; } else { length++; } } //最后一个单词进行翻转 Reverse(cs,i-length,i-1);
return String.valueOf(cs);
} public void Reverse(char[] str,int begin,int end){ int i=begin; int j = end; char temp; while(i <= j){ temp = str[i]; str[i] = str[j]; str[j] = temp; i++; j--; } } } |
47.扑克牌顺子
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。
import java.util.Arrays; public class Solution { public boolean isContinuous(int [] numbers) { if(numbers==null || numbers.length<5){ return false; } Arrays.sort(numbers); int count1 = 0; // 记录有多少个大小王 int count2 = 0; //记录有多少个空缺 int i=0; while(numbers[i]==0){ i++; count1++; } int lastNum = numbers[i]; for(i=i+1;i<numbers.length;i++){ if(numbers[i]==0){ count1++; } else { //出现了对子,不可能是连子 if(numbers[i] == lastNum){ return false; } else if(numbers[i] != lastNum+1){ int differ = numbers[i] - lastNum -1; count2 = count2+differ; } lastNum = numbers[i]; } } if(count2<=count1){ return true; } return false; } } |
48.孩子们的游戏(圆圈中最后剩下的数)
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
方法一:用环形链表模拟圆圈 public class Solution { public int LastRemaining_Solution(int n, int m) { if(n==0 || m==0){ return -1; } ListNode head = new ListNode(0); ListNode curNode = head; for(int i=1;i<n;i++){ ListNode node = new ListNode(i); curNode.next = node; curNode = curNode.next; } curNode.next = head; ListNode last = curNode; int count = 1; while(last!=head){ if(count<m){ head = head.next; last = last.next; count++; } else if(count==m){
last.next = head.next; head = head.next; count = 1; }
} return head.val; } |
public class Solution { public int LastRemaining_Solution(int n, int m) { if(n<1 || m<1){ return -1; } int last = 0; for(int i=2;i<=n;i++){ last = (last+m) % i; } return last; } } |
49.求1+2+3+...+n
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
public class Solution { public int Sum_Solution(int n) { int sum=n; boolean flag = n>0 && (sum=sum+Sum_Solution(n-1))>0; return sum; } } |
50.不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
public class Solution { public int Add(int num1,int num2) { int sum; int carry; do{ sum = num1 ^ num2; carry = (num1 & num2) << 1; //进位 num1 = sum; num2 = carry; }while(carry!=0); return sum; } } |
50.把字符串转换成整数
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
public class Solution { public int StrToInt(String str) { if(str.equals("") ||str.length()==0){ return 0; } char[] cs = str.toCharArray(); int minus = 1; if(cs[0]==‘-‘){ minus = -1; } int i=0; if(cs[i]==‘-‘){ if(cs.length==1){ return 0; } minus = -1; i++; } if(cs[i]==‘+‘){ if(cs.length==1){ return 0; } minus = 1; i++; } int sum=0; int common = 1; for(int j=cs.length-1;j>=i;j--){ if(cs[j]>=‘0‘ && cs[j]<=‘9‘){ sum = sum + (cs[j]-‘0‘)*common; common = common*10; System.out.println(sum); } else{ return 0; } if(sum>0x7FFFFFFF || sum<0x80000000){ return 0; } } return sum*minus; } |
51.数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
public class Solution { public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers==null || length<=1){ return false; } int temp; for(int index=0;index<length-1;index++){ while(numbers[index]!=index){ if(numbers[numbers[index]]==numbers[index]){ duplication[0] = numbers[index]; return true; } temp = numbers[numbers[index]]; numbers[numbers[index]] = numbers[index]; numbers[index] = temp; } } return false; } } |
52.构建乘积数组
给定一个数组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 class Solution { public int[] multiply(int[] A) { if(A==null || A.length==0){ return null; } int length = A.length; int[] B = new int[length]; int[] C = new int[length]; int[] D = new int[length]; C[0] = 1; D[length-1] = 1; for(int i=1;i<length;i++){ C[i] = A[i-1] * C[i-1]; D[length-1-i] = A[length-i] * D[length-i]; } for(int i=0;i<length;i++){ B[i] = C[i] * D[i]; } return B; } } |
53.正则表达式匹配
请实现一个函数用来匹配包括‘.‘和‘*‘的正则表达式。模式中的字符‘.‘表示任意一个字符,而‘*‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
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 matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符 || matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符 || matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个 } 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; } } |
54.表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
public class Solution { public boolean isNumeric(char[] str) { String string = String.valueOf(str); return string.matches("[\+-]?[0-9]*(\.[0-9]*)?([eE][\+-]?[0-9]+)?"); } } |
55.字符流中第一个不重复的字符
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
public class Solution { StringBuilder str = new StringBuilder(); int[] hashtable = new int[256]; //一共有256个字符
public void Insert(char ch) { str.append(ch); //第一次读入 if(hashtable[ch]==0){ hashtable[ch]=1; } //不是第一次读入 else{ hashtable[ch] = hashtable[ch] +1; } }
public char FirstAppearingOnce() { char[] ch = str.toString().toCharArray(); for(int i=0;i<ch.length;i++){ if(hashtable[ch[i]]==1){ return ch[i]; } } return ‘#‘; } } |
56.链表中环的入口结点
一个链表中包含环,请找出该链表的环的入口结点。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) { if(pHead==null || pHead.next==null){ return null; } int count = isLoop(pHead); if(count == 0){ return null; } ListNode fast = pHead; ListNode slow = pHead; for(int i=0;i<count;i++){ fast = fast.next; } while(fast!=slow){ fast = fast.next; slow = slow.next; } return fast; } //判断是否存在环 public int isLoop(ListNode pHead){ ListNode fast = pHead.next.next; ListNode slow = pHead.next; while(fast!=slow){ //不存在环 if(fast==null || fast.next==null){ return 0; } fast = fast.next.next; slow = slow.next; } ListNode nextNode = slow.next; int count = 1; while(nextNode!=slow){ nextNode = nextNode.next; count++; } return count; } } |
57.删除链表中重复的结点
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
public class Solution { public ListNode deleteDuplication(ListNode pHead) { if(pHead==null){ return null; } if(pHead.next==null){ return pHead; } ListNode lastNode = null; ListNode curNode = pHead; int count = 0; while(curNode!=null && curNode.next!=null){ if(curNode.next.val != curNode.val){ if(count>0){ if(lastNode==null){ pHead = curNode.next; count = 0; } else{ lastNode.next = curNode.next; count = 0; } } else{ lastNode = curNode; } curNode = curNode.next;
} else{ curNode = curNode.next; count++; } } if(count>0){ if(lastNode==null){ return null; } lastNode.next = null; } return pHead; } } |
58.二叉树的下一个结点
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
public class Solution { public TreeLinkNode GetNext(TreeLinkNode pNode) { if(pNode==null){ return pNode; } if (pNode.right != null) { TreeLinkNode node = pNode.right; while (node.left != null) { node = node.left; } return node; } while(pNode.next!=null){ if(pNode.next.left==pNode){ return pNode.next; } pNode = pNode.next; }
return null; } } |
59.对称的二叉树
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
public class Solution { boolean isSymmetrical(TreeNode pRoot) { return isSymmetricalCore(pRoot,pRoot); } public boolean isSymmetricalCore(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 isSymmetricalCore(root1.left,root2.right) && isSymmetricalCore(root1.right,root2.left); } } |
60.按之字形顺序打印二叉树
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
import java.util.ArrayList; import java.util.Stack; public class Solution { public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); if (pRoot == null) { return lists; } Stack<TreeNode> leftToRight = new Stack<TreeNode>(); Stack<TreeNode> rightToLeft = new Stack<TreeNode>(); leftToRight.push(pRoot); ArrayList<Integer> root = new ArrayList<Integer>(); root.add(pRoot.val); lists.add(root); while ((leftToRight.isEmpty() && !rightToLeft.isEmpty()) || (!leftToRight.isEmpty() && rightToLeft.isEmpty())) {
if (!leftToRight.isEmpty()) { ArrayList<Integer> list1 = new ArrayList<Integer>(); while (!leftToRight.isEmpty()) { TreeNode curNode = leftToRight.pop(); if (curNode.right != null) { rightToLeft.push(curNode.right); list1.add(curNode.right.val); } if (curNode.left != null) { rightToLeft.push(curNode.left); list1.add(curNode.left.val); } } if(list1.size()==0){ return lists; } lists.add(list1); }
if (!rightToLeft.isEmpty()) { ArrayList<Integer> list2 = new ArrayList<Integer>(); while (!rightToLeft.isEmpty()) { TreeNode curNode = rightToLeft.pop(); if (curNode.left != null) { leftToRight.push(curNode.left); list2.add(curNode.left.val); } if (curNode.right != null) { leftToRight.push(curNode.right); list2.add(curNode.right.val); } } if(list2.size()==0){ return lists; } lists.add(list2); } } return lists; } } |
61.把二叉树打印成多行
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; public class Solution { ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); if(pRoot==null){ return lists; } Queue<TreeNode> queue1 = new LinkedList<TreeNode>(); TreeNode node = new TreeNode(0); queue1.add(pRoot); queue1.add(node); ArrayList<Integer> root = new ArrayList<Integer>(); lists.add(root); root.add(pRoot.val); while(!queue1.isEmpty()){ ArrayList<Integer> list = new ArrayList<Integer>();
while(queue1.peek()!=node){ TreeNode curNode = queue1.poll(); if(curNode.left!=null){ queue1.add(curNode.left); list.add(curNode.left.val); } if(curNode.right!=null){ queue1.add(curNode.right); list.add(curNode.right.val); } }
queue1.add(queue1.poll()); if(list.size()==0){ return lists; } lists.add(list); } return lists; } } |
62.序列化二叉树
请实现两个函数,分别用来序列化和反序列化二叉树
public class Solution { String Serialize(TreeNode root) { if (root == null) { return ""; } StringBuilder sb = new StringBuilder(); Serialize1(root, sb); return sb.toString(); }
void Serialize1(TreeNode root, StringBuilder sb) { if (root == null) { sb.append("#,"); return; } sb.append(root.val); sb.append(","); Serialize1(root.left, sb); Serialize1(root.right, sb); } int index=0; TreeNode Deserialize(String str) { if(str.length()==0){ return null; } String[] s = str.split(","); return Deserialize1(s); } TreeNode Deserialize1(String[] s){ if(s[index].equals("#")){ index++; return null; } int val = Integer.parseInt(s[index]); System.out.println(val); index++; TreeNode node = new TreeNode(val); node.left = Deserialize1(s); node.right = Deserialize1(s); return node; } } |
63.二叉搜索树的第k个结点
给定一颗二叉搜索树,请找出其中的第k大的结点。例如, 5 / 3 7 / / 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4。
public class Solution { private int count; TreeNode KthNode(TreeNode pRoot, int k) { if(pRoot==null || k==0){ return null; } count = k; return KthNodeCore(pRoot); }
TreeNode KthNodeCore(TreeNode pRoot){ TreeNode targe = null; if(pRoot.left!=null){ targe = KthNodeCore(pRoot.left); } if(count==1){ targe = pRoot; } count--; if(targe==null && pRoot.right!=null){ targe = KthNodeCore(pRoot.right); }
return targe; } } |
64.数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
import java.util.Comparator; import java.util.PriorityQueue; public class Solution { private int count = 0; private PriorityQueue<Integer> minHeap = new PriorityQueue<>(); private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15,new Comparator<Integer>() {
@Override public int compare(Integer o1, Integer o2) { // TODO Auto-generated method stub return o2 - o1; }
});
public void Insert(Integer num) { if (count %2 == 0) {//当数据总数为偶数时,新加入的元素,应当进入小根堆 //(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆) //1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素 maxHeap.offer(num); int filteredMaxNum = maxHeap.poll(); //2.筛选后的【大根堆中的最大元素】进入小根堆 minHeap.offer(filteredMaxNum); } else {//当数据总数为奇数时,新加入的元素,应当进入大根堆 //(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆) //1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素 minHeap.offer(num); int filteredMinNum = minHeap.poll(); //2.筛选后的【小根堆中的最小元素】进入大根堆 maxHeap.offer(filteredMinNum); } count++; }
public Double GetMedian() { if (count %2 == 0) { return new Double((minHeap.peek() + maxHeap.peek())) / 2; } else { return new Double(minHeap.peek()); } }
} |
65.滑动窗口的最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
import java.util.ArrayList; import java.util.LinkedList; public class Solution { public ArrayList<Integer> maxInWindows(int [] num, int size) { ArrayList<Integer> list = new ArrayList<Integer>(); if(num==null || num.length==0 || size>num.length || size==0){ return list; } LinkedList<Integer> queue = new LinkedList<>(); // 双端队列 for(int i=0;i<num.length;i++){
while(!queue.isEmpty() && num[queue.getLast()]<=num[i]){ queue.removeLast(); } queue.add(i); //移除无效的数字 if(queue.peek() == i-size){ queue.poll(); } if(i >= size-1){ list.add(num[queue.peek()]); } } return list; } } |
66.矩阵中的路径
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
public class Solution { int index=0; int m_row; int m_col; public boolean hasPath(char[] matrix, int rows, int cols, char[] str) { if(matrix==null || matrix.length==0 || matrix.length<rows*cols || str==null || str.length==0 || str.length>matrix.length){ return false; } if(rows<=0 || cols<=0){ return false; } m_row = rows; m_col = cols; int[] visited = new int[rows*cols]; boolean flag = false; for(int i=0;i<rows;i++){ for(int j=0;j<cols;j++){ flag = hasPathCore(matrix,i,j,str,visited); if(flag){ return true; } } } return false; } public boolean hasPathCore(char[] matrix, int row, int col, char[] str,int[] visited){ boolean flag = false; //当前格子的字符相等时的情况 if(row>=0 && row<m_row && col>=0 && col<m_col && visited[row*m_col+col]!=1 && matrix[row*m_col+col]==str[index] ){ index++; System.out.println("index:"+index+"-----matrix:"+matrix[row*m_col+col]); visited[row*m_col+col] = 1;
if(index==str.length){ return true; } flag = hasPathCore(matrix,row,col+1,str,visited) || hasPathCore(matrix,row+1,col,str,visited) || hasPathCore(matrix,row,col-1,str,visited) || hasPathCore(matrix,row-1,col,str,visited); if(!flag){ index--; visited[row*m_col+col] = 0; return false; } } return flag; } } |
67.机器人的运动范围
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
public class Solution { int m_rows; int m_cols; int m_threshold; public int movingCount(int threshold, int rows, int cols) { int[] visited = new int[rows*cols]; m_rows = rows; m_cols = cols; m_threshold = threshold; return movingCountCore(0,0,visited);
}
public int movingCountCore(int row,int col,int[] visited){ if(row<0 || row>=m_rows || col<0 || col>=m_cols){ return 0; } int index = row*m_cols+col; if(visited[index]==1 || !checkSum(row,col)){ return 0; } visited[index]=1; return 1+movingCountCore(row+1,col,visited)+ movingCountCore(row-1,col,visited)+ movingCountCore(row,col-1,visited)+ movingCountCore(row,col+1,visited); } public boolean checkSum(int row,int col){ int sum = 0; while(row!=0){ sum = sum + row%10; row = row/10; } while(col!=0){ sum = sum + col%10; col = col/10; }
if(sum>m_threshold){ return false; } return true; } } |
以上是关于剑指offer(java版)的主要内容,如果未能解决你的问题,请参考以下文章
剑指 Offer(第 2 版)完整题解笔记 & C++代码实现(LeetCode版)