面试题53_1:在排序数组中查找数字
因数组有序,可以考虑二分查找,但二分查找后还需顺序查找第一个k和最后一个k,可以在二分查找的过程中对是否为第一个或最后一个k进行判断。最终的复杂度控制在o(logn); |
public static int getApperanceNumber(int[] nums,int k){
if (nums == null || nums.length == 0){
return 0;
}
if (nums.length == 1){
if (k == nums[0]){
return 1;
}else {
return 0;
}
}
int firstindex = getFirstIndex(nums,k);
int lastindex = getLastIndex(nums,k);
if (firstindex != -1 && lastindex!=-1){
int reback = lastindex-firstindex+1;
if (reback>= 0){
return reback;
}else {
return 0;
}
}
return 0;
}
public static int getFirstIndex(int[] nums,int k){
int low = 0;
int high = nums.length -1;
while (low <= high){
int mid = (low+high)/2;
if (k == nums[mid]){
for (int x = mid;x>=0;x--){
if (nums[x] != k){
return x+1;
}
if (x == 0){
return 0;
}
}
}
if (k < nums[mid]){
high = mid-1;
}
if (k > nums[mid]){
low = mid+1;
}
}
return -1;
}
public static int getLastIndex(int[] nums,int k){
int low = 0;
int high = nums.length -1;
while (low <= high){
int mid = (low+high)/2;
if (k == nums[mid]){
for (int x = mid;x<=nums.length-1;x++){
if (nums[x] != k){
return x-1;
}
if (x == nums.length-1){
return x;
}
}
}
if (k < nums[mid]){
high = mid-1;
}
if (k > nums[mid]){
low = mid+1;
}
}
return -1;
}
53_2:0-(n-1)中的数字
求出0-(n-1)原本的和,再求数组的和,相减过后得到差得那个数,o(n)但没有利用数组按顺序排列的特点 |
缺失的那个数下标与原数不同,且之前相同,之后不同;利用二分来找到第一个下标不同的数,o(logn) |
public static int getMissingNum(int[] nums){
if (nums == null || nums.length == 0){
return -1;
}
int low = 0;
int high = nums.length-1;
while (low <= high){
int mid = (low+high)/2;
if (nums[mid] != mid){
if (mid == 0){
return mid;
}
if (nums[mid-1] != mid-1){
high = mid-1;
}else {
return mid;
}
}else {
low = mid +1;
}
}
return nums.length;
}
面试题53_3:数组中数值和下标相等的元素
数组有序,则可以用二分查找来优化。o(logn) |
public static Integer getIndexSameNum(int[] nums){
if (nums == null || nums.length == 0){
return null;
}
if (nums.length == 1){
if (nums[0] == 0){
return 0;
}else {
return null;
}
}
int low = 0;
int high = nums.length-1;
while (low <= high){
int mid = (low+high)/2;
if (nums[mid] == mid){
return mid;
}else if (nums[mid] > mid){
high = mid-1;
}else {
low = mid+1;
}
}
return null;
}
面试题54:二叉搜索树的第k大节点
中序遍历 |
//递归中传递参数:当返回类型,函数参数都不好用时,直接设置一个大小为1的数组进行值得传递;
public static Node getKmaxNum(Node root,int k){
if (root == null || k <= 0){
return null;
}
int[] temp = {k};
return getKmaxNum_core(root,temp);
}
public static Node getKmaxNum_core(Node root,int[] temp){
Node reback = null;
if (root.left != null){
reback = getKmaxNum_core(root.left,temp);
}
if (reback == null){
if (temp[0] == 1){
reback = root;
}
temp[0]--;
}
if (reback == null && root.right!= null){
reback = getKmaxNum_core(root.right,temp);
}
return reback;
}
面试题55_1:二叉树的深度
二叉树的深度可以依靠左右子树的深度递归求解 |
public static int getTreeDeep(Node root){
if (root == null){
return 0;
}
int left = getTreeDeep(root.left);
int right = getTreeDeep(root.right);
return left > right ? left+1:right+1;
}
面试题55_2:平衡二叉树
求出左右子树的深度,判断,再递归判断左右子树是否为平衡的,存在重复判断的情况 |
后续遍历,先检查左右子树,并记录深度,从叶子节点开始检查 |
public static boolean judgeBlanceTree(Node root){
if (root == null) {
return false;
}
judgeBlanceTree_core(root);
return isBlanced;
}
public static int judgeBlanceTree_core(Node root){
if (root == null){
isBlanced = true;
return 0;
}
int left = judgeBlanceTree_core(root.left);
int right = judgeBlanceTree_core(root.right);
int depth =( left > right?left:right)+1;
if (Math.abs(left-right)<=1){
isBlanced = true;
}else {
isBlanced = false;
}
return depth;
}
面试题56_1:数组中数字出现的次数
任何一个数字异或它自己 = 0;遍历数组,若只有一个数出现一次,通过异或的结果可以得到此数 存在两个只出现一次的数时需要将数组分为两个,使每个只包含一个; 分数组的方式为:遍历异或数组后的值为两个孤儿的异或值,因不相同,则至少有1位为1,取最低的1位,在此位两个数相反,故按此位是否为0,将原数组划分为2组,再对两组分别求孤儿; |
//无法判断数组中是否有重复的,或有多于2个的重复;如果需要判断此步,在第一次遍历过程中需要用hashmap将出现次数存起来。但也就得到出现次数为1的了
public static int[] notDuplicateNums(int[] nums){
int[] reback = new int[2];
if (nums == null || nums.length <= 1){
return reback;
}
if (nums.length == 2){
if (nums[0] == nums[1]){
return reback;
}else {
reback[0] = nums[0];
reback[1] = nums[1];
return reback;
}
}
int exclusive = countYH(nums);
int firstdigt = getFirstDigt(exclusive);
for (int x :nums){
if (isBit1(x,firstdigt) == 1){
reback[0] = reback[0]^x;
}else {
reback[1] = reback[1]^x;
}
}
return reback;
}
public static int countYH(int[] nums){
int exclusive = 0;
for (int x : nums){
exclusive = exclusive^x;
}
return exclusive;
}
public static int getFirstDigt(int num){
int indexBit = 0;
while (((num&1) == 0 )&& (indexBit < 32)){
num = num>>1;
indexBit++;
}
return indexBit;
}
public static int isBit1(int num,int firstdigt){
num = num>>firstdigt;
return num&1;
}
面试题56_2:数组中唯一只出现一次的数字
hashmap存储出现次数 |
位运算:每位的和加起来应是3的倍数,若不是,则只出现1次的数此位为1;正负无影响 |
public static int getNoDuplicated(int[] nums){
if (nums == null || nums.length == 0){
error = true;
}
int[] bitsum = new int[32];
for (int x = 0; x<=nums.length-1;x++){
int bitmash = 1;
for (int j = 31;j>=0;j--){
int bit = nums[x] & bitmash;
if (bit != 0){
bitsum[j] += 1;
}
bitmash = bitmash<<1;
}
}
int result = 0;
for (int x =0 ;x<32;x++){
result = result<<1;
result += bitsum[x]%3;
}
return result;
}
面试题57_1:和为s的数字
从头开始遍历数组,将当前数与剩下的数进行比较,寻找和相等的 | o(n[2]) |
由于数组有序,设置两个指针,一个指向第一个元素,一个指向最后一个元素,根据和调整这两个指针 | o(n) |
public static int[] judgeSum(int[] nums,int sum){
if (nums == null || nums.length<2 ){
return null;
}
int[] reback = new int[2];
int start = 0;
int end = nums.length - 1;
while (start < end){
int temp = nums[start]+nums[end];
if (temp == sum){
reback[0] = nums[start];
reback[1] = nums[end];
return reback;
}else if (temp < sum){
start++;
}else {
end -- ;
}
}
return null;
}
面试题57_2:和为s的连续正数序列
由于是等差数列,可以化简为找到头尾两个数,第一个指针指向第一个元素,第二个指针指向第二个元素,因为必须含两个元素,故直到较小元素到达(1+s)/2为止 |
public static void FindContinusSequence(int sum){
if (sum <= 0){
return;
}
int start = 1;
int end = 2;
while (start < (1+sum)/2){
float temp = ((float)(start + end))*(end-start+1)/2;
if (temp == sum){
for (int x = start;x<=end;x++){
System.out.print(x);
}
System.out.print("\n");
end++;
start++;
}else if (temp < sum){
end++;
}else {
start++;
}
}
return;
}
面试题58_1:翻转字符串
先将字符串全部翻转,再依据空格将每个单词翻转回来。也可以使用java中的split函数来先进行分割,再在词的基础上进行翻转。额外需要用空间; |
public static String ReverseString(String st){
if (st == null || st.length() == 0){
return "";
}
if (st.length() == 1){
return st;
}
String[] st_li = st.split(" ");
int start = 0;
int end = st_li.length-1;
StringBuilder stb = new StringBuilder();
while (start < end) {
stb.append(st_li[end]);
st_li[end] = st_li[start];
st_li[start] = stb.substring(0,stb.length());
stb.delete(0,stb.length());
start++;
end--;
}
return String.join(" ",st_li);
}
面试题58_2:左旋转字符串
StringBuilder的思路更简洁 |
public static String LeftRotate(String st ,int index){
if (st == null||st.length() == 0||index>st.length()-1||index<0){
return "";
}
if (index == 0 || index == st.length()){
return st;
}
StringBuilder stb = new StringBuilder();
stb.append(st);
stb.append(stb.subSequence(0,index));
stb.delete(0,index);
return stb.substring(0,stb.length());
}
面试题59_1:滑动窗口的最大值
顺序遍历及比较 | o(nk) |
用双端开口的队列进行辅助 | o(n) |
public static ArrayList MaxOfWindow(int[] nums ,int k){
if (nums == null || nums.length < k || k<=0 ){
return null;
}
ArrayDeque arrayDeque = new ArrayDeque<>();
ArrayList reback = new ArrayList<>();
for (int x = 0;x= nums[arrayDeque.peekFirst()]){
arrayDeque.pollFirst();
}
arrayDeque.addFirst(x);
}
for (int x=k;x=nums[arrayDeque.peekFirst()]){
arrayDeque.pollFirst();
}
if (!arrayDeque.isEmpty() && arrayDeque.peekLast() <= (x-k)){
arrayDeque.pollLast();
}
arrayDeque.addFirst(x);
}
reback.add(nums[arrayDeque.peekLast()]);
return reback;
}
59_2:队列的最大值
相当于将整个队列设置为滑动窗口 |
class Deque_max{
class Data{
int value;
int index;
Data(int value,int index){
this.value = value;
this.index = index;
}
}
ArrayDeque arrayDeque_data = new ArrayDeque<>();
ArrayDeque arrayDeque_max = new ArrayDeque<>();
public static int currentIndex;
public void push_back(int num){
while (!arrayDeque_max.isEmpty() && num >= arrayDeque_max.peekLast().value){
arrayDeque_max.pollLast();
}
Data newdata = new Data(num,currentIndex);
arrayDeque_data.addLast(newdata);
arrayDeque_max.addLast(newdata);
currentIndex++;
}
public int pop_front() throws Exception{
if (arrayDeque_data.isEmpty()){
throw new Exception("no numbers");
}
if (arrayDeque_data.peekFirst().index == arrayDeque_max.peekFirst().index){
arrayDeque_max.pollFirst();
}
return arrayDeque_data.pollFirst().value;
}
public int max() throws Exception{
if (arrayDeque_max.isEmpty()){
throw new Exception("no max");
}
return arrayDeque_max.peekFirst().value;
}
}
面试题60:n个骰子的点数
求出n个骰子的点数和,用递归进行求解 |
用两个数组存储,第一个数组存储和为n出现的次数,第二个数组n为前一个数组n-1,n-2,n-3到n-6之和 |
public static void Prosibility(int numOfGm){
if (numOfGm<1){
return;
}
int maxSum = 6*numOfGm;
int[] prosibilitys = new int[maxSum-numOfGm+1];
Probability(numOfGm,prosibilitys);
int total = (int)Math.pow(6,numOfGm);
for (int i = numOfGm;i<= maxSum;i++){
System.out.println(prosibilitys[i-numOfGm]/(float)(total));
}
}
public static void Probability(int numOfGm,int[] prosibilitys){
for (int x = 1;x<=6;x++){
Prosibility_core(numOfGm,numOfGm,x,prosibilitys);
}
}
public static void Prosibility_core(int original,int current,int sum,int[] prosibilitys){
if (current == 1){
prosibilitys[sum-original]++;
}else {
for (int i = 1;i<=6;i++){
Prosibility_core(original,current-1,i+sum,prosibilitys);
}
}
}
public static void Prosibility(int numOfGm){
if (numOfGm<1){
return;
}
int[][] pProbabilities = new int[2][];
pProbabilities[0] = new int[6*numOfGm+1];
pProbabilities[1] = new int[6*numOfGm+1];
int flag = 0;
for (int i = 1;i<=6;i++){
pProbabilities[flag][i] = 1;
}
for (int k = 2;k<=numOfGm;k++){
for (int x = 0;x
面试题61:扑克牌中的顺子
将扑克看做数字,大小王视作0 。需要将数组排序,统计数组中0的个数,判断空缺数,若空缺数<=0的个数也是有序的。非0数字连续出现则数组无序。 |
public static boolean isContinues(int[] nums){
if (nums == null || nums.length != 5){
return false;
}
Arrays.sort(nums);
int numOfzero = 0;
for (int x:nums){
if (x == 0){
numOfzero++;
}
}
if (numOfzero>2){
return false;
}
int gap = 0;
for (int x = 0;x= gap ? true:false;
}
面试题62:圆圈中最后剩下的数字(约瑟夫环问题)
用头尾相连的链表来模拟约瑟夫环,每次删除第m个 |
数学推导得到递推公式:f(n,m) = (f(n-1,m)+m)%n |
class Circle {
class Node{
int value;
Node next;
Node(int value){
this.value = value;
}
}
Node head;
Node tail;
public void addNode(int value){
Node now = new Node(value);
if (head == null){
head = now;
tail = now;
}else {
tail.next = now;
tail = tail.next;
tail.next = head;
}
}
public Node delNode(int index,Node start){
Node temp = start;
for (int x = 1;x
面试题63:股票的最大利润
直接求出所有数对,取差值最大的一个 | o(n[2]) |
扫描数字,记录前面出现过的最小值及差值的最大值 | o(n) |
public static int MaxD(int[] nums) throws Exception{
if (nums == null || nums.length <= 1){
throw new Exception("error");
}
int minNum = nums[0];
int MaxD = 0;
for (int x = 1;x MaxD){
MaxD = nums[x]-minNum;
}
if (nums[x]
面试题64:求1-n的和
利用&&的短路效果结束递归 |
利用反射的技术 |
public static int getSum(int num){
int sum = num;
boolean ans = (num>0) && ((sum += getSum(num-1))>0);
return sum;
}
class count{
public int terminator(int n){
return 0;
}
public int sum(int num) throws IllegalAccessException,InvocationTargetException {
List li = new ArrayList<>();
li.add(false);
li.add(true);
Method methods[] = this.getClass().getMethods();
int index = li.indexOf( num == 0);
return num+(int)methods[index].invoke(this,(--num));
}
}
面试题65_1:不用加减乘除做加法
用位运算代替:异或,求进位,再异或直至不产生进位 |
public static int Sum(int num1,int num2){
int sum = num1 ^ num2;
int carry = (num1 & num2)<<1;
while (carry != 0){
int temp = sum;
sum = sum ^ carry;
carry = (temp & carry)<<1;
}
return sum;
}
面试题65_2:不使用心得变量,交换两个变量的值
//基于加法
public static void Turn(int num1,int num2){
num1 = num1+num2; num2 = num1-num2; num1 = num1 - num2; System.out.println(num1); System.out.println(num2); }
public static void Turn(int num1,int num2){
num1 = num1^num2;
num2 = num1^num2;
num1 = num1^num2;
System.out.println(num1);
System.out.println(num2);
}
面试题66:构建乘积数组
直接遍历相乘 | o(n[2]) |
下一个要求的元素的两部分与上一个有关联,故可以在前一个的基础上求得下一个 | o(n) |
public static int[] getSecondArray(int[] nums,int[] target){
if (nums == null||target == null||nums.length != target.length || nums.length == 0){
return null;
}
//每次计算出的要进入数组。
target[0] = 1;
for (int x = 1;x=0;x--){
temp *= nums[x+1];
target[x] *= temp;
}
return target;
}