题目2:实现Singleton模式
构造函数设为私有函数;饿汉式;懒汉式+同步锁;静态内部类; |
//饿汉式
class Singleton1{
private static Singleton1 s1= new Singleton1();
private Singleton1(){};
public static Singleton1 get(){
return s1;
}
}
//懒汉式_将get方法设为同步;
class Singleton1{
private static Singleton1 s1= null;
private Singleton1(){};
public static synchronized Singleton1 get(){
if (s1 == null){
s1 = new Singleton1();
}
return s1;
}
}
//懒汉式_同步代码块
class Singleton1{
volatile private static Singleton1 s1= null;
private Singleton1(){};
public static Singleton1 get(){
if (s1 == null){
synchronized (Singleton1.class){
if (s1 == null){
s1 = new Singleton1();
}
}
}
return s1;
}
}
//静态内部类
class Singleton1{
private Singleton1(){};
private static class Inner{
private static Singleton1 s1 = new Singleton1();
}
public static Singleton1 get(){
return Inner.s1;
}
}
题目3_1:数组中重复的数字
解法1 | 对数组排序,再扫描数组;o(nlogn) |
解法2 | 额外增加一个o(n)的哈希表,遇到数字则存入哈希表,若已存在则遇到重复;时间复杂度o(n) |
解法3 | 因为数字限定在0-n-1的范围内,则可以将数字与下标对应,若遇到乱序则进行还原;时间复杂度o(n),空间复杂度o(1) |
public static int duplicate(int[] a){
//空数组
if (a.length == 0){
System.out.println("null array");
return -1;
}
for (int x = 0;x<a.length;x++){
int temp = a[x];
//包含0-(n-1)范围外的数字
if (temp>a.length-1){
System.out.print("error input");
return -1;
}
//当角标不等时
if (temp != x){
//已有
if (a[temp] == temp){
return temp;
}else {
a[x] = a[temp];
a[temp] = temp;
System.out.println(Arrays.toString(a));
x -- ;
}
}
}
return -1;
}
题目 3-2
额外o(n)的数组,依次将原数组复制至新数组对应下标,出现已有元素则找到重复 | o(n);o(n) |
将n个数二分,计算每部分在数组中出现的次数,必然有一边多于n/2,则重复出现在那边 | o(nlogn);o(1) |
public static int getDuplication(int[] a){
if (a == null || a.length == 0){
return -1;
}
int start = 1;
int end = a.length - 1;
while (start<=end){
int mid = (end-start)/2+start;
int count = count(a,start,mid);
if (start == end){
if (count>1){
return start;
}else {
break;
}
}
if ( count > mid - start + 1){
end = mid;
}else {
start = mid+1;
}
}
return -1;
}
public static int count(int[] a ,int start ,int end){
int count = 0;
if (a == null || a.length == 0){
return count;
}
for (int x :a){
if (x>=start && x<=end){
count++;
}
}
return count;
}
题目 4
利用左下角或右上角减小需要判断的区域; |
public static boolean find(int[][] a,int num){
int hang = 0;
int lie = a.length-1;
if (a == null || a.length == 0){
return false;
}
while (hang <= a.length-1 && lie >= 0){
if (a[hang][lie] == num){
return true;
}
else if (a[hang][lie] < num){
hang++;
}
else if (a[hang][lie] > num){
lie--;
}
if (hang > a.length || lie < 0){
break;
}
}
return false;
}
题目 5_1
从头扫描字符串,遇到空格则后移后面的字符插入替换的字符 | o(n[2]) |
预算替换后的长度,用两个指针,一个指向字符串尾,一个指向预算长度尾,向前替换 | o(n) |
//java中String类型是不可变的,数组是不可直接扩容的,故这题无法直接在原字符串上进行替换
public static String replaceBlack(String a){
if (a ==null ||a.length() == 0){
return null;
}
StringBuilder sb = new StringBuilder();
for (int x = 0;x<a.length();x++){
if (a.charAt(x) == ' '){
sb.append("%20");
}else {
sb.append(a.charAt(x));
}
}
return sb.toString();
}
题目5_2 合并两个有序数组
public static int[] combineArray(int[] a, int[] b){
//数组为空的情况
if (a == null || b == null){
return null;
}
//数组长度为0的情况
if (a.length == 0 || b.length == 0){
return a.length == 0 ? b:a;
}
ArrayList<Integer> c = new ArrayList<>();
int i = 0;
int j = 0;
while (i<a.length || j< b.length){
if (i == a.length && j<b.length){
c.add(b[j]);
j++;
}
else if (i<a.length && j == b.length){
c.add(a[i]);
i++;
}
else {
if (a[i]>=b[j]){
c.add(b[j]);
j++;
}else {
c.add(a[i]);
i++;
}
}
}
int[] d = new int[c.size()];
for(int x = 0;x<d.length;x++){
d[x] = c.get(x);
}
return d;
}
题目 6
用栈来进行暂存; | o(n);o(n) |
递归,每当访问一个节点首先输出后一个节点,链表过长会导致函数调用栈溢出; | o(1);o(n) |
//用的是自己写的链表,用的时候才发现当时漏掉了好多函数;
public static void PrintListReversingly_Iteratively(MyLinkList my){
Stack<MyLinkList.Node> st = new Stack<>();
MyLinkList.Node no = MyLinkList.getRoot();
while (MyLinkList.getNext(no)!= null){
MyLinkList.Node next = MyLinkList.getNext(no);
st.push(next);
no = next;
}
while (st.isEmpty() != true){
System.out.print(st.pop().data);
}
}
public static void PrintListReversingly_Recursively(MyLinkList.Node e){
if (e != null){
if ( MyLinkList.getNext(e) != null){
PrintListReversingly_Recursively(MyLinkList.getNext(e));
}
System.out.print(e.data);
}
}
题目7
递归的思想构建,每次返回子树根节点 |
public static Tree.Node Construct(int[] preorder ,int[] inorder){
if (preorder == null || inorder == null || preorder.length == 0 || inorder.length == 0|| preorder.length != inorder.length){
return null;
}
Tree.Node temp = Tree.newNode(preorder[0]);
for (int i = 0;i<inorder.length;i++){
if (preorder[0] == inorder[i]){
temp.leftchild = Construct(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
temp.rightchild = Construct(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
}
}
return temp;
}
题目8 二叉树的下一个节点
分类讨论;若存在右子树,则下一个节点为右子树的最左节点;若无且当前节点为父节点的左节点,则为父节点;若为右节点,则向上找一个为左子节点的父亲的父亲; |
public class test {
public static void main(String[] args) {
Node[] nodes = new Node[9];
for(int i = 0;i<9;i++){
nodes[i] = new Node(i);
}
nodes[0].leftchild = nodes[1];
nodes[0].rightchild = nodes[2];
nodes[1].father = nodes[0];
nodes[1].leftchild = nodes[3];
nodes[1].rightchild = nodes[4];
nodes[2].father = nodes[0];
nodes[2].leftchild = nodes[5];
nodes[2].rightchild = nodes[6];
nodes[3].father = nodes[1];
nodes[4].father = nodes[1];
nodes[4].leftchild = nodes[7];
nodes[4].rightchild = nodes[8];
nodes[5].father = nodes[2];
nodes[6].father = nodes[2];
nodes[7].father = nodes[4];
nodes[8].father = nodes[4];
print(nodes[0]);
System.out.print("\n");
System.out.println(getNext(nodes[6]).data);
}
public static void print(Node e){
if (e == null){
return;
}
if (e.leftchild != null){
print(e.leftchild);
}
System.out.print(e.data);
print(e.rightchild);
}
public static Node getNext(Node e){
if (e == null){
return null;
}
if (e.rightchild != null){
Node temp = e.rightchild;
while (temp.leftchild != null){
temp = temp.leftchild;
}
return temp;
}else {
if (e.father.leftchild == e){
return e.father;
}else {
while (e.father != null && e.father.father.leftchild != e.father){
e = e.father;
}
return e.father;
}
}
}
}
class Node{
int data;
Node(int num){
this.data = num;
}
Node leftchild;
Node rightchild;
Node father;
}
面试题9_1
stack2为空时,stack1弹出所有元素并压入stack2;stack2不为空时,直接弹出stack2的栈顶; |
class CQueue{
private static Stack<Integer> e1 = new Stack();
private static Stack<Integer> e2 = new Stack();
public static void appendTail(int num){
e1.push(num);
}
public static int deleteHead() throws Exception{
if (e2.isEmpty() == true){
while (e1.isEmpty() != true){
e2.push(e1.pop());
}
}
if (e2.isEmpty() == true){
throw new Exception("queue is empty");
}
return e2.pop();
}
}
面试题9_2
维持一个队列为空,每次弹出是将1-(n-1)的元素移动至另一队列中; |
class Cstack{
private static ArrayDeque<Integer> q1 = new ArrayDeque<>();
private static ArrayDeque<Integer> q2 = new ArrayDeque<>();
public static void push(int num){
if (q1.isEmpty() != true){
q1.add(num);
}else {
q2.add(num);
}
}
public static int pop() throws Exception{
if (q1.isEmpty() != true){
int temp = 0;
while (temp<q1.size()-1){
q2.add(q1.poll());
}
return q1.poll();
}else {
if (q2.isEmpty() == true){
throw new Exception("empty");
}
int temp = 0;
while (temp<q2.size()-1){
q1.add(q2.poll());
}
return q2.poll();
}
}
}
面试题10_1
自底向上计算,避免重复计算 | o(n) |
利用公式化解计算 | o(log[n]) |
public static int Fibonacci(int num){
int f0 = 0;
int f1 = 1;
int temp = f0+f1;
for (int x = 0;x<num-1;x++){
temp = f0+f1;
f0 = f1;
f1 = temp;
}
return num == 0?f0:temp;
}
面试题10_2
n = 1时只有一种跳法;n = 2时2钟;n时,可以选择前一步为只走1步,也可以选择前一步只走2步,f(n) = f(n-1)+f(n-2); |
面试题10_3
f(8) = 竖着放一个小矩形:f(7)+横着放两个小矩形+f(6) |
面试题11
最小值刚好位于两个有序数组的分界处,可以用二分查找的思想进行范围的缩小;当尾指针=头指针=中间指针时,只能进行顺序查找;o(log[n]) |
public static int Min(int[] a){
if ( a == null || a.length == 0){
return -1;
}
int i = 0;
int j = a.length-1;
int mid = 0;
while (a[i]>=a[j]){
if (j-i == 1){
mid =j;
break;
}
mid = (i+j)/2;
if (a[i] == a[j] && a[i] == a[mid]){
return MinInOrder(a,i,j);
}
if (a[i]<=a[mid]){
i = mid;
}
else if (a[j]>=a[mid]){
j = mid;
}
}
return a[mid];
}
public static int MinInOrder(int[] a, int i,int j){
int re = a[i];
for(int x = i;x<=j;x++){
if ( re > a[x] ){
re = a[x];
}
}
return re;
}
面试题12:矩阵中的路径
遍历矩阵选择点;判断该点是否可作为字符串开头;若可作为开头,递归的查看其4个方向的点是否在字符串中; |
public static boolean hasPath(char[][] matrix,char[] str){
if (matrix == null || str == null){
return false;
}
int rows = matrix.length;
if (rows == 0){
return false;
}
int cols = matrix[0].length;
if (cols == 0){
return false;
}
boolean[][] visited = new boolean[rows][cols];
int pathLenth = 0;
for (int x = 0;x<rows;x++){
for (int y = 0; y<cols;y++){
if (hasPathCore(matrix,rows,cols,x,y,str,pathLenth,visited) == true){
return true;
}
}
}
return false;
}
public static boolean hasPathCore(char[][] matrix,int rows,int cols,int x,int y,char[] str,int pathlenth,boolean[][] visited){
if (str.length == pathlenth){
return true;
}
boolean hasPath = false;
if (x>=0 && x<rows && y>=0 && y<cols && matrix[x][y] == str[pathlenth] && visited[x][y] == false){
pathlenth++;
visited[x][y] = true;
hasPath = hasPathCore(matrix,rows,cols,x,y+1,str,pathlenth,visited) ||
hasPathCore(matrix,rows,cols,x,y-1,str,pathlenth,visited) ||
hasPathCore(matrix,rows,cols,x-1,y,str,pathlenth,visited) ||
hasPathCore(matrix,rows,cols,x+1,y,str,pathlenth,visited);
if (hasPath == false){
pathlenth --;
visited[x][y] = false;
}
}
return hasPath;
}
面试题13:机器人的运动范围
可达点的周围也必然有可达点;从(0,0)开始,进行递归的扫描; |
public static int getDigitSum(int number){
int sum = 0;
while (number > 0){
sum += number%10;
number /= 10;
}
return sum;
}
public static boolean check(int threshold,int rows,int cols,int row,int col,boolean[][] visited){
if (row>=0 && row<rows && col>=0 && col<cols && (getDigitSum(row)+getDigitSum(col))>threshold && visited[row][col] == false){
return true;
}
return false;
}
public static int movingCount(int threshold,int rows,int cols){
if (threshold<0 || rows<0 || cols<0){
return 0;
}
boolean[][] visited = new boolean[rows][cols];
return movingCountCore(visited,threshold,rows,cols,0,0);
}
public static int movingCountCore(boolean[][] visited,int threshold,int rows,int cols,int row,int col){
int count = 0;
if (check(threshold,rows,cols,row,col,visited) == true){
visited[row][col] = true;
count = 1+ movingCountCore(visited,threshold,rows,cols,row,col+1)+movingCountCore(visited,threshold,rows,cols,row,col-1)+
movingCountCore(visited,threshold,rows,cols,row-1,col)+movingCountCore(visited,threshold,rows,cols,row+1,col);
}
return count;
}
面试题14:剪绳子
动态规划 f(n) = max{f(i) * f(n-i)}; | o(n[2]);o(n); |
贪婪算法 尽量多剪长度为3的,剩下长度为4时,剪成两个长度为2 | o(1) |
public static int maxProductAfterCutting_solution1(int lenth){
if (lenth<2){
return 0;
}else if (lenth == 2){
return 1;
}else if (lenth == 3){
return 2;
}
int[] products = new int[lenth+1];
//用于乘的组件 product[4]过后才是记录的值
products[1] = 1;
products[2] = 2;
products[3] = 3;
for (int x = 4;x <= lenth;x++){
int max = 0;
for (int y = 1;y <= x/2;y++){
int product = products[y]*products[x-y];
if (max<product){
max = product;
}
}
products[x] = max;
}
return products[lenth];
}
public static int maxProductAfterCutting_solution2(int length){
if (length<2){
return 0;
}else if (length == 2){
return 1;
}else if (length == 3){
return 2;
}
int timesOf3 = length/3;
if (length - timesOf3*3 == 1){
timesOf3 = timesOf3 -1;
}
int timesof2 = (length - timesOf3*3)/2;
return (int)(Math.pow(3,timesOf3) * Math.pow(2,timesof2));
}
面试题15_1:二进制中1的个数
在电脑中存储的是二进制的树;可以通过与1进行某位的判断;可以通过n&(n-1)将最右的1置0; |
public static int NumberOf1(int n){
int count = 0;
while (n!=0){
count++;
n = n & (n-1);
}
return count;
}
面试题15_2:
若一个整数是2的整数次方,则其二进制表示中只有1位为1; |
public static boolean if2(int num){
if ( num <= 0){
return false;
}
if ( (num & (num-1)) == 0){
return true;
}
return false;
}
面试题15_3:
求两个数二进制表示中不同的位的个数;求异或,再计算1的个数; |
public static int diff(int num1,int num2){
int num3 = num1 ^ num2;
int count = 0;
while (num3 != 0){
count = count+1;
num3 = num3 & (num3-1);
}
return count;
}