1.面试题2 实现单例模式 --- 静态内部类的应用。
好处: 1.解决多线程安全问题 2.按需创建实例对象,提高内存使用效率。
public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return Nested.singleton;
}
private static class Nested {
public static final Singleton singleton = new Singleton();
}
}
2. 面试题50 第一个只出现一次的字符:
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写). (点击前往牛客网)
/**
* 解法一:不使用Map
* 时间复杂度: O(n)
* 空间复杂度: O(1)
*/
public class Solution {
public int FirstNotRepeatingChar(String str) {
if (str.length() == 0 || str == null) return -1;
int[] hash = new int[56];
char ans = 0;
for (int i = 0; i < str.length(); i++) {
int index = str.charAt(i);
if (Character.isUpperCase(str.charAt(i))) {
index -= 65;
} else {
index -= 71;
}
hash[index]++;
}
int pos = 0;
for (int i = 0; i < str.length(); i++) {
int index = str.charAt(i);
if (Character.isUpperCase(str.charAt(i))) {
index -= 65;
} else {
index -= 71;
}
if (hash[index] == 1) {
pos = i;
break;
}
}
return pos;
}
}
/**
* 解法二:使用HashMap
* 时间复杂度: O(n)
* 空间复杂度: O(1)
*/
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int FirstNotRepeatingChar(String str) {
if (str == null || str.length() == 0) return -1;
Map<Character, Integer> map = new HashMap<Character, Integer>();
for (int i = 0; i < str.length(); i++) {
if (map.get(str.charAt(i)) == null) {
map.put(str.charAt(i), 1);
} else {
map.put(str.charAt(i), map.get(str.charAt(i)) + 1);
}
}
for (int i = 0; i < str.length(); i++) {
if (map.get(str.charAt(i)) == 1) {
return i;
}
}
return 0;
}
}
3.字符流中第一个不重复的字符:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。 (点击前往牛客网)
import java.util.HashMap;
import java.util.Map;
/**
*和上面的題目类似
*/
public class Solution {
// Insert one char from stringstream
Map<Character, Integer> map = new HashMap<Character, Integer>();
StringBuffer sb = new StringBuffer();
public void Insert(char ch) {
sb.append(ch);
if (map.get(ch) == null) {
map.put(ch, 1);
} else {
map.put(ch, map.get(ch) + 1);
}
}
// return the first appearence once char in current stringstream
public char FirstAppearingOnce() {
String str = sb.toString();
for (int i = 0; i < str.length(); i++) {
if (map.get(str.charAt(i)) == 1) {
return str.charAt(i);
}
}
return '#';
}
}
4.面试题3 数组中重复的数字:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。 (点击前往牛客网)
public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of
// duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication
// in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications
// in the array number
// otherwise false
/**
* 时间复杂度:O(n) 空间复杂度:O(1)
*/
public boolean duplicate(int numbers[], int length, int[] duplication) {
if (numbers == null || length == 0) {
return false;
}
for (int i = 0; i < length; i++) {
if (numbers[i] < 0 || numbers[i] > length - 1)
return false;
}
for (int i = 0; i < length; i++) {
while (numbers[i] != i) {
if (numbers[numbers[i]] != numbers[i]) {
int temp = numbers[numbers[i]];
numbers[numbers[i]] = numbers[i];
numbers[i] = temp;
} else {
duplication[0] = numbers[i];
return true;
}
}
}
return false;
}
}
5.面试题4 二维数组中的查找:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。(点击前往牛客网)
public class Solution {
/**
* 从右上角找起,如果右上角的数大于要查找的数,则去掉这一列;
* 如果右上角的数小于要查找的数,则去掉这一行。
* 不使用递归解法
*/
public boolean Find(int target, int[][] array) {
if (array == null) return false;
if (array.length > 0 && array[0].length > 0) {
int row = 0;
int col = array[0].length - 1;
while (array[row][col] != target) {
if (array[row][col] > target) {
col--;
} else {
row++;
}
if (col < 0 || row > array.length - 1)
return false;
}
} else {
return false;
}
return true;
}
}
public class Solution {
/**
* 从右上角找起,如果右上角的数大于要查找的数,则去掉这一列;
* 如果右上角的数小于要查找的数,则去掉这一行。
* 使用递归解法
*/
public boolean Find(int target, int[][] array) {
if (array == null) return false;
int row = array.length;
if (row <= 0) return false;
int col = array[0].length;
if (col <= 0) return false;
if (array[0][col - 1] == target) return true;
if (array[0][col - 1] > target) {
int[][] temp = new int[row][col - 1];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col - 1; j++) {
temp[i][j] = array[i][j];
}
}
if (Find(target, temp)) return true;
else return false;
} else {
int[][] temp = new int[row - 1][col];
for (int i = 0; i < row - 1; i++) {
for (int j = 0; j < col; j++) {
temp[i][j] = array[i + 1][j];
}
}
if (Find(target, temp)) return true;
else return false;
}
}
}
6.面试题5 替换空格:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。(点击前往牛客网)
public class Solution {
/**
* 直接利用String封装好的方法
*/
public String replaceSpace(StringBuffer str) {
if (str == null) return null;
return new String(str).replace(" ", "%20");
}
}
7.面试题6 从尾到头打印链表:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。(点击前往牛客网)
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack = new Stack<Integer>();
ArrayList<Integer> array = new ArrayList<Integer>();
while(listNode != null){
stack.push(listNode.val);
listNode = listNode.next;
}
while(!stack.empty()){
array.add(stack.pop());
}
return array;
}
}
8.面试题18 删除链表的节点: 请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。(点击前往LeetCode) 注:LeetCode此题和剑指Offer上的测试用例略有不同,剑指Offer上测试用例包含了删除的节点是尾节点(只能从前往后遍历,完成删除操作)和链表只有一个节点等特殊情况,需特判。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
if(node == null)return ;
node.val = node.next.val;
node.next = node.next.next;
}
}
9.面试题22 链表中倒数第k个结点: 输入一个链表,输出该链表中倒数第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 p1 = head;
ListNode p2 = head;
for(int i = 0; i < k - 1; i++){
if(p1.next != null){
//防止k大于节点数
p1 = p1.next;
}else{
return null;
}
}
while(p1.next != null){
p1 = p1.next;
p2 = p2.next;
}
return p2;
}
}
10.面试题24 反转链表: 输入一个链表,反转链表后,输出新链表的表头。(点击前往牛客网)
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode bef = head;
ListNode cur = head.next;
head.next = null;
while(cur != null){
ListNode next = cur.next;
cur.next = bef;
bef = cur;
cur = next;
}
return bef;
}
}
11.面试题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) {
if(list1 == null) return list2;
if(list2 == null) return list1;
ListNode mergeList = null;
if(list1.val < list2.val){
mergeList = list1;
mergeList.next = Merge(list1.next, list2);
}else {
mergeList = list2;
mergeList.next = Merge(list1, list2.next);
}
return mergeList;
}
}
12.面试题52 两个链表的第一个公共结点: 输入两个链表,找出它们的第一个公共结点。(点击前往牛客网)
public class Solution {
/**
*不使用栈辅助空间,时间复杂度要求为O(n+m)
* @return
*/
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if(pHead1 == null || pHead2 == null) return null;
if(pHead1 == pHead2) return pHead1;
int list1Length = 0;
ListNode p1 = pHead1;
while(p1 != null){
list1Length++;
p1 = p1.next;
}
int list2Length = 0;
ListNode p2 = pHead2;
while(p2 != null){
list2Length++;
p2 = p2.next;
}
ListNode ans = null;
if(list1Length > list2Length){
int more = list1Length - list2Length;
for(int i = 0; i < more; i++){
pHead1 = pHead1.next;
}
for(int i = 0; i < list2Length; i++){
if(pHead1 == pHead2){
ans = pHead1;
break;
}else {
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
}
}
if(list1Length < list2Length){
int more = list2Length - list1Length;
for(int i = 0; i < more; i++){
pHead2 = pHead2.next;
}
for(int i = 0; i < list1Length; i++){
if(pHead1 == pHead2){
ans = pHead1;
break;
}else {
pHead1 = pHead1.next;
pHead2 = pHead2.next;
}
}
}
return ans;
}
}
13.面试题62 孩子们的游戏(圆圈中最后剩下的数):每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)(点击前往牛客网)
public class Solution {
/**
* 经典的约瑟夫环问题,解决这个问题可以采用模拟或利用递归找出答案。
* 但是模拟的算法效率较低,所用采用简单的递归找出答案。
* 此处递归的关键是利用这轮幸运者的下标推出上轮幸运者的下标:
* f(n,k) = (f(n-1,k) + k) % m
*/
public int LastRemaining_Solution(int n, int m) {
if(n < 1 || m < 1)return -1;
if(n == 1)
return 0;
else
return (LastRemaining_Solution(n-1, m) + m) % n;
}
}
关于递归的图解推理,我推荐看这篇文章 约瑟夫环 数学解法 f(n,k)=(f(n-1,k)+k)%n 公式讲解
14.面试题36 二叉搜索树与双向链表:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。(点击前往牛客网)
public class Solution {
TreeNode curNode = null; // 当前节点
TreeNode headNode = null;
/**
* 思路:按中序遍历,遍历完左子树,就将当前节点与根节点串起来。
*/
public TreeNode Convert(TreeNode pRootOfTree) {
if (pRootOfTree == null)
return null;
traverse(pRootOfTree);
return headNode;
}
public void traverse(TreeNode root) {
if (root == null)
return;
traverse(root.left);
if (curNode == null) {
curNode = root;
headNode = root;
} else {
curNode.right = root;
root.left = curNode;
curNode = root;
}
traverse(root.right);
}
}
15.面试题35 复杂链表的复制:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)(点击前往牛客网)
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null) return null;
RandomListNode currentNode = pHead;
while (currentNode != null) {
RandomListNode cloneNode = new RandomListNode(currentNode.label);
RandomListNode nextNode = currentNode.next;
currentNode.next = cloneNode;
cloneNode.next = nextNode;
currentNode = nextNode;
}
currentNode = pHead;
while (currentNode != null) {
currentNode.next.random = currentNode.random == null ? null : currentNode.random.next;
currentNode = currentNode.next.next;
}
currentNode = pHead;
RandomListNode copylist = pHead.next;
while (currentNode != null) {
RandomListNode cloneNode = currentNode.next;
currentNode.next = cloneNode.next;
cloneNode.next = cloneNode.next == null ? null : cloneNode.next.next;
currentNode = currentNode.next;
}
return copylist;
}
}
16.面试题7 重建二叉树:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。(点击前往牛客网)
public class Tree1 {
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
if(pre.length == 0 || in.length == 0 || pre == null || in == null) {
return null;
}
return traverseTree(pre, 0, pre.length -1, in, 0, in.length - 1);
}
public TreeNode traverseTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) {
if(startPre > endPre || startIn > endIn) return null;
TreeNode treeNode = new TreeNode(pre[startPre]);
for(int i = startIn; i <= endIn; i++) {
if(in[i]== pre[startPre]) {
//i - startIn 是在中序数组中根节点左边的左子树节点个数
treeNode.left = traverseTree(pre, startPre+1, startPre + (i - startIn), in, startIn, i - 1);
treeNode.right = traverseTree(pre, startPre + (i - startIn) + 1, endPre, in, i+1, endIn);
break;
}
}
return treeNode;
}
}
17.面试题8 二叉树的下一个结点:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。(点击前往牛客网)
public class Solution {
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode == null) return null;
if(pNode.right != null) {
//如果该节点有右子树
TreeLinkNode pr = pNode.right;
while (pr.left !=null) {
pr = pr.left;
}
return pr;
}else if (pNode.next == null) {
return null;
}else if (pNode.right == null && pNode.next.left == pNode) {
//如果该节点没有右子树且是一个父节点的左子节点
return pNode.next;
}else if (pNode.right == null &&pNode.next.left != pNode) {
//如果该节点没有右子树且不是一个父节点的左子节点
TreeLinkNode node = pNode.next;
while(node != null) {
if(node.next != null && node.next.left == node) {
return node.next;
}
node = node.next;
}
return node;
}
return null;
}
}
18. 面试题39 数组中出现次数超过一半的数字: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。(点击前往牛客网)
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array.length == 0 || array == null) return 0;
int half = array.length / 2;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i = 0; i < array.length; i++){
if(map.get(array[i]) == null){
map.put(array[i], 1);
if(map.get(array[i]) > half) return array[i];
}else{
int count = map.get(array[i]);
map.put(array[i], count+1);
if(map.get(array[i]) > half) return array[i];
}
}
return 0;
}
}
19. 输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。(点击前往牛客网)
public class Solution {
//解法1: n &= (n - 1)操作实质是抹掉最右边的1,因此循环次数只与1的个数有关
public int NumberOf2(int n) {
int result = 0;
while(n != 0){
result++;
n = n & (n - 1);
}
return result;
}
//解法2:首先把n和1做与运算,判断n的最低位是不是为1。
//接着把1左移一位得到2,再和n做与运算,就能判断n的次低位是不是1…….
//这样反复左移,每次都能判断n的其中一位是不是1。
public int NumberOf2(int n) {
int result = 0;
int base = 1;
while(base != 0){
if((n & base) != 0){
result++;
}
base = base << 1;
}
return result;
}
//解法3: 利用无符号右移, 数n每次进行无符号右移一位,检查最右边的bit是否为1来进行统计
public int NumberOf3(int n) {
int result = 0;
while (n != 0) {
result += n & 1;
n >>>= 1;
}
return result;
}
}
20. 给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。保证base和exponent不同时为0
public class Solution {
public double Power(double base, int exponent) {
if(base == 0) return 0;
if(exponent == 0) return 1;
double result = 1;
if(exponent > 0){
//正次幂
for(int i = 0; i < exponent; i++){
result *= base;
}
}else{
//-负次幂
exponent = -exponent;
for(int i = 0; i < exponent; i++){
result = result / base;
}
}
return result;
}
}
21. 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
import java.util.ArrayList;
public class Solution {
/*
*1. 如果array数组为null, 或长度为0 return
*2. 遍历一次数组, 将奇数存到一个数组中, 将偶数存到一个数组中
*3. 将奇数数组和偶数数组替换掉原来数组的内容
*/
public void reOrderArray(int [] array) {
if(array == null || array.length == 0) return ;
ArrayList<Integer> oddArray = new ArrayList<>();
ArrayList<Integer> evenArray = new ArrayList<>();
for(int i = 0; i < array.length; i++){
if(array[i] % 2 == 1){
oddArray.add(array[i]);
}else{
evenArray.add(array[i]);
}
}
for(int i = 0; i < oddArray.size(); i++){
array[i] = oddArray.get(i);
}
for(int i = oddArray.size(); i < evenArray.size()+oddArray.size(); i++){
array[i] = evenArray.get(i-oddArray.size());
}
}
}