剑指offer
第三章 高质量代码
面试题16 求base的exp次方
处理边界样例,负样例
- base = 0,exp >0,<0,=0三种情况
- base != 0 ,exp >=0,<0 两种情况
package nowcoder;
import org.junit.Test;
public class offer_16 {
boolean invalid = false;
double power(double base,int exp) {
double eps = 1e-10;
invalid = false;
if( Math.abs(base-0) < eps && exp <= 0) {
invalid = true;
return 0;
}
double absExp = Math.abs(exp);
if(exp<0) return 1/getResult(base, exp);
else return getResult(base, exp);
}
double getResult(double base,int exp) {
if(exp == 1) return base;
else if((exp&0x1) == 0){
double t = getResult(base, exp>>1);
return t*t;
}
else {
double t = getResult(base, exp>>1);
return t*t*base;
}
}
@Test
public void test() {
System.out.println(power(4, 3));
System.out.println(invalid);
}
}
面试题17 打印从1到最大的n位数
弄清楚 n的范围,是否能用基本数据类型存储,需要考虑大数情况,利用数组来存数字或者使用DFS遍历
package nowcoder;
import javax.sound.midi.Instrument;
import org.junit.Test;
public class offer_17 {
public void print1ToMax(int num) {
short[] number = new short[num];
for(int i =0;i<num;i++) number[i] = 0;
while(!increment(number)) {
printNumber(number);
}
}
public boolean increment(short[] number) {
int len = number.length;
int c = 0;
number[len-1]++;
for(int i = len-1;i>=0;i--) {
number[i] = (short) (number[i] +c);
c = number[i] / 10;
if(number[i] >= 10) {
if(i == 0) {
return true;
}else {
number[i] = (short) (number[i] - 10);
}
}else {
break;
}
}
return false;
}
public void printNumber(short[] number) {
boolean flag = true;
for(int i= 0;i<number.length;i++) {
if(number[i] == 0 && flag) continue;
else {
flag = false;
System.out.print(number[i]);
}
}
System.out.println();
}
public void print1ToMaxDFS(int num) {
short[] number = new short[num];
dfs(number,0);
}
public void dfs(short[] number,int index) {
if(index == number.length) {
printNumber(number);
return;
}
for(int i = 0;i<10;i++) {
number[index]++;
dfs(number,index+1);
}
}
@Test
public void test() {
print1ToMax(2);
}
@Test
public void test2() {
print1ToMax(4);
}
}
面试题18 删除链表的节点
此处不是直接删除,而是将删除节点的下一个节点覆盖到该节点,然后删除下一个节点
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead == null || pHead.next == null) return pHead;
ListNode p = new ListNode(-1);
p.next = pHead;
pHead = p;
ListNode Node = pHead.next;
ListNode preNode = pHead;
ListNode nextNode = null;
while( Node!= null && Node.next!=null){
nextNode = Node.next;
if(Node.val == nextNode.val){
while(nextNode!= null && Node.val == nextNode.val){
nextNode = nextNode.next;
}
preNode.next = nextNode;
Node = nextNode;
}else{
preNode = Node;
Node = Node.next;
}
}
return pHead.next;
}
}
面试题19 正则表达式匹配
传送门
将str和pattern分为四种情况讨论
- str为空,pattern不为空
- str不为空,pattern为空
- str 和pattern为空
- str和pattern都不为空
public class Solution {
public boolean match(char[] str, char[] pattern)
{
if(str == null || pattern == null) return false;
return matchCore(str,pattern,0,0);
}
public boolean matchCore(char[] str,char[] pattern,int strIndex,int patternIndex){
if(strIndex == str.length && patternIndex == pattern.length)
return true;
else if(strIndex != str.length && patternIndex == pattern.length)
return false;
else if(strIndex == str.length && patternIndex != pattern.length){
if(patternIndex +1 < pattern.length && pattern[patternIndex+1] == '*' )
return matchCore(str,pattern,strIndex,patternIndex+2);
else
return false;
}
else{
if(patternIndex+1 < pattern.length && pattern[patternIndex+1] == '*'){
//*前面的字符匹配成功
if( (strIndex <str.length &&pattern[patternIndex] == str[strIndex] )||(pattern[patternIndex]=='.' && strIndex!=str.length )){
//匹配,进入下一个状态
return matchCore(str,pattern,strIndex+1,patternIndex+2) ||
// 匹配成功,继续匹配,即*视为大于1
matchCore(str,pattern,strIndex+1,patternIndex)||
// 跳过 ,将*视为0
matchCore(str,pattern,strIndex,patternIndex+2);
}else{
//*前面的字符没有匹配成功,*视为0次
return matchCore(str,pattern,strIndex,patternIndex+2);
}
}else{
if((strIndex < str.length && str[strIndex] == pattern[patternIndex] )|| (pattern[patternIndex] == '.' &&strIndex != str.length))
return matchCore(str,pattern,strIndex+1,patternIndex+1);
else
return false;
}
}
}
}
面试题21 调整数组顺序使奇数位于偶数前面
传送门
1、双指针方法,复杂度o(n),不能保证原来奇数和偶数各自相对位置
public class Solution {
public void reOrderArray(int[] array) {
int left = 0,right = array.length-1;
while(left<right){
while(left < right && (array[left]&0x1) != 0)
left ++;
while(left < right && (array[right]&0x1) == 0)
right --;
if(left<right){
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
}
}
}
2、插入排序、冒泡排序思想,复杂度o(n),能够保证原来相对位置
public class Solution {
public void reOrderArray(int[] array) {
for(int i = 1;i<array.length;i++){
int temp = array[i];
int j = i-1;
if(array[i]%2 == 1){
for(;j>=0;j--){
if((array[j]&0x1) == 1){
break;
}else
array[j+1]= array[j];
}
array[j+1] = temp;
}else{
continue;
}
}
}
}
面试题22 链表中倒数第k个节点
传送门
方法1:遍历两次链表
方法2:通过快慢指针方式,首先快指针先走k-1步,然后两个指针一起往后走,知道快指针先到达最后重点。
为了保证鲁棒性,需要考虑边缘情况,链表为空,k为0或者链表中节点数目小于k。
举一反三,获取链表中间节点,也可通过双指针方式,一个一次走两步,一个一次走一步。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head == null || k == 0) return null;
ListNode first = head,last = head;
for(int i = 0;i<k-1;i++){
if(first.next!=null) first=first.next;
else return null;
}
while(first.next!=null){
first = first.next;
last = last.next;
}
return last;
}
}
面试题23 链表中环的入口节点
同样采用快慢指针方式,我们注意到第一次相遇时
慢指针走过的路程S1 = 非环部分长度 + 弧A长
快指针走过的路程S2 = 非环部分长度 + n * 环长 + 弧A长
S1 * 2 = S2,可得 非环部分长度 = n * 环长 - 弧A长
让指针A回到起始点后,走过一个非环部分长度,指针B走过了相等的长度,也就是n * 环长 - 弧A长,正好回到环的开头。
为了保证鲁棒性,需要考虑链表是否存在环问题。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode slow = pHead,fast = pHead;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
break;
}
if(fast == null || fast.next == null) return null;
slow = pHead;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
面试题24 反转链表
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode pre = null;
ListNode pNode = head;
ListNode pNext = null;
while(pNode != null){
pNext = pNode.next;
pNode.next = pre;
pre = pNode;
pNode = pNext;
}
return pre;
}
}
面试题25 合并两个排序链表
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode head = new ListNode(0);
ListNode t = head;
while(list1 != null && list2!= null){
if(list1.val <= list2.val){
t.next = list1;
list1 = list1.next;
t = t.next;
t.next = null;
}else{
t.next = list2;
list2 = list2.next;
t = t.next;
t.next = null;
}
}
if(list1 == null){
t.next = list2;
}else{
t.next = list1;
}
return head.next;
}
}
面试题26 树的子结构
分两步,第一步找到A树与树B的根节点相同的节点,采用DFS或BFS方法,第二步继续后续判断左子节点和右子节点是否相同。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1 == null || root2 == null) return false;
boolean flag = false;
if(root1.val == root2.val){
flag = equal(root1,root2);
if(flag == false)
flag = HasSubtree(root1.left,root2) || HasSubtree(root1.right,root2);
}
return flag;
}
public boolean equal(TreeNode node1,TreeNode node2){
if(node2 == null) return true;
if(node1 == null && node2!= null) return false;
if(node1.val == node2.val)
return equal(node1.left,node2.left) && equal(node1.right,node2.right);
else{
return false;
}
}
}
面试题27 二叉树的镜像树
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public void Mirror(TreeNode root) {
dfs(root);
}
public void dfs(TreeNode node){
if(node == null) return;
TreeNode t = node.right;
node.right = node.left;
node.left = t;
dfs(node.left);
dfs(node.right);
return;
}
}
面试题28 对称的二叉树
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null)
return true;
else
return dfs(pRoot.left,pRoot.right);
}
boolean dfs(TreeNode node1,TreeNode node2){
if(node1 == null && node2 != null) return false;
if(node1 != null && node2 == null) return false;
if(node1 == null && node2 == null) return true;
if(node1.val == node2.val){
return dfs(node1.left,node2.right) && dfs(node1.right,node2.left);
}
return false;
}
}
面试题30 包含min函数的栈
import java.util.Stack;
public class Solution {
//
int N = 1000;
int[] stack = new int[N];
int[] h = new int[N];
int index = -1;
int t = -1;
public void push(int node) {
stack[++index] = node;
if(t == -1){
h[++t] = index;
}else if(node < stack[h[t]]){
h[++t] = index;
}
}
public void pop() {
if(index<0) return;
else if(index == h[t]){
t--;
index--;
}else{
index--;
}
return;
}
public int top() {
return stack[index];
}
public int min() {
return stack[h[t]];
}
}
面试题34:二叉树中和为某一值的路径
这题是采用深度优先搜索方法遍历所有路径,搜索故过程中将节点保存在List中,当搜索到叶子节点时并且总和为target时则写入ret。然后继续搜索左子节点和右子节点。注意的是回溯的时候需要path中加入的元素删除掉。
传送门
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<Integer> path = new ArrayList<>();
ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null) return ret;
dfs(root,target,0);
ret.sort((a,b)->b.size()-a.size());
return ret;
}
public void dfs(TreeNode node,int target,int sum){
path.add(node.val);
sum += node.val;
if(sum == target && node.left == null && node.right == null){
ArrayList<Integer> t = new ArrayList<>();
t.addAll(path);
ret.add(t);
}
if(node.left != null){
dfs(node.left,target,sum);
}
if(node.right!= null){
dfs(node.right,target,sum);
}
path.remove(path.size()-1);
return;
}
}
复制复杂链表
传送门
1、在原链表中每个节点的后面赋值一个新的节点。
2、更新每个新节点的随机指针值。
3、将链表拆分成两个链表。
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null) return null;
RandomListNode head = pHead;
while(head!=null){
RandomListNode t = new RandomListNode(head.label);
t.next = head.next;
head.next = t;
head = t.next;
}
head = pHead;
while(head != null){
head.next.random = head.random==null ? null:head.random.next;
head = head.next.next;
}
RandomListNode ret = pHead.next;
head = ret;
RandomListNode h = pHead;
while(head.next!=null){
h.next = head.next;
h = h.next;
head.next = head.next.next;
head = head.next;
}
h.next = null;
return ret;
}
}
二叉搜索树与双向链表
传送门
1、很明显中序遍历的结果就是链表的顺序。
2、以根节点为例,先获取左子树的形成的链表,该链表中最后一个元素为最大值,根节点需要与其相连。然后生成右子树形成的链表,该链表中第一个元素需要与根节点相连。考虑到需要左子树链表的最后一个元素,需要右子树的第一个元素,第一个元素作为函数的返回值,最后一个元素用过一个全局变量记录。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeNode last; //全局变量,用于记录已经生成的链表的最右边节点。
//以根节点为例,确定递归函数的返回值。
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null) return null;
if(pRootOfTree.left == null && pRootOfTree.right == null){
last = pRootOfTree;
return last;
}
TreeNode left = Convert(pRootOfTree.left);
if(left!=null){
pRootOfTree.left = last;
last.right = pRootOfTree;
}
last = pRootOfTree;
TreeNode right = Convert(pRootOfTree.right);
if(right!=null){
pRootOfTree.right = right;
right.left = pRootOfTree;
}
return left==null?pRootOfTree:left;
}
}
序列化二叉树
传送门
先序遍历二叉树,那反序列化时第一个读取到的数就是根节点的值。
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
StringBuilder ret = new StringBuilder();
String[] r;
int index = -1;
String Serialize(TreeNode root) {
dfs(root);
return ret.toString();
}
public void dfs(TreeNode node){
if(node == null) ret.append("#,");
else{
ret.append(node.val);
ret.append(",");
dfs(node.left );
dfs(node.right);
}
return;
}
public TreeNode dfsD(){
index += 1;
if(index < r.length){
if(r[index].equals("#")){
return null;
}
TreeNode node = new TreeNode(Integer.parseInt(r[index]));
node.left = dfsD();
node.right = dfsD();
return node;
}
return null;
}
TreeNode Deserialize(String str) {
r = str.split(",");
return dfsD();
}
}
38 字符串的排列
给一个字符串,求出他的所有排列可能性,并以字典序输出。
分析:将该问题分解为若干个子问题来解决。对于字符串abc,第一步求出所有可能出现在第一个位置的字符,即把第一个字符与后面的字符进行交换。第二步固定第一个字符位置,求后面字符的所有排列可能。
传送门
import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> ret = new ArrayList<>();
if(str!=null&&str.length()!=0){
char[] s = str.toCharArray();
dfs(s,0,ret);
Collections.sort(ret);
}
return ret;
}
public void dfs(char[] ch ,int index, ArrayList<String> ret){
if(index == ch.length){
ret.add(String.valueOf(ch));
}
for(int j = index;j<ch.length;j++){
if(j == index||ch[j]!=ch[index]){
swap(ch,j,index);
dfs(ch,index+1,ret);
swap(ch,j,index);
}
}
}
public void swap(char[] ch,int x,int y){
char t = ch[x];
ch[x] = ch[y];
ch[y] = t;
return;
}
}
字符串的组合
该题是上面一题的扩展,对于abc它的所有存在的组合包括a、b、c、ab、ac、bc、abc。对于长度为n的字符串x,求长度为m的组合,第一个字符可能是x的第一个字符,然后在剩下n-1的长度里取m-1个,也可能是n-1个字符里取m个字符。
package nowcoder;
import java.awt.event.ItemEvent;
import java.lang.annotation.Retention;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.junit.Test;
public class TestSplit {
public static void Func(int len,char[] s,int index,List<Character> ret) {
if(len == 0) {
for(char ch : ret) {
System.out.print(ch);
}
System.out.println();
return;
}
if(index == s.length) {
return;
}
ret.add(s[index]);
Func(len-1, s, index+1, ret);
ret.remove(ret.size()-1);
Func(len, s, index+1, ret);
}
public static void main(String[] args) {
String pString = "abc";
char[] s = pString.toCharArray();
for(int i = 1;i<=s.length;i++) {
List<Character> ret= new ArrayList<>();
Func(i,s,0,ret);
}
}
}
数组中出现次数超过一半的数字
方法一:基于快排思想找到排序数组中位于索引n/2出的元素,因为如果某个数字个数超过一半,那么该数字一定会在n/2处出现一次,通过快排中Partition能够将基数放置到合适位置,如果位置位于n/2处,则找到,然后验证过即可。如果不是,继续对左边或者右半边使用partition函数查找。
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int len = array.length;
if(len == 0) return 0;
int mid = len>>1;
int index = Partition(array,0,len-1);
int end =len-1;
int start = 0;
while(index!=mid){
if(index < mid){
start = index+1;
index = Partition(array,start,end);
}else{
end = index -1;
index = Partition(array,start,end);
}
}
int count = 0;
for(int i = 0;i<len;i++){
if(array[index] == array[i]){
count++;
}
}
return count>len/2?array[index]:0;
}
public int Partition(int[] array,int left, int right){
int temp = array[left];
while(left<right){
while(right>left&&array[right]>=temp){
right--;
}
array[left] = array[right];
while(right>left&&array[left]<=temp){
left++;
}
array[right] = array[left];
}
array[left] = temp;
return left;
}
}
方法二:摩尔投票法。
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length == 0) return 0;
int count = 1;
int num=array[0];
for(int i = 1;i<array.length;i++){
if(array[i] == num){
count++;
}else{
count--;
if(count == 0){
num=array[i];
count = 1;
}
}
}
count =0;
for(int i = 0;i<array.length;i++){
if(array[i] == num){
count++;
}
}
return count>array.length/2?num:0;
}
}
面试题40 最小的k个数
1、基于快排的思想,把输入的n个整数排序,位于最前面的k个数就是最小的k个数。基于数组的第k个数调整,比该数小的位于数组左边,比k大的位于右边即可。
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> a = new ArrayList<>();
if(input == null || input.length == 0 || input.length<k || k == 0) return a;
int start = 0,end = input.length-1;
int index = Partition(input,start,end);
while(index!=k-1){
if(index<k-1){
start = index + 1;
index = Partition(input,start,end);
}else{
end = index-1;
index = Partition(input,start,end);
}
}
for(int i= 0;i<k;i++){
a.add(input[i]);
}
return a;
}
public int Partition(int[] input,int s, int e){
int t = input[s];
while(s<e){
while( s < e && input[e] >= t){
e--;
}
input[s] = input[e];
while(s<e && input[s]<= t){
s++;
}
input[e] = input[s];
}
input[s] = t;
return s;
}
}
2、最大堆,构造一个容量为k的元素的最大堆。当堆不满时候,往里面插入数字。当堆满时,堆顶数字则为最大数字,将该数字与插入数字对比,如果堆顶数字更大,则删除堆顶数字,将插入数字插入,否则跳过。
PriorityQueue底层使用堆实现,构造优先队列时候需要传入一个实现Comparator接口的对象。
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> a = new ArrayList<>();
if(input == null || input.length == 0 || input.length<k || k == 0) return a;
PriorityQueue<Integer> q = new PriorityQueue<>(k,new Comparator<Integer>(){
public int compare(Integer x,Integer y){
return y-x;
}
});
for(int i = 0;i<input.length;i++){
if(q.size()<k){
q.add(input[i]);
}else{
int t = q.element();
if(input[i] >= t){
continue;
}else{
q.remove();
q.add(input[i]);
}
}
}
while(!q.isEmpty()){
a.add(q.element());
q.remove();
}
return a;
}
}
面试题42 连续子数组的最大和
动态规划方法:
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int len = array.length;
int[] dp= new int[len];
dp[0] = array[0];
int ret = dp[0];
for(int i = 1;i<len;i++){
dp[i] = Math.max(dp[i-1]+array[i],array[i]);
ret = Math.max(dp[i],ret);
}
return ret;
}
}
找规律:
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int len = array.length;
int ret = array[0];
int sum = ret;
for(int i = 1;i<len;i++){
if(sum<0){
sum = array[i];
ret = Math.max(ret,sum);
}else{
sum += array[i];
ret = Math.max(ret,sum);
}
}
}
}
面试题43:1~n整数中1出现的次数
分析0-9 出现次数1,10-99 :101+10=20,0-999:2010+100=300,0-9999:30010+1000=4000,以7245举例,可以先千求1~7000中1的个数,为3007+1000,如果千位为1,则3001+146。然后计算 千位为7时7000~7199出现1的个数,202+100,然后计算7200~7239之间1的个数,1*4+10,最后加上个位的1。
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int[] d = new int[10];
d[1] = 1;
d[0] = 0;
int[] w = new int[10];
for(int i = 2;i<=9;i++){
d[i] = d[i-1]*10+(int)Math.pow(10,i-1);
}
int len = 0;
int t = n;
while(t>0){
w[len+1] = t%10;
len++;
t = t/10;
}
int ret = 0;
for(int i =len;i>=1;i--){
if(w[i]>=2){
ret = ret + d[i-1]*w[i] + (int)Math.pow(10,i-1);
}else if(w[i] == 1){
ret = ret +d[i-1] + func(w,i-1);
}else{
continue;
}
}
return ret;
}
public int func(int[] w,int x){
int ret=0;
for(int i = x;i>=1;i--){
ret = ret * 10 +w[i];
}
return ret+1;
}
}
面试题44 数字序列中某一位的数字
数字以012345678910111213141516...的格式序列化为一个字符序列,第5位为5(从0开始),第13位为1,第19位为4,求任意第n位对应的数字。
0-9有10位,10-99 有902=180位,100-999有3900=2700位,以求第1001位举例,1001大于10,则求10开始的第991位,991大于180,则求100开始的第811位,811小于2700,则肯定在100-999之间,822=270*3+1,则为370中间一位,为1。
传送门
package nowcoder;
public class offer44 {
public int func(int n) {
int[] dp = new int[10];
dp[1] = 10;
int num = n;
for(int i =2;i<7;i++) {
dp[i] = (int)Math.pow(10, i-1)*9*i;
}
int i = 1;
for(i = 1;i<10;i++) {
if(num-dp[i]>0) {
num -= dp[i];
}else {
break;
}
}
int k = num%i;
int t = num/i+(int)Math.pow(10, i);
int l = i-k;
while(l>1) {
t = t/10;
l--;
}
return t%10;
}
public static void main(String[] args) {
offer44 pOffer44 = new offer44();
System.out.println(pOffer44.func(5));
System.out.println(pOffer44.func(13));
System.out.println(pOffer44.func(19));
}
}
把数组排成最小的数
传送门
对数组进行排序即可,比较m和n大小,如果mn<nm,则m<n.
import java.util.*;
public class Solution {
public String PrintMinNumber(int [] numbers) {
if(numbers==null|| numbers.length == 0) return "";
Integer[] q = new Integer[numbers.length];
for(int i =0;i<numbers.length;i++){
q[i] = numbers[i];
}
Arrays.sort(q,new Comparator<Integer>(){
public int compare(Integer x,Integer y){
int a = countOfIntegers(x,y);
int b = countOfIntegers(y,x);
return a-b;
}
});
StringBuffer p = new StringBuffer("");
for(int i =0;i<numbers.length;i++){
p.append(q[i]+"");
}
return p.toString();
}
public int countOfIntegers(int x,int num){
int ret=1;
int t= num;
while(num>0){
ret*=10;
num = num/10;
}
return x*ret+t;
}
}
面试题46 把数字翻译成字符串
方法1:自顶向下递归搜索,但会重复解决子问题。
package nowcoder;
import java.text.DateFormatSymbols;
import java.util.*;
public class offer46 {
public static int DFS(char[] x,int index) {
if(index+1<x.length) {
char a = x[index];
char b = x[index+1];
int num = (a-'0')*10+b-'0';
if(num<=25) {
return DFS(x, index+2)+DFS(x, index+1);
}else {
return DFS(x, index+1);
}
}else {
return 1;
}
}
public static void main(String[] args) {
String pString = "12258";
char[] p = pString.toCharArray();
System.out.println(DFS(p,0));
}
}
2、动态规划
package nowcoder;
import java.text.DateFormatSymbols;
import java.util.*;
public class offer45 {
public static int DFS(char[] x,int index) {
int len = x.length;
int[] dp = new int[len+1];
for(int i = 0;i<len+1;i++) dp[i] = 0;
dp[len-1] = 1;
dp[len] = 0;
for(int i = len-2;i>=0;i--) {
char a = x[i+1];
char b = x[i];
int t = (b-'0')*10+a-'0';
if(t<=25) {
dp[i] = dp[i] + dp[i+2] + dp[i+1];
}else {
dp[i] = dp[i+1];
}
}
return dp[0];
}
public static void main(String[] args) {
String pString = "12345";
char[] p = pString.toCharArray();
System.out.println(DFS(p,0));
}
}
面试题65 不用加减乘除法做加法
通过位运算来实现加法,这里注意首先不进位加法t= x ^ y,通过异或运算完成,进位通过与运算再移位 c = x & y,最后c 与t相加,下一步循环执行。
package nowcoder;
public class Offer_65 {
public static void solution(int x,int y) {
int t = 0;
int c= 0 ;
while(true) {
t = x^y;
c = (x & y)<<1;
if(c == 0) break;
x = t;
y = c;
}
System.out.println(t);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Offer_65.solution(121234, 22);
}
}
通过位运算交换两个数字
package nowcoder;
public class Offer_65_2 {
public static void solution(int x, int y) {
x = x+y;
y = x - y;
x = x -y;
System.out.println(x+" "+y);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Offer_65_2.solution(5,6);
}
}