剑指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);
	}
}
posted @ 2019-12-05 19:51  御心飞行  阅读(307)  评论(0编辑  收藏  举报