剑指Offer面试算法题Java实现


面试题3

/**
 * 二维数组的查找
 * @author acer
 *
 */
public class _03FindInPartiallySortedMatrix {

	public static boolean Find(int array[][],int number){
		boolean flag = false;
		int rows = array.length;	
		int columns = array[0].length;
		//从右上角开始比较
		int row = 0;
		int column = columns - 1;
		while(row < rows && column >= 0){
			if(array[row][column] == number){
				flag = true;
				break;
			}else if(array[row][column] > number){
				//比要找的数大,删除这列
				column--;
			}else{
				//比要找的数小,删除这行 
				row++;
			}
		}
		return flag;
	}
	
	public static void main(String[] args) {
		//测试:最小的数,最大的数,中间的数,没有的数
		int[][] a = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}};
		System.out.println(Find(a,1));
		System.out.println(Find(a,15));
		System.out.println(Find(a,7));
		System.out.println(Find(a,5));
		System.out.println(Find(a,0));
	}
}

面试题4

/**
 * 将一串字符串的空格替换为%20
 * @author acer
 *
 */
public class _04ReplaceBank {

	public static void main(String[] args) {
		//测试:一个空格,空格在最后,空格在最前,多个连续空格,没有空格
		String s = " we are  happy ";
		char[] c_old = s.toCharArray();
		char[] c_new = new char[100];
		for (int i = 0; i < c_old.length; i++) {
			c_new[i] = c_old[i];
		}
		System.out.println(ReplaceBank(c_new,c_old.length));
	}
	
	public static String ReplaceBank(char c[],int length){
		if(c == null || length <= 0)
			return null;
		int originalLength = 0;
		int numberOfBank = 0;
		int i= 0;
		while(c[i] != '\0'){
			++originalLength;
			if(c[i] == ' '){
				++numberOfBank;
			}
			++i;
		}
		
		int newLength = originalLength + 2 * numberOfBank;
		
		int indexOfOriginal = originalLength;
		int indexOfNew = newLength;
		while(indexOfOriginal >=0 && indexOfNew > indexOfOriginal){
			if(c[indexOfOriginal] == ' '){
				c[indexOfNew--] = '0';
				c[indexOfNew--] = '2';
				c[indexOfNew--] = '%';
			}else{
				c[indexOfNew--] = c[indexOfOriginal];
			}
			--indexOfOriginal;
		}
		return new String(c);
	}

}
面试题5

import java.util.Stack;

/**
 * 从尾到头打印链表
 * @author acer
 *
 */
public class _05PrintListReversingly {

	public static void main(String[] args) {
		ListNode head = new ListNode(0);
		ListNode node_1 = new ListNode(1);
		ListNode node_2 = new ListNode(2);
		ListNode node_3 = new ListNode(3);
		ListNode node_4 = new ListNode(4);
		head.setNext(node_1);
		node_1.setNext(node_2);
		node_2.setNext(node_3);
		node_3.setNext(node_4);
		node_4.setNext(null);
		
		PrintListReversingly1(head);
		System.out.println();
		PrintListReversingly2(head);
	}
	
	//递归实现
	public static void PrintListReversingly1(ListNode head){
		if(head != null){
			if(head.getNext() != null){
				PrintListReversingly1(head.getNext());
			}
			System.out.println(head.getValue() + "、");
		}
	}
	
	//栈实现
	public static void PrintListReversingly2(ListNode head){
		Stack<Integer> s = new Stack<Integer>();
		ListNode p = head;
		//进栈
		while(p != null){
			s.push(p.getValue());
			p = p.getNext();
		}
		//出栈
		while(! s.isEmpty()){
			System.out.println(s.pop() + "、");
		}
	}
}
面试题6

import java.util.Stack;

/**
 * 从尾到头打印链表
 * @author acer
 *
 */
public class _05PrintListReversingly {

	public static void main(String[] args) {
		ListNode head = new ListNode(0);
		ListNode node_1 = new ListNode(1);
		ListNode node_2 = new ListNode(2);
		ListNode node_3 = new ListNode(3);
		ListNode node_4 = new ListNode(4);
		head.setNext(node_1);
		node_1.setNext(node_2);
		node_2.setNext(node_3);
		node_3.setNext(node_4);
		node_4.setNext(null);
		
		PrintListReversingly1(head);
		System.out.println();
		PrintListReversingly2(head);
	}
	
	//递归实现
	public static void PrintListReversingly1(ListNode head){
		if(head != null){
			if(head.getNext() != null){
				PrintListReversingly1(head.getNext());
			}
			System.out.println(head.getValue() + "、");
		}
	}
	
	//栈实现
	public static void PrintListReversingly2(ListNode head){
		Stack<Integer> s = new Stack<Integer>();
		ListNode p = head;
		//进栈
		while(p != null){
			s.push(p.getValue());
			p = p.getNext();
		}
		//出栈
		while(! s.isEmpty()){
			System.out.println(s.pop() + "、");
		}
	}
}

面试题7

import java.util.Stack;

/**
 * 题目大致为:
    	用两个栈实现队列的两个函数appendTail和deleteHead。
	思路:
    	栈的特性是:后进先出,而队列的特性是:先进先出。这里使用两个栈实现队列有点负负得正的意思。栈1负责添加,而栈2负责删除。
 * @author acer
 *
 */
public class _07TwoStackQueue {

	public static void main(String[] args) {
		CQueue<Integer> cq = new CQueue<>();
		for (int i = 0; i < 10; i++) {
			cq.appendTail(i);
		}
		
		for (int i = 0; i < 10; i++) {
			System.out.println(cq.deleteHead() + "、");
		}
		System.out.println();
		//此时为空,再取一次,看是否报错
		cq.deleteHead();
	}

}

class CQueue<T>{
	private Stack<T> stack1;
	private Stack<T> stack2;
	
	public CQueue() {
		this.stack1 = new Stack<T>();
		this.stack2 = new Stack<T>();
	}
	
	//模拟在队列末尾插入
	public void appendTail(T node){
		stack1.push(node);
	}
	
	//模拟在队列头删除
	public T deleteHead(){
		if(stack2.size() == 0){
			if(stack1.size() == 0){
				try {
					throw new Exception("队列为空");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			//stack1不为空,将其放入stack2中
			while(stack1.size() > 0){
				stack2.push(stack1.pop());
			}
		}
		return stack2.pop();
	}
}
面试题8

/**
 * 题目大致为:
    	一个递增排序的数组的一个旋转(把一个数组最开始的若干元素搬到数组的末尾,称之为数组的旋转),输出旋转数组的最小元素。
	思路:
    	其实旋转过后的数组是部分有序的,这样的数组依然可以使用折半查找的思路求解
 */
public class _08RevolveArray {

	public static void main(String[] args) throws Exception {
		//功能测试
		int[] A = {4,5,6,7,8,0,1,2,3};
		int[] A1 = {1,1,0,0,1,1,1};
		System.out.println(findMin(A));
		System.out.println(findMin(A1));
		//边界值测试
		int[] B = {1};
		System.out.println(findMin(B));
		//特殊输入测试
		int[] C = {};
		System.out.println(findMin(C));
	}

	public static int findMin(int array[]) throws Exception{
		if(array == null || array.length <= 0){
			throw new Exception("输入参数有误");
		}
		
		int start = 0;
		int end = array.length - 1;
		int mid = start;
		
		while(array[start] >= array[end]){
			if(end - start == 1){
				mid = end;
				break;
			}
			mid = (start + end)/2;
			if(array[start] == array[end] && array[end] == array[mid]){
				return MinInOrder(array,start,end);
			}
			if(array[mid] >= array[start]){
				start = mid;
			}else if (array[mid] <= array[end]){
				end = mid;
			}
		}
		return array[mid];
	}

	private static int MinInOrder(int[] array, int start, int end) {
		int result = array[start];
		for (int i = start+1; i <= end; ++i) {
			if(result > array[i]){
				result = array[i];
			}
		}
		return result;
	}
}
面试题9

/**
 * 用递归实现的过程中会出现重复计算的情况,此时,可以利用动态规划的思想,保存中间结果,这样可以避免不必要的计算
 * @author acer
 *
 */
public class _09Fibonacci {

	public static void main(String[] args) {
		//System.out.println(Fibonacci(20));
		
		System.out.println(Fibonacci2(100));
		//边界值测试
		System.out.println(Fibonacci2(0));
		System.out.println(Fibonacci2(1));
		System.out.println(Fibonacci2(2));
	}

	//很占用内存且花费时间的解法
	public static long Fibonacci(long n){
		if(n <= 0){
			return 0;
		}
		if(n == 1){
			return 1;
		}
		return Fibonacci(n-1) + Fibonacci(n-2);
	}
	
	//好的解法
	public static long Fibonacci2(long n){
		if(n <= 0){
			return 0;
		}
		if(n == 1){
			return 1;
		}
		long zero = 0;
		long one = 1;
		long fn = 0;
		for (long i = 2; i <= n; i++) {
			fn = zero + one;
			zero = one;
			one = fn;
		}
		return fn;
	}
}
面试题10

/**
 * 题目大致为:
    	实现一个函数,输入一个整数,输出该数二进制表示中1的个数。
	思路:
    	把一个整数减去1,再和原整数做与运算,会把最右边一个1编程0,那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作。
 * @author acer
 *
 */
public class _10NumberOf1 {

	public static void main(String[] args) {
//		System.out.println(numberOf1_1(0x7FFFFFFF));
//		System.out.println(numberOf1_1(0x80000000));
//		System.out.println(numberOf1_1(0xFFFFFFFF));
		
//		System.out.println(numberOf1_2(5));
//		System.out.println(numberOf1_2(1));
//		System.out.println(numberOf1_2(1));
		
//		System.out.println(numberOf1_3(1));
//		System.out.println(numberOf1_3(0x7FFFFFFF)); // 31
//		System.out.println(numberOf1_3(0x80000000)); // 1
		System.out.println(numberOf1_3(0xFFFFFFFF)); // 32
		System.out.println(numberOf1_3(0)); // 0
	}
	
	//可能会造成死锁的方法,n向右移位
	public static int numberOf1_1(int n){
		int count = 0;
		while(n != 0){
			if((n & 1) > 0){
				count++;
			}
			n = n >> 1;
		}
		return count;
	}

	//常规解法,flag向左移位
	public static int numberOf1_2(int n){
		int count = 0;
		int flag = 1;
		while(n != 0){
			if((n & flag) > 0){
				count++;
			}
			flag = flag << 1;
		}
		return count;
	}
	
	//让面试官惊艳的解法
	public static int numberOf1_3(int n){
		int count = 0;
		while(n != 0){
			++count;
			//一个整数和它减去1的结果做位与运算,相当于把它最右边的1变成0,其他位不变
			n = (n-1) & n;
		}
		return count;
	}
}
面试题11

/**
 * 题目大致为:
    	实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
	思路:
    	可以考虑对指数折半,这样只需要计算一半的值,若指数是奇数,则-1再折半,否则直接折半。
 * @author acer
 *
 */
public class _11Power {

	public static void main(String[] args) {
//		System.out.println(Power(2, 5));//32
//		System.out.println(Power(2, -2));//0.25
//		System.out.println(Power(2, 0));//1
//		System.out.println(Power(-2, 5));//-32
//		System.out.println(Power(-2, 0));//1
		System.out.println(Power(0, 0));//1

	}

	private static boolean g_InvalidInput = false;
	
	public static double Power(double base,int exponent){
		
		if(equal(base,0.0) && exponent < 0){
			g_InvalidInput = true;
			return 0.0;
		}
		
		int absExponent = exponent;
		if(exponent < 0){
			absExponent = -exponent;
		}
		double result = PowerWithUnsignedExponent(base,absExponent);
		if(exponent < 0){
			result = 1.0 / result;
		}
		return result;
	}

	private static double PowerWithUnsignedExponent(double base, int absExponent) {
		if(absExponent == 0){
			return 1;
		}
		if(absExponent == 1){
			return base;
		}
//		double result = PowerWithUnsignedExponent(base, absExponent >> 1);
//		result *= result; 
//		return 0;
		if(absExponent >> 1 == 0){
			//指数为偶数
			int absExponent_1 = absExponent >> 1;
			double result = PowerWithUnsignedExponent(base, absExponent_1);
			return result * result;
		}else{
			//指数为奇数
			int absExponent_2 = absExponent - 1;
			double result = PowerWithUnsignedExponent(base, absExponent_2);
			return result * base;
		}
	}

	private static boolean equal(double num1, double num2) {
		if((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001)){
			return true;
		}else{
			return false;
		}
	}
}
面试题12

public class _12Print1ToMaxOfNDigits {

	public static void main(String[] args) {
		Print1ToMaxOfNDigits(2);

	}
	
	public static void Print1ToMaxOfNDigits(int n){
		if(n <= 0){
			return;
		}
		char[] number = new char[n+1];
		number[n] = '\0';
		
		for (int i = 0; i < 10; i++) {
			number[0] = (char) (i + '0');
			Print1ToMaxOfNDigitsRecursively(number,n,0);
		}
		
	}

	private static void Print1ToMaxOfNDigitsRecursively(char[] number, int length, int index) {
		if(index == length - 1){
			System.out.println(number);
			return;
		}
		for (int i = 0; i < 10; ++i) {
			number[index+1] = (char) (i + '0');
			Print1ToMaxOfNDigitsRecursively(number, length, index + 1);
		}
	}

}

面试题13

/**
题目大致为:
    给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
思路:
    想要在O(1)时间内删除链表的指定结点,要遍历的话得O(n),则肯定不能遍历。
    若是要删除的结点不是尾结点,那么可以将后面的那个值复制到该指针处,并将后面指针所指空间删除,
    用复制+删除后面的实现删除,时间复杂度为O(1)。对于尾结点,需要遍历,那么时间复杂度是O(n),
    但是总的时间复杂度为[(n-1)*O(1)+O(n)]/n,结果是O(1)。
 * @author acer
 *
 */
public class _13DeleteNodeList {

	public static void main(String[] args) {
		ListNode head = new ListNode(1);
		ListNode node_2 = new ListNode(2);
		ListNode node_3 = new ListNode(3);
		ListNode node_4 = new ListNode(4);
		ListNode node_5 = new ListNode(5);
		ListNode node_6 = new ListNode(6);
		ListNode node_7 = new ListNode(7);
		head.setNext(node_2);
		node_2.setNext(node_3);
		node_3.setNext(node_4);
		node_4.setNext(node_5);
		node_5.setNext(node_6);
		node_6.setNext(node_7);
		node_7.setNext(null);
		
		// 输出原始链表  
//        System.out.println("原始链表:");  
//        printList(head);  
//        System.out.println("----------------");  
        
        // 删除结点node_3  
//        DeleteNode(head, node_3);  
//        System.out.println("删除node_3后链表:");  
//        printList(head);  
//        System.out.println("----------------");  
        
        // 删除结点head  
//        DeleteNode(head, head);  
//        System.out.println("删除head后链表:");  
//        printList(head);  
//        System.out.println("----------------"); 
		
		//删除尾节点
		DeleteNode(head, node_7);
		System.out.println("删除尾节点后链表:");
		printList(head);
		System.out.println("----------------"); 
	}
	
	public static void DeleteNode(ListNode head,ListNode toBeDelete){
		if(head == null || toBeDelete == null){
			return;
		}
		//找删除节点的下一节点,要删除的节点不是尾节点
		if(toBeDelete.getNext() != null){
			ListNode p = toBeDelete.getNext();
			toBeDelete.setValue(p.getValue());
			toBeDelete.setNext(p.getNext());//删除p节点
		}else if(toBeDelete == head){
			head = null;
		}else{
			//删除尾节点
			ListNode currentNode = head;
			while(currentNode.getNext() != toBeDelete){
				currentNode = currentNode.getNext();
			}
			currentNode.setNext(null);
		}
	}
	
	//打印链表
	public static void printList(ListNode head){
		ListNode current = head;
		while(current != null){
			System.out.println(current.getValue() + "、");
			current = current.getNext();
		}
		System.out.println();
	}

}

面试题14

import java.util.Arrays;

/**
题目大致为:
    对于一个数组,实现一个函数使得所有奇数位于数组的前半部分,偶数位于数组的后半部分。
思路:
    可以使用双指针的方式,一个指针指向数组的开始,一个指针指向数组的尾部,
    如果头部的数为偶数且尾部的数是奇数则交换,否则头部指针向后移动,尾部指针向前移动,直到两个指针相遇
 * @author acer
 *
 */
public class _14ReorderArray {

	public static void main(String[] args) {
		int[] array = {1,2,3,4,5,6};
		int[] array2 = {1,3,5,7,2,4,6};
		int[] array3 = {2,4,6,8,1,3,5,7};
		ReorderOddEven(array3);
		System.out.println(Arrays.toString(array3));

	}
	
	public static void ReorderOddEven(int[] array){
		if(array == null || array.length == 0){
			return;
		}
		int start = 0;
		int end = array.length - 1;
		
		while(start < end){
			//如果start为偶数,end为奇数,则交换
			if(array[start] % 2 == 0 && array[end] % 2 == 1){
				int tmp = array[start];
				array[start] = array[end];
				array[end] = tmp;
				start++;
				end--;
			}else if(array[start] % 2 == 1){
				//start位置上为奇数,start后移
				start++;
			}else{
				//end位置上为偶数,end前移
				end--;
			}
		}
	}
}

面试题15

/**
题目大致为:
    在一个链表中,查找倒数的第k个数。
思路:
    使用双指针的方式,前一个指针先走k步(中间隔k-1个结点),后一个指针才开始走,直到第一个指针走到尾,后一个指针指向的就是要找的倒数第k个数。
    值得注意的是:1、k是否超过链表长度且k必须为正整数;2、链表是否为空。
 * @author acer
 */
public class _15KthNodeFromEnd {

	public static void main(String[] args) {
		ListNode head = new ListNode(1);
		ListNode node_2 = new ListNode(2);
		ListNode node_3 = new ListNode(3);
		ListNode node_4 = new ListNode(4);
		ListNode node_5 = new ListNode(5);
		ListNode node_6 = new ListNode(6);
		ListNode node_7 = new ListNode(7);
		head.setNext(node_2);
		node_2.setNext(node_3);
		node_3.setNext(node_4);
		node_4.setNext(node_5);
		node_5.setNext(node_6);
		node_6.setNext(node_7);
		node_7.setNext(null);
		//查找倒数第k个元素
//		System.out.println(FindKthToTail(head, 3).getValue());
//		System.out.println(FindKthToTail(head, 1).getValue());
//		System.out.println(FindKthToTail(head, 7).getValue());
		
		//鲁棒性测试
//		System.out.println(FindKthToTail(head, 0).getValue());
//		System.out.println(FindKthToTail(head, 8).getValue());
//		System.out.println(FindKthToTail(null, 3).getValue());
	}
	
	public static ListNode FindKthToTail(ListNode head,int k){
		if(k == 0 || head == null){
			return null;
		}
		ListNode firstPoint = head;
		ListNode secondPoint = head;
		for (int i = 0; i < k-1; i++) {
			if(firstPoint.getNext() != null){
				firstPoint = firstPoint.getNext();
			}else{
				return null;
			}
		}
		
		while(firstPoint.getNext() != null){
			firstPoint = firstPoint.getNext();
			secondPoint = secondPoint.getNext();
		}
		return secondPoint;
	}

}

面试题16

/**
题目大致为:
    对于一个链表,反转该链表并返回头结点。
思路:
    主要是指针的操作,但是要注意不能断链。这里可以使用非递归的方式求解。
 * @author acer
 *
 */
public class _16ReverseList {

	public static void main(String[] args) {
		// 构建链表  
        ListNode head = new ListNode(0);  
        ListNode node_one = new ListNode(1);  
        ListNode node_two = new ListNode(2);  
        ListNode node_three = new ListNode(3);  
        ListNode node_four = new ListNode(4);  
        head.setNext(node_one);  
        node_one.setNext(node_two);  
        node_two.setNext(node_three);  
        node_three.setNext(node_four);  
        node_four.setNext(null); 
        
        ListNode reverseHead = ReverseList(head);
        ListNode tmp = reverseHead;
        while(tmp != null){
        	System.out.println(tmp.getValue() + "、");
        	tmp = tmp.getNext();
        }

	}
	
	public static ListNode ReverseList(ListNode head){
		ListNode reverseHead = null;//翻转后的头节点
		ListNode pNode = head;//当前节点
		ListNode pPrev = null;//当前节点的前一个节点
		while(pNode != null){
			ListNode next = pNode.getNext();
			pNode.setNext(pPrev);
			pPrev = pNode;
			pNode = next;
			
			if(next == null){
				reverseHead = pNode;
			}
		}
		return reverseHead;
	}

}

面试题17

/**
题目大致为:
	输入两个递增排序的链表,合并这两个链表并使得新链表中的结点仍然按照递增排序的。
思路:
	主要是链表中值的比较,取较小的结点插入到新的链表中。
 * @author acer
 *
 */
public class _17MergeSortedLists {

	public static void main(String[] args) {
		// 构建链表1  
        ListNode head1 = new ListNode(1);  
        ListNode node1_2 = new ListNode(3);  
        ListNode node1_3 = new ListNode(5);  
        ListNode node1_4 = new ListNode(7);  
        head1.setNext(node1_2);  
        node1_2.setNext(node1_3);  
        node1_3.setNext(node1_4);  
        node1_4.setNext(null);  
        // 构建链表2  
        ListNode head2 = new ListNode(2);  
        ListNode node2_2 = new ListNode(4);  
        ListNode node2_3 = new ListNode(6);  
        ListNode node2_4 = new ListNode(8);  
        head2.setNext(node2_2);  
        node2_2.setNext(node2_3);  
        node2_3.setNext(node2_4);  
        node2_4.setNext(null);
        
        System.out.println("链表1:");  
        printList(head1);  
        System.out.println("-------------");  
        System.out.println("链表2:");  
        printList(head2);  
        System.out.println("-------------");  
        System.out.println("合并后的链表:");  
        ListNode mergedList = MergeSortedLists(head1, head2);
        printList(mergedList);
	}

	public static ListNode MergeSortedLists(ListNode head1,ListNode head2){
		if(head1 == null){
			return head2;
		}else if(head2 == null){
			return head1;
		}
		ListNode mergedHead = null;
		//采用递归表示
		if(head1.getValue() < head2.getValue()){
			mergedHead = head1;
			mergedHead.setNext(MergeSortedLists(head1.getNext(), head2));
		}else{
			mergedHead = head2;
			mergedHead.setNext(MergeSortedLists(head1, head2.getNext()));
		}
		return mergedHead;
	}
	
	public static void printList(ListNode head) {  
        ListNode current = head;  
        while (current != null) {  
            System.out.print(current.getValue() + "、");  
            current = current.getNext();  
        }  
        System.out.println();  
    }
}

面试题18

/**
题目:
	输入两棵二叉树A,B,判断B是不是A的子结构
思路:
	递归
 * @author acer
 *
 */
public class _18SubstructureInTree {

	public static void main(String[] args) {
		//第一个二叉树
		BinaryTreeNode root1 = new BinaryTreeNode(8);
		BinaryTreeNode node1_2 = new BinaryTreeNode(8);
		BinaryTreeNode node1_3 = new BinaryTreeNode(9);
		BinaryTreeNode node1_4 = new BinaryTreeNode(2);
		BinaryTreeNode node1_5 = new BinaryTreeNode(4);
		BinaryTreeNode node1_6 = new BinaryTreeNode(7);
		BinaryTreeNode node1_7 = new BinaryTreeNode(7);
		root1.setLeft(node1_2);
		root1.setRight(node1_7);
		node1_2.setLeft(node1_3);
		node1_2.setRight(node1_4);
		node1_4.setLeft(node1_5);
		node1_4.setRight(node1_6);
		//第二个二叉树
		BinaryTreeNode root2 = new BinaryTreeNode(8);
		BinaryTreeNode node2_1 = new BinaryTreeNode(9);
		BinaryTreeNode node2_2 = new BinaryTreeNode(2);
		root2.setLeft(node2_1);
		root2.setRight(node2_2);
		
		System.out.println(HasSubtree(root1, root2));

	}

	public static boolean HasSubtree(BinaryTreeNode root1,BinaryTreeNode root2){
		boolean result = false;
		
		if(root1 != null && root2 != null){
			if(root1.getValue() == root2.getValue()){
				result = DoesTree1HaveTree2(root1,root2);
			}
			if(!result)
				result = HasSubtree(root1.getLeft(), root2);
			if(!result)
				result = HasSubtree(root1.getRight(), root2);
		}
		return result;
	}

	private static boolean DoesTree1HaveTree2(BinaryTreeNode root1, BinaryTreeNode root2) {
		//这个判断顺序是固定的,不能改变,不过不知道为啥??????????????? 
		if(root2 == null)
			return true;
		if(root1 == null)
			return false;
		if(root1.getValue() != root2.getValue())
			return false;
		return DoesTree1HaveTree2(root1.getLeft(), root2.getLeft()) && DoesTree1HaveTree2(root1.getRight(), root2.getRight());
	}
}

面试题19

/**
题目大致为:二叉树的镜像
    给定一棵二叉树,将其每一个结点的左右子树交换,这就叫做镜像。
思路:
    先对其根节点的左右子树处理,交换左右子树,此时再递归处理左右子树。
    这里要注意分为三种情况:
    1、树为空;2、只有根结点;3、左右子树至少有一个不为空。
 * @author acer
 */
public class _19MirrorOfBinaryTree {

	public static void main(String[] args) {
		BinaryTreeNode root = new BinaryTreeNode(8);  
		BinaryTreeNode t1 = new BinaryTreeNode(6);  
		BinaryTreeNode t2 = new BinaryTreeNode(10);  
		BinaryTreeNode t3 = new BinaryTreeNode(5);  
		BinaryTreeNode t4 = new BinaryTreeNode(7);  
		BinaryTreeNode t5 = new BinaryTreeNode(9);  
		BinaryTreeNode t6 = new BinaryTreeNode(11);  
		root.setLeft(t1);  
		root.setRight(t2);  
		t1.setLeft(t3);  
		t1.setRight(t4);  
		t2.setLeft(t5);  
		t2.setRight(t6);  
		t3.setLeft(null);  
		t3.setRight(null);  
		t4.setLeft(null);  
		t4.setRight(null);  
		t5.setLeft(null);  
		t5.setRight(null);  
		t6.setLeft(null);  
		t6.setRight(null);

		MirrorRecursively(root);
		printPreOrder(root);
	}

	public static void MirrorRecursively(BinaryTreeNode root){
		if(root == null){
			return;
		}
		if(root.getLeft() == null && root.getRight() == null){
			return;
		}
		BinaryTreeNode tmp = root.getLeft();
		root.setLeft(root.getRight());
		root.setRight(tmp);
		
		//递归求解左右子树
		if(root.getLeft() != null){
			MirrorRecursively(root.getLeft());
		}
		if(root.getRight() != null){
			MirrorRecursively(root.getRight());
		}
	}
	
	//前序输出
	private static void printPreOrder(BinaryTreeNode root){
		if(root != null){
			System.out.println(root.getValue() + " ");
			printPreOrder(root.getLeft());
			printPreOrder(root.getRight());
		}
	}
}

面试题20

/**
 * 题目:
 * 	输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。  
 * @author acer
 *
 */
public class _20PrintMatrix {

	public static void main(String[] args) {
		int[][] array = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
		PrintMatrixClockwisely(array);
	}
	
	public static void PrintMatrixClockwisely(int[][] array){
		if(array == null){
			return;
		}
		int start = 0;
		while(array[0].length > start*2 && array.length > start*2){
			PrintMatrixInCircle(array,start);
			start++;
		}
	}

	private static void PrintMatrixInCircle(int[][] array, int start) {
		//打印上横
		for (int i = start; i < array[0].length-start; i++) {
			System.out.println(array[start][i] + "、");
		}
		//打印右竖
		if(start < array.length - 1 - start){
			for (int i = start + 1; i < array.length - 1 - start; i++) {
				System.out.println(array[i][array[0].length - 1 - start] + "、");
			}
		}
		//打印下横
		if(start < array[0].length - 1 - start && start < array.length - 1 - start){
			for (int i = array.length - 1 - start; i > start; i--) {
				System.out.println(array[array.length - start - 1][i] + "、");
			}
		}
		//打印左竖
		if(start < array.length - 1 - start && start < array[0].length - 1 - start){
			for (int i = array.length - 1 - start; i > start; i--) {
				System.out.println(array[i][start] + "、");
			}
		}
		
	}

}

面试题21

import java.util.Stack;

/**
题目大致为:
    定义栈的数据结构,在给类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)。
思路:
    可以建一个辅助的栈,在插入的过程中,插入栈1,同时在插入辅助栈的过程中要求与栈中的元素比较,
    若小于栈顶元素,则插入该元素,若大于栈顶元素,则继续插入栈顶元素。
 * @author acer
 *
 */
public class _21MinInStack {

	public static void main(String[] args) {
		StackWithMin s = new StackWithMin();
		s.push(3);
		s.push(4);
		s.push(2);
		System.out.println(s.min());
		s.push(1);
		System.out.println(s.min());
		s.pop();
		System.out.println(s.min());
		s.push(6);
		s.pop();
		System.out.println(s.min());
		
	}

}

class StackWithMin{
	private Stack<Integer> stack;
	private Stack<Integer> stackHelp;
	
	public StackWithMin() {
		stack = new Stack<>();
		stackHelp = new Stack<>();
	}
	
	//入栈
	public void push(int t){
		stack.push(t);
		//插入辅助的栈
		if(stackHelp.size() == 0 || t < stackHelp.peek()){
			stackHelp.push(t);
		}else{
			stackHelp.push(stackHelp.peek());
		}
	}
	
	//出栈
	public int pop(){
		assert(stack.size() > 0 && stackHelp.size() > 0);
		
		stackHelp.pop();
		return stack.pop();
	}
	
	//取得最小值
	public int min(){
		assert (stack.size() > 0 && stackHelp.size() > 0);
		return stackHelp.peek();
	}
}

面试题22

import java.util.Stack;

/**
题目大致为:
    输入两个整数序列,第一个序列表示栈的压入顺序,判断第二个序列是否为该栈的弹出顺序。
思路:
    主要分为这样的几种情况:首先判断两个序列的长度是否相等,若相等且大于0,则利用辅助栈模拟入栈和出栈。
    如果栈为空,则入栈,此时若栈顶元素与出栈序列的第一个元素相等,则出栈,否则继续入栈,最后判断栈是否为空且出栈序列所有的元素都遍历完。
 * @author acer
 *
 */
public class _22StackPushPopOrder {

	public static void main(String[] args) {
		// 第一组  
        int pushArray_1[] = { 1, 2, 3, 4, 5 };  
        int popArray_1[] = { 4, 5, 3, 2, 1 };  
        System.out.println("第一组:" + isPopOrder(pushArray_1, popArray_1));  
        
        // 第二组  
        int pushArray_2[] = { 1, 2, 3, 4, 5 };  
        int popArray_2[] = { 4, 3, 5, 1, 2 };  
        System.out.println("第二组:" + isPopOrder(pushArray_2, popArray_2));  
  
        // 第三组,主要长度不等  
        int pushArray_3[] = { 1, 2, 3, 4, 5 };  
        int popArray_3[] = { 4, 5, 3 };  
        System.out.println("第三组:" + isPopOrder(pushArray_3, popArray_3));

	}

	//判断序列popArray是否为pushArray的出栈序列 
	public static boolean isPopOrder(int[] pushArray,int[] popArray){
		boolean flag = false;
		// 能够执行的条件是这样的序列不为空,而且两个序列的长度是相等的  
		if(pushArray.length > 0 && pushArray.length == popArray.length){
			//构造一个辅助栈,模拟入栈和出栈
			Stack<Integer> stackHelp = new Stack<>();
			int i = 0;
			int j = 0;
			//保证入栈序列全部进栈
			while(i < pushArray.length){
				//当栈非空时,若栈顶元素与出栈序列中的元素相同,则出栈
				if(stackHelp.size() > 0 && stackHelp.peek() == popArray[j]){
					stackHelp.pop();
					j++;
				}else{
					//若不相同,或者栈为空,则在入栈序列中继续增加
					stackHelp.push(pushArray[i]);
					i++;
				}
			}
			//此时栈中还有元素需要与出栈序列对比
			while(stackHelp.size() > 0){
				//若相等则出栈
				if(stackHelp.peek() == popArray[j]){
					stackHelp.pop();
					j++;
				}else{
					//不想等则直接退出
					break;
				}
			}
			// 最终如果栈是空的,而且popArray中的所有数都遍历了,则是出栈序列  
			if(stackHelp.isEmpty() && j == popArray.length){
				flag = true;
			}
		}
		return flag;
	}
}

面试题23

import java.util.LinkedList;
import java.util.Queue;

public class _23PrintFromTopToBottom {

	public static void main(String[] args) {
		// 构建二叉树  
        BinaryTreeNode root = new BinaryTreeNode(8);  
        BinaryTreeNode t1 = new BinaryTreeNode(6);  
        BinaryTreeNode t2 = new BinaryTreeNode(10);  
        BinaryTreeNode t3 = new BinaryTreeNode(5);  
        BinaryTreeNode t4 = new BinaryTreeNode(7);  
        BinaryTreeNode t5 = new BinaryTreeNode(9);  
        BinaryTreeNode t6 = new BinaryTreeNode(11);  
        root.setLeft(t1);  
        root.setRight(t2);  
        t1.setLeft(t3);  
        t1.setRight(t4);  
        t2.setLeft(t5);  
        t2.setRight(t6);  
        t3.setLeft(null);  
        t3.setRight(null);  
        t4.setLeft(null);  
        t4.setRight(null);  
        t5.setLeft(null);  
        t5.setRight(null);  
        t6.setLeft(null);  
        t6.setRight(null);
        
        // 层次遍历  
        System.out.println("层次遍历序列:");  
        PrintFromTopToBottom(root);

	}
	
	public static void PrintFromTopToBottom(BinaryTreeNode root){
		//使用队列
		Queue<BinaryTreeNode> queue = new LinkedList<>();
		//根节点入队
		queue.add(root);
		
		while(!queue.isEmpty()){
			//去除队列的头
			BinaryTreeNode treeNode = queue.poll();
			System.out.println(treeNode.getValue() + "、");
			
			//左孩子不为空
			if(treeNode.getLeft() != null){
				queue.add(treeNode.getLeft());
			}
			
			//右孩子不为空
			if(treeNode.getRight() != null){
				queue.add(treeNode.getRight());
			}
		}
	}
}

面试题24

import java.util.Arrays;

/**
题目大致为:
    输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。
思路:
    主要考察的是二叉搜索树和后序遍历的性质,其中后序遍历的最后一个数是根结点,在二叉搜索树中,左子树的值都小于根结点,
    右子树的值都大于跟结点,这样便能构造左右子树的序列,用同样的方法分别处理左右子树序列,这便是一个递归的问题。
 * @author acer
 *
 */
public class _24SquenceOfBST {

	public static void main(String[] args) {
		int[] array = {5,7,6,9,11,10,8};
		System.out.println(VerifySquenceOfBST(array));
		
		int[] array2 = {7,4,6,5};
		System.out.println(VerifySquenceOfBST(array2));
	}

	public static boolean VerifySquenceOfBST(int squence[]){
		int length = squence.length;
		if(squence == null && length <= 0){
			return false;
		}
		//序列最后一个数为二叉树的根节点
		int root = squence[length - 1];
		
		//二叉搜索树左子树节点小于根节点
		int i;
		for (i = 0; i < length - 1; ++i) {
			if(squence[i] > root){
				break;
			}
		}
		//二叉搜索树右子树节点大于根节点
		int j;
		for (j = i; j < length - 1; ++j) {
			if(squence[j] < root){
				return false;
			}
		}

		// 左子树结点的个数  
        int leftNum = i;  
        // 构造左子树的序列  
        int left[] = Arrays.copyOfRange(squence, 0, leftNum);  
        // 构造右子树的序列  
        int right[] = Arrays.copyOfRange(squence, leftNum, length - 1);  
        boolean leftBool = true;  
        boolean rightBool = true;  
        // 当左子树的序列存在时  
        if (left.length > 0) {  
            leftBool = VerifySquenceOfBST(left);  
        }  
        // 当右子树的序列存在时  
        if (right.length > 0) {  
            rightBool = VerifySquenceOfBST(right);  
        }  
        return (leftBool && rightBool); 
	}
}

面试题25

import java.util.Stack;
/**
题目:输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶结点所经过的结点形成一条路径。 
 * @author acer
 *
 */
public class _25PathInTree {

	public static void main(String[] args) {
		BinaryTreeNode root = new BinaryTreeNode(10);
		BinaryTreeNode node2 = new BinaryTreeNode(5);
		BinaryTreeNode node3 = new BinaryTreeNode(12);
		BinaryTreeNode node4 = new BinaryTreeNode(4);
		BinaryTreeNode node5 = new BinaryTreeNode(7);
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(null);
		node3.setRight(null);
		node4.setLeft(null);
		node4.setRight(null);
		node5.setLeft(null);
		node5.setRight(null);
		
		FindPath(root, 22);
		
	}
	
	public static void FindPath(BinaryTreeNode root,int sum){
		if(root == null){
			return;
		}
		Stack<Integer> stack = new Stack<Integer>();
		int currentSum = 0;
		FindPath(root,sum,stack,currentSum);
	}

	private static void FindPath(BinaryTreeNode root, int sum, Stack<Integer> stack, int currentSum) {
		currentSum += root.getValue();
		stack.push(root.getValue());
		//如果是叶节点
		if(root.getLeft() == null && root.getRight() == null){
			//当前路径长度等于输入路径长度
			if(currentSum == sum){
				System.out.println("找到同一条路径");
				for (int path : stack) {
					System.out.println(path + " ");
				}
			}
			System.out.println();
		}
		//如果不是叶节点,则遍历它的子节点
		if(root.getLeft() != null){
			FindPath(root.getLeft(), sum, stack, currentSum);
		}
		if(root.getRight() != null){
			FindPath(root.getRight(), sum, stack, currentSum);
		}
		stack.pop();
	}

}

面试题26

/**
题目:实现函数复制一个复杂链表。在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个指向链表中任意结点或 null。  
 * @author acer
 *
 */
public class _26CopyComplexList {

	public static void main(String[] args) {
		ComplexListNode root=new ComplexListNode(1);  
        ComplexListNode node1=new ComplexListNode(2);  
        ComplexListNode node2=new ComplexListNode(3);  
        ComplexListNode node3=new ComplexListNode(4);  
        ComplexListNode node4=new ComplexListNode(5); 
        root.setNext(node1);
        node1.setNext(node2);
        node2.setNext(node3);
        node3.setNext(node4);
        root.setSibling(node2);
        node1.setSibling(node4);
        node3.setSibling(node1);
        ComplexListNode result = Clone(root);
        System.out.println(result.getSibling().getValue());

	}
	
	public static ComplexListNode Clone(ComplexListNode head){
		cloneNode(head);
		connectSiblingNodes(head);
		return reconnectNode(head);
	}

	private static void cloneNode(ComplexListNode head){
		ComplexListNode node = head;
		while(node != null){
			ComplexListNode cloneNode = new ComplexListNode();
			cloneNode.setValue(node.getValue());
			cloneNode.setNext(node.getNext());
			cloneNode.setSibling(null);
			node.setNext(cloneNode);
			node = cloneNode.getNext();
		}
	}
	
	private static void connectSiblingNodes(ComplexListNode head){
		ComplexListNode node = head;
		while(node != null){
			ComplexListNode cloneNode = node.getNext();
			if(node.getSibling() != null){
				cloneNode.setSibling(node.getSibling().getNext());
			}
			node = cloneNode.getNext();
		}
	}
	
	private static ComplexListNode reconnectNode(ComplexListNode head){
		ComplexListNode node = head;
		ComplexListNode cloneHead = null;
		ComplexListNode cloneNode = null;
		
		if(node != null){
			cloneNode = node.getNext();
			cloneHead = cloneNode;
			node.setNext(cloneNode.getNext());
			node = node.getNext();
		}
		
		while(node != null){
			cloneNode.setNext(node.getNext());
			cloneNode = cloneNode.getNext();
			node.setNext(cloneNode.getNext());
			node = node.getNext();
		}
		return cloneHead;
	}
}

class ComplexListNode{
	private int value;
	private ComplexListNode next;
	private ComplexListNode sibling;
	
	public ComplexListNode() {
		super();
	}

	public ComplexListNode(int value) {
		super();
		this.value = value;
	}

	public ComplexListNode(int value, ComplexListNode next, ComplexListNode sibling) {
		super();
		this.value = value;
		this.next = next;
		this.sibling = sibling;
	}

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
	}

	public ComplexListNode getNext() {
		return next;
	}

	public void setNext(ComplexListNode next) {
		this.next = next;
	}

	public ComplexListNode getSibling() {
		return sibling;
	}

	public void setSibling(ComplexListNode sibling) {
		this.sibling = sibling;
	}
	
}

面试题27

/**
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求:不能创建任何新的结点,只能调整树中结点指针的指向。  
 * @author acer
 *
 */
public class _27ConvertBinarySearchTree {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}
	
	public static BinaryTreeNode Convert(BinaryTreeNode root){
		BinaryTreeNode lastNodeInList = null;
		ConvertNode(root,lastNodeInList);
		//lastNodeInList指向双向链表的尾节点
		//我们需要返回头节点
		BinaryTreeNode headOfList = lastNodeInList;
		while(headOfList != null && headOfList.getLeft() != null){
			headOfList = headOfList.getLeft();
		}
		return headOfList;
	}

	private static void ConvertNode(BinaryTreeNode root, BinaryTreeNode lastNodeInList) {
		if(root == null){
			return;
		}
		BinaryTreeNode current = root;
		//遍历左子树
		if(current.getLeft() != null){
			ConvertNode(root.getLeft(), lastNodeInList);
		}
		
		current.setLeft(lastNodeInList);
		if(lastNodeInList != null){
			lastNodeInList.setRight(current);
		}
		lastNodeInList = current;
		
		//遍历右子树
		if(current.getRight() != null){
			ConvertNode(root.getRight(), lastNodeInList);
		}
	}

}

面试题28

/**
题目:输入一个字符串,打印出该字符串中字符的所有排列。
 * @author acer
 *
 */
public class _28StringPermutation {

	public static void main(String[] args) {
		String str = "abcd";
		permutation(str);
		
	}
	
	public static void permutation(String str){
		int count = 0;
		if(str == null){
			return;
		}
		char[] chs = str.toCharArray();
		int point = 0;
		System.out.println(chs);
		System.out.println(" ");
		count++;
		
		char temp1 = chs[point];
		chs[point] = chs[++point];
		chs[point] = temp1;
		
		while(!String.valueOf(chs).equals(str)){
			System.out.println(chs);
			System.out.println(" ");
			count++;
			if(point == chs.length - 1){
				char temp = chs[point];
				chs[point] = chs[0];
				chs[0] = temp;
				point = 0;
			}else{
				char temp = chs[point];
				chs[point] = chs[++point];
				chs[point] = temp;
			}
		}
		System.out.println("count = " + count);
	}
}

面试题29

/**
题目:数组中有一个数字出现次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组 {1,2,3,2,2,2,5,4,2}。
2出现的次数超过数组长度的一半,因此输出2. 
 * @author acer
 *
 */
public class _29MoreThanHalfNumber {

	public static void main(String[] args) {
		int[] array = {1,2,3,2,2,2,5,4,2};
		System.out.println(moreThanHalfNum(array));
	}
	
	public static Integer moreThanHalfNum(int[] array){
		if(checkInvalidArray(array)){
			return 0;
		}
		int result = array[0];
		int times = 1;
		for (int i = 1; i < array.length; ++i) {
			if(times == 0){
				result = array[i];
				times = 1;
			}else if(array[i] == result){
				times++;
			}else{
				times--;
			}
		}
		if(!checkMoreThanHalf(array, result)){
			result = 0;
		}
		return result;
	}

	//判断输入是否为null
	private static boolean inputInvalid = false;
	private static boolean checkInvalidArray(int[] array) {
		inputInvalid = false;
		if(array == null || array.length <= 0){
			inputInvalid = true;
		}
		return inputInvalid;
	}
	
	//判断是否有一个数的数量超过数组长度一半
	private static boolean checkMoreThanHalf(int[] array,int number){
		int times = 0;
		for (int i = 0; i < array.length; ++i) {
			if(array[i] == number){
				times++;
			}
		}
		
		boolean isMoreThanHalf = true;
		if(times*2 <= array.length){
			inputInvalid = true;
			isMoreThanHalf = false;
		}
		return isMoreThanHalf;
	}
}

面试题30

import java.util.Iterator;
import java.util.TreeSet;

/**
题目:最小的K个数
 * @author acer
 *
 */
public class _30KLeastNumbers {

	public static void main(String[] args) {
		int[] array = {4,5,1,6,2,7,3,8};
//		final int k = 1;
//		final int k = 8;
		final int k = 4;
		TreeSet<Integer> treeSet = getLeastNumbers(array, k);
		//遍历输出
		Iterator<Integer> it = treeSet.iterator();
		System.out.println("最小的" + k + "个数为:");
		while(it.hasNext()){
			System.out.println(it.next() + "、");
		}

	}

	public static TreeSet<Integer> getLeastNumbers(int[] array,int k){
		TreeSet<Integer> treeSet = new TreeSet<>();
		//判断k和array的合法性
		if(array == null || k <= 0){
			return null;
		}
		
		for (int i = 0; i < array.length; i++) {
			if(treeSet.size() < k){//如果set中元素小于k个,直接插入
				treeSet.add(array[i]);
			}else{
				if(treeSet.last() > array[i]){
					treeSet.pollLast();
					treeSet.add(array[i]);
				}
			}
		}
		return treeSet;
	}
}

面试题31

/**
题目大致为:
    输入一个整型数组,数组里有正数也有负数。数组中一个或者连续的多个整数组成一个字数组。求所有字数组的和的最大值。要求时间复杂度为O(n)。
思路:
    因为时间复杂度为O(n),则只能遍历一次数组,这里同时使用两个变量currentSum和finalGreatSum,其中currentSum保存的是当前的和,
    若currentSum<0,则从下一个位置从新记录,finalGreatSum记录的是历史的最小值,只有当currentSum>finalGreatSum时
    用currentSum替换finalGreatSum。
 * @author acer
 *
 */
public class _31GreatestSumOfSubarrays {

	public static void main(String[] args) {
		int[] array = {1,-2,3,10,-4,7,2,-5};
		int[] array1 = {-3,-1,-2,-4};
		System.out.println(findGreatestSumOfSubarray(array));//18
		System.out.println(findGreatestSumOfSubarray(array1));//-1

	}

	private static boolean invalidInput = false;
	
	public static int findGreatestSumOfSubarray(int[] array){
		if(array == null || array.length <= 0){
			invalidInput = true;
			return 0;
		}
		
		invalidInput = false;
		
		int currentSum = 0;
		int greatestSum = 0x80000000;
		for (int i = 0; i < array.length; i++) {
			if(currentSum < 0){
				currentSum = array[i];
			}else{
				currentSum += array[i];
			}
			if(currentSum > greatestSum){
				greatestSum = currentSum;
			}
		}
		return greatestSum;
	}
}

面试题32

/**
 * 题目:输入一个整数 n,求从 1 到 n 这 n 个整数的十进制表示中 1 出现的次 数。例如输入 12,这些整数中包含 1 的数字有
 * 1,10,11,12,1 一共出现了 5 次。 
 * 解题思路:解法二告诉我们 1~ N 中"1"的个数跟最高位有关,那我们换个角 度思考,给定一个N,我们分析 1~N 中的数在每一位上出现 1 的次数的和,
 * 看看 每一位上"1"出现的个数的和由什么决定。 1 位数的情况:在解法二中已经分析过,大于等于
 * 1 的时候,有 1 个,小于 1 就 没有。 2 位数的情况:N=13,个位数出现的 1 的次数为 2,分别为 1 和 11,十位数出现 1 的次数为
 * 4,分别为 10,11,12,13,所以 f(N) = 2+4。N=23,个位数出现的 1 的
 * 次数为3,分别为1,11,21,十位数出现1的次数为10,分别为10~19,f(N)=3+10。 由此我们发现,个位数出现 1
 * 的次数不仅和个位数有关,和十位数也有关,如果 个位数大于等于 1,则个位数出现 1 的次数为十位数的数字加 1;如果个位数为 0,个位数出现 1
 * 的次数等于十位数数字。而十位数上出现 1 的次数也不仅和十 位数相关,也和个位数相关:如果十位数字等于 1,则十位数上出现 1 的次数为 个位数的数字加
 * 1,假如十位数大于 1,则十位数上出现 1 的次数为 10。 3 位数的情况: N=123,个位出现 1 的个数为
 * 13:1,11,21,…,91,101,111,121。十位出现 1 的 个数为 20:10~19,110~119。百位出现 1 的个数为
 * 24:100~123。 我们可以继续分析 4 位数,5 位数,推导出下面一般情况: 假设 N,我们要计算 百位上出现 1
 * 的次数,将由三部分决定:百位上的数字,百位以上的数字,百位 一下的数字。 如果百位上的数字为 0,则百位上出现 1 的次数仅由更高位决定,比如
 * 12013, 百位出现 1 的情况为 100~199,1100~1199,2100~2199,…,11100~11199,共 1200
 * 个。等于更高位数字乘以当前位数,即 12 * 100。 如果百位上的数字大于 1,则百位上出现 1 的次数仅由更高位决定,比如 12213, 百位出现 1
 * 的情况为 100~199,1100~1199,2100~2199,…,11100~11199, 12100~12199 共 1300 个。等于更高位数字加
 * 1 乘以当前位数,即(12 + 1)*100。 如果百位上的数字为 1,则百位上出现 1 的次数不仅受更高位影响,还受低位影 响。例如
 * 12113,受高位影响出现 1 的情况:100~199,1100~1199,2100~2199,…, 11100~11199,共 1200
 * 个,但它还受低位影响,出现 1 的情况是 12100~12113, 共 114 个,等于低位数字 113+1。
 * 
 * @author acer
 *
 */
public class _32NumberOf1 {

	public static void main(String[] args) {
		System.out.println(numberOf1Between1AndN(12));
		System.out.println(numberOf1Between1AndN(5));
		System.out.println(numberOf1Between1AndN(10));
		System.out.println(numberOf1Between1AndN(55));
		System.out.println(numberOf1Between1AndN(99));
		System.out.println(numberOf1Between1AndN(0));
		System.out.println(numberOf1Between1AndN(1));
		System.out.println(numberOf1Between1AndN(10000));
		System.out.println(numberOf1Between1AndN(21235));

	}
	
	public static int numberOf1Between1AndN(int n){
		int number = 0;
		for (int i = 1; i <= n; ++i) {
			number += NumberOf1(i);
		}
		return number;
	}

	private static int NumberOf1(int n) {
		int number = 0;
		while(n > 0){
			if(n % 10 == 1){
				number++;
			}
			n = n / 10;
		}
		return number;
	}
}

面试题33

/**
题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼  
接出的所有数字的最小的一个。例如输入 {3,32,321},则打印最小的数字是  321323. 
 * @author acer
 *
 */
public class _33SortArrayForMinNumber {

	public static void main(String[] args) {
		int[] array = {3,32,321};
		printMin(array);

	}

	public static void printMin(int[] array){
		if(array == null || array.length <= 0){
			return;
		}
		int[] clone = array.clone();
		printMinNumber(clone,0,clone.length - 1);
		for (int i : clone) {
			System.out.print(i + " ");
		}
	}

	private static void printMinNumber(int[] array, int start, int end) {
		if(start < end){
			int main_number = array[end];
			int small_cur = start;
			for (int j = start; j < end; j++) {
				if(isSmall(String.valueOf(array[j]),String.valueOf(main_number))){
					int tmp = array[j];
					array[j] = array[small_cur];
					array[small_cur] = tmp;
					small_cur++;
				}
			}
			array[end] = array[small_cur];
			array[small_cur] = main_number;
			printMinNumber(array,0,small_cur-1);
			printMinNumber(array, small_cur+1, end);
		}
	}

	private static boolean isSmall(String m, String n) {
		String left = m + n;
		String right = n + m;
		boolean result = false;
		for (int i = 0; i < left.length(); i++) {
			if(left.charAt(i) < right.charAt(i)){
				return true;
			}else if(left.charAt(i) > right.charAt(i)){
				return false;
			}
		}
		return result;
	}
}

面试题34

/**
题目大致为:
    丑数的定义为:只包含因子2,3和5的数。求按从小到大的顺序的第1500个丑数。约定:1当做第一个丑数。
思路:
    设置三个指针分别代表该位置*2,*3和*5,并将这三个数中的最小值插入数组中,若当前位置的值*对应的因子<=刚插入的值,
    便将该指针后移,直到新的位置上的值*对应的因子>刚插入的值。
 * @author acer
 */
public class _34UglyNumber {

	public static void main(String[] args) {
		int index = 1500;
		long t1 = System.currentTimeMillis();
		System.out.println("第" + index + "个丑数为" + getUglyNumber(index));
		long t2 = System.currentTimeMillis();
		System.out.println(t2 - t1);
		
		
		long t3 = System.currentTimeMillis();
		System.out.println("第" + index + "个丑数为" + getUglyNum(index));
		long t4 = System.currentTimeMillis();
		System.out.println(t4 - t3);
	}

	//算法一:效率不高
	public static int getUglyNumber(int index){
		if(index <= 0){
			return 0;
		}
		int number = 0;
		int foundUgly = 0;
		while(foundUgly < index){
			++number;
			if(isUgly(number)){
				foundUgly++;
			}
		}
		return number;
	}
	
	private static boolean isUgly(int number){
		while(number % 2 == 0){
			number /= 2;
		}
		while(number % 3 == 0){
			number /= 3;
		}
		while(number % 5 == 0){
			number /= 5;
		}
		return (number == 1) ? true : false;
	}
	
	/*************************************************************/
	//算法二:创建数组保持已经找到的丑数,用空间换时间的解法
	public static int getUglyNum(int index){
		if(index <= 0){
			return 0;
		}
		//为了便于存储,建立数组保存中间结果
		int[] tmp = new int[index];
		tmp[0] = 1;
		//记录三组数的位置
		int multi2 = 0;
		int multi3 = 0;
		int multi5 = 0;
		
		int nextUglyNum = 1;
		while(nextUglyNum < index){
			int min = findMin(tmp[multi2] * 2,tmp[multi3] * 3,tmp[multi5] * 5);
			tmp[nextUglyNum] = min;
			//重新计算multi2,multi3,multi5
			while(tmp[multi2] * 2 <= tmp[nextUglyNum]){
				multi2++;
			}
			while(tmp[multi3] * 3 <= tmp[nextUglyNum]){
				multi3++;
			}
			while(tmp[multi5] * 5 <= tmp[nextUglyNum]){
				multi5++;
			}
			nextUglyNum++;
		}
		return tmp[index - 1];
	}

	private static int findMin(int i, int j, int k) {
		int minTmp = (i < j) ? i : j;
		return (minTmp < k ? minTmp : k);
	}
	
}

面试题35

import java.util.LinkedHashMap;
/**
题目大致为:
    在字符串中找出第一个只出现一次的字符。
思路:
    在Java中可以把字符串转换成字符数组处理,可以使用HashMap的数据结构存储,其中key为字符,value为对应出现的次数,
    这样通过两次遍历字符数组就可以找出,其中,第一次是构建HashMap,第二次是对每个字符判断其HashMap中对应的value的值是否为1。
 * @author acer
 */
public class _35FirstNotRepeatingChar {

	public static void main(String[] args) {
		String str = "abaccdeff";
		String str2 = "ababccddefef";
		String str3 = "abcdefg";
		
		System.out.println(firstNotRepeatingChar(str));
		System.out.println(firstNotRepeatingChar(str2));
		System.out.println(firstNotRepeatingChar(str3));

	}

	//用哈希表存储字符出现的次数
	public static Character firstNotRepeatingChar(String s){
		if(s == null){
			return null;
		}
		char[] c = s.toCharArray();
		char tmp = 0;
		LinkedHashMap<Character, Integer> map = new LinkedHashMap<>();
		//第一次遍历:在哈希表中插入字符出现的次数
		for (int i = 0; i < c.length; i++) {
			//哈希表中已经存在key
			if(map.containsKey(c[i])){
				int value = map.get(c[i]);
				map.put(c[i], value + 1);
			}else{
				map.put(c[i], 1);
			}
		}
		//第二次遍历:插入完毕后依次搜索
		for (int i = 0; i < c.length; i++) {
			if(map.get(c[i]) == 1){
				tmp = c[i];
				break;
			}
		}
		return tmp;
	}
}

面试题36

/**
题目:在数组中的两个数字如果前一个数字大于后一个数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组的逆序对的总数。
例如在数组 {7,5,6,4}中,一共存在 5 个逆序对,分别是(7,6)、(7、5),(7、4),(6、4),  (5、4)。  
 * @author acer
 *
 */
public class _36InversePairs {

	public static void main(String[] args) {
		 int[] array= {7,5,6,4};  
		 System.out.println(inversePairs(array));

	}

	public static int inversePairs(int[] array){
		if(array == null){
			return 0;
		}
		int[] clone = array.clone();
		return mergeSort(array,clone,0,array.length - 1);
	}

	private static int mergeSort(int[] array, int[] result, int start, int end) {
		if(start == end){
			result[start] = array[start];
			return 0;
		}
		int length = (end - start) / 2;
		int left = mergeSort(result,array,start,start + length);
		int right = mergeSort(result,array,start + length + 1,end);
		//leftIndex初始化为前半段最后一个数字的下标
		int leftIndex = start + length;
		//rightIndex初始化为后半段最后一个数字的下标
		int rightIndex = end;
		
		int count = 0;
		int indexCopy = end;
		
		while(leftIndex >= start && rightIndex >= start + length + 1){
			if(array[leftIndex] > array[rightIndex]){
				result[indexCopy--] = array[leftIndex--];
				count += rightIndex - start - length;
			}else{
				result[indexCopy--] = array[rightIndex--];
			}
		}
		for (int i = leftIndex; i >= start; i--) {
			result[indexCopy--] = array[i];
		}
		for (int j = rightIndex; j >= start + length + 1 ; j--) {
			result[indexCopy--] = array[j];
		}
		return left + right + count;
	}
}

面试题37

/**
题目:输入两个链表,找出它们的第一个公共结点。
 * @author acer
 *
 */
public class _37FirstCommonNodesInLists {

	public static void main(String[] args) {
		// 构建链表  
        ListNode head1 = new ListNode(1);  
        ListNode node_2 = new ListNode(2);  
        ListNode node_3 = new ListNode(3);  
        ListNode head2 = new ListNode(4);  
        ListNode node_5 = new ListNode(5);  
        ListNode node_6 = new ListNode(6);  
        ListNode node_7 = new ListNode(7);  
        head1.setNext(node_2);  
        node_2.setNext(node_3);  
        node_3.setNext(node_6);  
        node_6.setNext(node_7);  
        node_7.setNext(null);  
        head2.setNext(node_5);  
        node_5.setNext(node_6);  
        
        ListNode first = findFirstCommonNode(head1, head2);
        System.out.println(first.getValue());

	}

	public static ListNode findFirstCommonNode(ListNode head1,ListNode head2){
		//得到两个链表的长度,赋初值
		int length1 = getListLength(head1);
		int length2 = getListLength(head2);
		int lengthDif = length1 - length2;
		ListNode listHeadLong = head1;
		ListNode listHeadShort = head2;
		
		if(length2 > length1){
			listHeadLong = head2;
			listHeadShort = head1;
			lengthDif = length2 - length1;
		}
		
		//先在长链表上走几步,再同时在两个链表上遍历
		for (int i = 0; i < lengthDif; i++) {
			listHeadLong = listHeadLong.getNext();
		}
		
		while(listHeadLong != null && listHeadShort != null && listHeadLong != listHeadShort){
			listHeadLong = listHeadLong.getNext();
			listHeadShort = listHeadShort.getNext();
		}
		
		//得到第一个公共结点
		ListNode firstCommonNode = listHeadLong;
		
		return firstCommonNode;
	}
	
	private static int getListLength(ListNode head){
		int length = 0;
		ListNode node = head;
		while(node != null){
			++length;
			node = node.getNext();
		}
		return length;
	}
}

面试题38

/**
题目大致为:
    统计一个数字在排序数组中出现的次数。
思路:
    由于是排序数组,要查找其中的一个数字,最简便的方法便是折半查找,这样我们可以先查找最先出现的位置和最后出现的位置,
    数出中间的个数便为总共的出现次数。
 * @author acer
 *
 */
public class _38NumberOfK {

	public static void main(String[] args) {
		int[] array = {1,2,3,3,3,4,4,4,5,6};
		System.out.println(getNumberOfK(array, 3));

	}
	
	public static int getNumberOfK(int[] array,int k){
		int number = 0;
		if(array != null && array.length > 0){
			int firstK = getFirstK(array, k, 0, array.length - 1);
			int lastK = getLastK(array, k, 0, array.length - 1);
			if(firstK > -1 && lastK > -1){
				number = lastK - firstK + 1;
			}
		}
		return number;
	}

	//得到第一个K
	private static int getFirstK(int[] array,int k,int start,int end){
		if(start > end){
			return -1;
		}
		int mid = (start + end) / 2;
		
		if(array[mid] == k){
			if((mid > 0 && array[mid - 1] != k) || mid == 0 ){
				return mid;
			}else{
				end = mid - 1;
			}
		}else if(array[mid] > k){
			end = mid - 1;
		}else{
			start = mid + 1;
		}
		
		return getFirstK(array,k,start,end);
	}
	
	//得到最后一个K
	private static int getLastK(int[] array,int k,int start,int end){
		if(start > end){
			return -1;
		}
		int mid = (start + end) / 2;
		
		if(array[mid] == k){
			if((mid > 0 && array[mid + 1] != k) || mid == array.length - 1 ){
				return mid;
			}else{
				start = mid + 1;
			}
		}else if(array[mid] > k){
			end = mid - 1;
		}else{
			start = mid + 1;
		}
		
		return getLastK(array,k,start,end);
	}
}

面试题39

public class _39_1_TreeDepth {

	public static void main(String[] args) {
		BinaryTreeNode root = new BinaryTreeNode(1);
		BinaryTreeNode node2 = new BinaryTreeNode(2);
		BinaryTreeNode node3 = new BinaryTreeNode(3);
		BinaryTreeNode node4 = new BinaryTreeNode(4);
		BinaryTreeNode node5 = new BinaryTreeNode(5);
		BinaryTreeNode node6 = new BinaryTreeNode(6);
		BinaryTreeNode node7 = new BinaryTreeNode(7);
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(null);
		node3.setRight(node6);
		node4.setLeft(null);
		node4.setRight(null);
		node5.setLeft(node7);
		node5.setRight(null);
		node6.setLeft(null);
		node6.setRight(null);
		node7.setLeft(null);
		node7.setRight(null);
		
		System.out.println(treeDepth(root));

	}

	public static int treeDepth(BinaryTreeNode root){
		if(root == null){
			return 0;
		}
		int left = treeDepth(root.getLeft());
		
		int right = treeDepth(root.getRight());
		
		return (left > right) ? (left + 1) : (right + 1);
	}
}

/**
题目二:输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过1,那么他就是一棵平衡二叉树。  
 * @author acer
 *
 */
public class _39_2_BalancedBinaryTree {

	public static void main(String[] args) {
		BinaryTreeNode root = new BinaryTreeNode(1);
		BinaryTreeNode node2 = new BinaryTreeNode(2);
		BinaryTreeNode node3 = new BinaryTreeNode(3);
		BinaryTreeNode node4 = new BinaryTreeNode(4);
		BinaryTreeNode node5 = new BinaryTreeNode(5);
		BinaryTreeNode node6 = new BinaryTreeNode(6);
		BinaryTreeNode node7 = new BinaryTreeNode(7);
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(null);
		node3.setRight(node6);
		node4.setLeft(null);
		node4.setRight(null);
		node5.setLeft(node7);
		node5.setRight(null);
		node6.setLeft(null);
		node6.setRight(null);
		node7.setLeft(null);
		node7.setRight(null);
		
		System.out.println(isBalanced(root));

	}
	
	public static boolean isBalanced(BinaryTreeNode root){
		int depth = 0;
		return isBalanced(root,depth);
	}

	private static boolean isBalanced(BinaryTreeNode root, int depth) {
		if(root == null){
			depth = 0;
			return true;
		}
		int left = 0;
		int right = 0;
		if(isBalanced(root.getLeft(), left) && isBalanced(root.getRight(), right)){
			int diff = left - right;
			if(diff <= 1 && diff >= -1){
				depth = 1 + (left > right ? left : right);
				return true;
			}
		}
		return false;
	}

}

面试题40

/**
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
要求时间复杂度是 O(n),空间复杂度为 O(1);  
 * @author acer
 */
public class _40NumbersAppearOnce {

	public static void main(String[] args) {
		int[] array = {2,4,3,6,3,2,5,5};
		FindNumsAppearOnce(array);

	}

	public static void FindNumsAppearOnce(int[] array){
		if(array == null || array.length < 2){
			return;
		}
		int result = 0;
		for (int i = 0; i < array.length; i++) {
			result ^= array[i];
		}
		
		int indexOf1 = FindFirstBitIs1(result);
		
		int num1 = 0;
		int num2 = 0;
		for (int j = 0; j < array.length; j++) {
			if(isBit1(array[j],indexOf1)){
				num1 ^= array[j]; //算出其中一个只出现一次的数
			}else{
				num2 ^= array[j];//算出另一个只出现一次的数
			}
		}
		System.out.println(num1);
		System.out.println(num2);
	}

	//判断数组所有元素异或之后的结果第一个为1的位是第几位
	private static int FindFirstBitIs1(int num) {
		int indexBit = 0;
		while((num & 1) == 0){
			num = num >> 1;
			++indexBit;
		}
		return indexBit;
	}
	
	//判断bit位是不是为1
	private static boolean isBit1(int num, int indexBit) {
		num = num >> indexBit;
		return (num & 1) == 0;
	}
}

面试题41

/**
题目一:输一个递增排序的数组和一个数字 s,在数组中查找两个数使得它们的和正好是 s。
如果有多对数字的和等于 s,输出任意一对即可。例如:输入数组 {1,2,4,7,11,15}和数字为 15.输出 4 和 11.  
 * @author acer
 *
 */
public class _41_1_TwoNumbersWithSum {

	public static void main(String[] args) {
		int[] array = {1,2,4,7,11,15};
		System.out.println(findNumbersWithSum(array, 15));

	}

	public static boolean findNumbersWithSum(int[] array,int sum){
		boolean found = false;
		if(array == null){
			return found;
		}
		
		int number1 = 0;
		int number2 = 0;
		//定义两个指针,分别指向头和尾
		int start = 0;
		int end = array.length - 1;
		while(start < end){
			if(array[start] + array[end] == sum){
				number1 = array[start];
				number2 = array[end];
				System.out.println(number1);
				System.out.println(number2);
				found = true;
				return found;
			}else if(array[start] + array[end] < sum){
				start++;
			}else{
				end--;
			}
		}
		return found;
	}
}

/**
题目二:输入一个正数 s,打印出所有和为 s 的连续正数序列(至少含两个数)。  
	例如输入 15,由于 1+2+3+4+5=4+5+6=7+8=15,所以结果打印出 3 个连续序列 1-5、  4-6、和 7-8.  
 * @author acer
 *
 */
public class _41_2_ContinuesSquenceWithSum {

	public static void main(String[] args) {
		findContinuousSequence(15);

	}

	public static void findContinuousSequence(int sum){
		if(sum < 3){
			return ;
		}
		int small = 1;
		int big = 2;
		int mid = (1 + sum) / 2;
		int currentSum = small + big;
		//开始遍历
		while(small < mid){
			if(currentSum == sum){
				printContinuousSequence(small,big);
			}
			while(currentSum > sum && small < big){
				currentSum -= small;
				small++;
				if(currentSum == sum){
					printContinuousSequence(small, big);
				}
			}
			//如果当前和小于目标sum
			big++;
			currentSum += big;
		}
	}

	private static void printContinuousSequence(int small, int big) {
		for (int i = small; i <= big; i++) {
			System.out.print(i);
		}
		System.out.println();
	}
}

面试题42

/**
题目一大致为:
    输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。
思路:
    两次翻转,对于字符串"I am a student.",首先进行第一次翻转,即翻转完为:
    ".tneduts a ma I",然后再对每个单词翻转,最终为:"student. a am I"。
 * @author acer
 */
public class _42_1_ReverseWordsInSentence {

	public static void main(String[] args) {
		String str = "i am a student.";
		reverseSentence(str);
	}

	public static void reverseSentence(String sentence){
		if(sentence == null){
			return;
		}
		String[] str = sentence.split(" ");
		StringBuffer sb = new StringBuffer();
		for (int i = str.length - 1; i >= 0; i--) {
			sb.append(str[i] + " ");
		}
		System.out.println(sb);
	}
}

/**
题目二:字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。  
	请定义一个函数实现字符串左旋转操作的功能。比如输入字符串“abcdefg”和数字 2.该函数左旋转 2 位得到的结果“cdefgab".  
 * @author acer
 *
 */
public class _42_2_LeftRotateString {

	public static void main(String[] args) {
		String str = "abcdefg";
		leftRotateString(str,2);
		leftRotateString(str,0);
		leftRotateString(str,6);
		leftRotateString(str,7);
		leftRotateString(str,8);

	}
	
	public static void leftRotateString(String sentence,int index){
		if(sentence == null || index > sentence.length() || index < 0){
			System.out.println("输入参数有误");
			return;
		}
		String[] splitString = {sentence.substring(0, index),sentence.substring(index, sentence.length())};
		StringBuffer buffer = new StringBuffer();
		for (String string : splitString) {
			buffer.append(reverse(string));//在buffer容器中,前index个字符和后面的字符分别翻转
		}
		String result = reverse(buffer.toString());//整体翻转一遍字符
		System.out.println(result);
	}

	//反转字符串每个字符
	private static String reverse(String string) {
		if(string == null){
			return null;
		}
		char[] array = string.toCharArray();
		for (int i = 0; i < (array.length + 1) / 2; i++) {
			char temp = array[i];
			array[i] = array[array.length-1-i];
			array[array.length-1-i] = temp;
		}
		return String.valueOf(array);
	}

}

面试题43

/**
题目:把 n 个骰子扔在地上,所有骰子朝上一面的点数之和为 s。输入 n,打印出 s 的所有可能的值出现的概率。 
思路:用两个数组来存储骰子点数的每个总数出现的次数。
 * @author acer
 *
 */
public class _43DicesProbability {

	public static void main(String[] args) {
		printProbability(1);
		
	}

	public static void printProbability(int number){  
		if(number < 1)  
			return;  
		int gMaxValue = 6;  
		int[][] probabilities = new int[2][];  
		probabilities[0] = new int[gMaxValue*number+1];  
		probabilities[1] = new int[gMaxValue*number+1];  
		int flag = 0;  
		for(int i = 1;i <= gMaxValue;i++){  
			probabilities[flag][i] = 1;  
		}  
		for(int k = 2;k <= number;++k){  
			for(int i = 0;i < k;i++){  
				probabilities[1-flag][i] = 0;  
			}  
			for(int i = k;i <= gMaxValue * k;i++){  
				probabilities[1-flag][i] = 0;  
				for(int j = 1;j <= i && j <= gMaxValue;j++){
					probabilities[1-flag][i] += probabilities[flag][i-j];  
				}
			}  
			flag = 1 - flag;  
		}  
		double total = Math.pow(gMaxValue, number);  
		for(int i = number;i <= gMaxValue * number;i++){  
			double ratio=(double)probabilities[flag][i] / total;  
			System.out.print(i + " ");  
			System.out.println(ratio);
		
		}  
	}  
}

面试题44

import java.util.Arrays;

/**
题目大致为:
    从扑克牌中随机抽出5张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王可以看成是任意数字。
思路:
    实则本题是判断一个数组是否是连续的,将大、小王看成是0,0可以充当任何的数。这样我的思路是:先对数组排序,排序后统计出0的个数,
    在计算非0数之间的缺少的数字个数的总和,若是在这个过程中发现前后两个的差为0则为相同的元素,则不符合题意;
    在满足题意的情况下,若是0的个数大于等于缺少的数字个数的总和,那么满足条件,否则不满足条件。
 * @author acer
 *
 */
public class _44ContinousCards {

	public static void main(String[] args) {
		int[] array1 = {0,0,1,4,5};
		System.out.println(isContinous(array1));
		
		int[] array2 = {1,2,3,5,6};
		System.out.println(isContinous(array2));
		
		int[] array3 = {1,2,2,3,4};
		System.out.println(isContinous(array3));
	}
	
	public static boolean isContinous(int[] array){
		// 由于数组的规模很小,则可以直接使用库函数完成  
        // 作者有句话很重要:通常我们认为不同级别的时间复杂度只有当n足够大的时候才有意义  
		Arrays.sort(array);
		int numOfZero = 0;
		int numOfGap = 0;
		for (int i = 0; i < array.length - 1; i++) {
			if(array[i] == 0){
				numOfZero++;
			}else{
				if(array[i+1] - array[i] == 0){
					return false;
				}else{
					numOfGap = array[i+1] - array[i] - 1;
				}
			}
		}
		if(numOfZero > numOfGap){//0能填补空缺
			return true;
		}else{//0不能填补空缺
			return false;
		}
		
	}

}

面试题45

public class _45LastNumberInCircle {

	public static void main(String[] args) {
		System.out.println(lastRemaining(5, 1));

	}

	public static int lastRemaining(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;
	}
}

面试题46


面试题47

public class _47AddTwoNumbersWithoutPlus {

	public static void main(String[] args) {
		System.out.println(add(5, 17));

	}

	public static int add(int m,int n){
		int sum = 0;
		int carry = 0;
		do{
			sum = m ^ n;
			carry = (m & n) << 1;
			
			m = sum;
			n = carry;
		}
		while(carry != 0);
		return m;
	}
}

面试题49

/**
题目:创建一个函数,可以把字符串转换为整数
思路:
	可能的输入:
		1 带符号数
		2 无符号数
		3 零
		4 空指针
		5 超出表示范围 – 暂时仅仅是直接退出且设置最小 – 可以考虑此时抛个异常
		6 非法输入,比如并不是一个0-9或者+ -组成的字符串 – 对于非法输入一律返回的是Integer.MIN_VALUE 
 * @author acer
 *
 */
public class _49StringToInt {

	public static void main(String[] args) {
		_49StringToInt test = new _49StringToInt();
		String str1 = "123456";
		String str2 = "-123456";
		String str3 = "-";
		String str4 = "abc";
		String str5 = "";
		String str6 = null;
		
		System.out.println(test.strToInt(str1));
		System.out.println(test.strToInt(str2));
		System.out.println(test.strToInt(str3));
		System.out.println(test.strToInt(str4));
		System.out.println(test.strToInt(str5));
		System.out.println(test.strToInt(str6));
		
	}

	public long strToInt(String str){
		if(str == null)
			return Long.MIN_VALUE;
		if(str.length() == 0)
			return 0;
		
		//判断输入字符是否合法
		for (int i = 0; i < str.length(); i++) {
			if(!judge(str.charAt(i))){
				return Long.MIN_VALUE;
			}
		}
		
		char[] chs = str.toCharArray();
		long result = 0;
		//判断符号
		if(chs[0] == '-' || chs[0] == '+'){
			result = trans(str.substring(1));
		}else{
			result = trans(str);
		}
		
		if(result > 0 && chs[0] == '-'){
			result = - result;
		}
		
		return result;
	}

	private long trans(String str) {
		if(str.length() == 0)
			return 0;
		long result = 0;
		for (int i = 0; i < str.length(); i++) {
			result = result * 10 + (str.charAt(i) - '0');
			if(result > Long.MAX_VALUE){
				result = Long.MIN_VALUE;
				break;
			}
		}
		return result;
	}

	private boolean judge(char c) {
		if(c == '+' || c == '-')
			return true;
		if(c >= '0' && c <= '9')
			return true;
		return false;
	}
}

面试题51

/**
题目:数组中的重复数字
在长度为n的数组里所有的数字都在0到n-1的范围内。数组中某些数字是重复的,找出数组中任意一个重复的数字。
思路:
	法1.先将数组排序,再遍历,找出重复的数字。 O(nlogn)
	法2.用哈希表遍历存储数组中每个数,每扫描一个数字,用O(1)时间判断哈希表里是否已经包含了该数字,如果包含,则找到重复数字
	法3.扫描数组,比较下标为i的数字m是否等于i,
			若相等继续扫描,
			若不等,和下标为m的数字比较,
				若相等,则找到重复数字
				若不等,则交换下标为m的数字和下标为i的数字
 * @author acer
 *
 */
public class _51FindRepeatNumberInArray {  

	public static void main(String[] args) {
		int[] array = {1,3,2,0,2,5,3};
		System.out.println(findRepeatNumber(array));

	}

	public static boolean findRepeatNumber(int[] array){
		if(array == null){
			return false;
		}
		for (int i = 0; i < array.length; i++) {
			if(array[i] < 0 || array[i] > array.length - 1){
				return false;
			}
		}
		int result = 0;
		//依次扫描数组中每个数字
		for (int i = 0; i < array.length; i++) {
			while(array[i] != i){
				if(array[i] == array[array[i]]){
					result = array[i];
					System.out.println(result);
					return true;
				}
				int temp = array[i];
				array[i] = array[temp];
				array[temp] = temp;
			}
		}
		
		return false;
	}

}

面试题52

/**
题目:给定一个数组A[1,2,....n-1],请构建一个数组B[0,1,....n-1]
	其中B中的元素 B[i] = A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1],不能使用除法8 
 * @author acer
 *
 */
public class _52BuildProductArray {

	public static void main(String[] args) {
		double[] a1 = {1,2,3,4,5};
		double[] a2 = {0,0,0,0,0};
		multiply(a1, a2);
	}
	
	public static void multiply(double[] a1,double[] a2){
		int length1 = a1.length;
		int length2 = a2.length;
		
		if(length1 == length2 && length2 > 1){
			a2[0] = 1;
			for (int i = 1; i < a1.length; ++i) {
				a2[i] = a2[i-1] * a1[i-1];
			}
			double temp = 1;
			for (int i = length1 - 2; i >= 0; --i){
				temp *= a1[i+1];
				a2[i] *= temp; 
			}
		}
		for (int i = 0; i < a2.length; i++) {
			System.out.println(a2[i]);
		}
	}

}

面试题53

/**
题目描述:正则表达式匹配 
 *             请实现一个函数用来匹配包含‘.’和‘*’的正则表达式。模式中的字符’.’表示任意一个字符, 
 *          而‘*’表示它前面的字符可以出现任意次(含0次)。本题中,匹配是指字符串的所有字符匹配整个模式。 
 *          例如,字符串“aaa”与模式“a.a”和“ab*ac*a”匹配,但与“aa.a”及“ab*a”均不匹配。 
* 输入描述: 请输入第一个字符串: 
*           aaa 
*           请输入第二个字符串: 
*           ab*a*a 
* 程序输出:匹配的结果为:true 
* 问题分析: 1.string.chatAt(i)表示将字符串中第i个符号转化为字符 
* 算法描述:每次从字符串里拿出一个字符和模式中的字符去匹配。先来分析如何匹配一个字符。如果模式中的字符ch是‘.’, 
*           那么它可以匹配字符串中的任意字符。如果模式中的字符ch不是’.’而且字符串中的字符也是ch,那么他们相互匹配。 
*           当字符串中的字符和模式中的字符相匹配时,接着匹配后面的字符。 
*           相对而言当模式中的第二个字符不是‘*’时问题要简单很多。如果字符串中的第一个字符和模式中的第一个字符相匹配, 
*           那么在字符串和模式上都向后移动一个字符,然后匹配剩余的字符串和模式。如果字符串中的第一个字符和 
*           模式中的第一个字符不相匹配,则直接返回false。 
*         当模式中的第二个字符是‘*’时问题要复杂一些,因为可能有多种不同的匹配方式。一个选择是在模式上向后移动两个字符。 
*           这相当于‘*’和它面前的字符被忽略掉了,因为‘*’可以匹配字符串中0个字符。如果模式中的第一个字符和字符串中的第一个字符相匹配时, 
*           则在字符串向后移动一个字符,而在模式上有两个选择:我们可以在模式上向后移动两个字符,也可以保持模式不变。 
 * @author acer
 */
public class _53MatchingRegularExpression {

	public static void main(String[] args) {
		String str1 = "a.a";
		String str2 = "aaa";
		
		
		System.out.println(match(str1, str2));

	}
	
	public static boolean match(String input,String pattern){
		if(input == null || pattern == null)
			return false;
		return matchCore(input,0,pattern,0);
	}

	private static boolean matchCore(String input, int i, String pattern, int p) {
		if(i >= input.length() && p >= pattern.length()){
			return true;
		}
		if(i != input.length() && p >= pattern.length()){
			return false;
		}
		//模式串未结束,匹配串有可能结束有可能未结束  
        //p的下一个字符为*
		if(p+1 < pattern.length() && pattern.charAt(p+1) == '*'){
			//匹配已经结束
			if(i >= input.length()){
				return matchCore(input, i, pattern, p+2);
			}else{//匹配还没结束
				if(pattern.charAt(p) == input.charAt(i) || pattern.charAt(p) == '.'){
					return
							matchCore(input, i+1, pattern, p+2)//pattern所指的下一个字符为*时
						||	matchCore(input, i+1, pattern, p)//pattern所指的下一个字符为.时
						|| matchCore(input, i, pattern, p+2);
				}else{
					return matchCore(input, i, pattern, p+2);
				}
			}
		}
		
		if(i >= input.length()){  
            return false;  
        }else{          //字符相同或匹配串带.的情况  
            if(input.charAt(i) == pattern.charAt(p) || pattern.charAt(p) == '.'){  
                return matchCore(input,i+1,pattern,p+1);  
            }  
        }  
        return false; 
	}

}



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值