# 位运算

n&1 : 判断n是否为奇数

n为偶数时，相应的最低位为0，n&1的结果就是0，

>>1 等价于 /2
<<1 等价于 *2

# 4 解决面试题的思路

## 27 二叉树的镜像

    /**
* 思路：（递归）交换每个树的左右子树
**/
public void Mirror(TreeNode tree) {
if(tree == null){
System.out.println();
}
else if(tree.left == null && tree.right ==null){
System.out.println();
}else{
TreeNode left = tree.left;
tree.left = tree.right;
tree.right = left;
if(tree.left != null){
Mirror(tree.left);
}
if(tree.right!=null){
Mirror(tree.right);
}
System.out.println(tree);
}
}


## 28 对称的二叉树

/*思路：首先根节点以及其左右子树，左子树的左子树和右子树的右子树相同
* 左子树的右子树和右子树的左子树相同即可，采用递归
* 非递归也可，采用栈或队列存取各级子树根节点
*/
boolean isSymmetrical(TreeNode root){
return isSymmetrical(root,root);
}
boolean isSymmetrical(TreeNode tree1,TreeNode tree2){
if(tree1 == null && tree2 == null){
return true;
}
if((tree1 == null || tree2 == null) || (tree1.val != tree2.val)){
return false;
}
return isSymmetrical(tree1.left,tree2.right) && isSymmetrical(tree1.right, tree2.left);
}


## 29 顺时针打印矩阵

import java.util.ArrayList;
public class Solution {
public static ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> result = new ArrayList<Integer>();
if(matrix.length==0){
return result;
}
int n = matrix.length,m = matrix[0].length;
int left = 0,up =0,right = m-1,down = n-1;
while(right>=0 && down>=0 && left <= right && up <= down){
// left - right
for(int i=left;i<=right;i++){
}
up++;
// up - down
if(up<=down){
for(int i=up;i<=down;i++){
}
}
right--;
// right - left
if(left<=right && up<= down){
for(int i=right;i>=left;i--){
}
}
down--;
// down - up
if(up<=down && left <=right){
for(int i=down;i>=up;i--){
}
}
left++;
}

return result;
}
}


## 30 包含min函数的栈

tmp的栈顶元素就是当前的最小值。

import java.util.Stack;
public class Solution {
Stack<Integer> s = new Stack<>();
Stack<Integer> tmpStack = new Stack<>();
public  void push(int node) {
s.push(node);
if((!tmpStack.isEmpty()&& node < tmpStack.peek()) || tmpStack.isEmpty()){
tmpStack.push(node);
}
}
public  void pop() {
int out = s.pop();
if(out == tmpStack.peek()){
tmpStack.pop();
}
}
public  int top() {
return s.peek();
}
public  int min() {
if(tmpStack.size()>0){
return tmpStack.peek();
}
return -1;
}
}


## 31 栈的压入、弹出序列

pushA = {1,2,3,4,5};
popA = {4,5,3,1,2};

sdata

sdata
3
2
1

squeue

squeue
4
5
    public static boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> sdata = new Stack<Integer>();
int first = popA[0];
int tmp = 0;
for(int i=0;i<pushA.length;i++){
if(pushA[i]==first) tmp=1;
}
boolean result = false;
for(int i=0;i<popA.length;i++){
int data = popA[i];
if(sdata.size()>0 && sdata.peek()==data){
sdata.pop();result = true;
}else if(!squeue.isEmpty()){
while(!squeue.isEmpty()){
int q = squeue.poll();
if(q==data){
result = true;break;
}
}
}// else
else { 	result = false;break; }
}// for
//    	System.out.println(sdata);
//    	System.out.println(squeue);
return result;
}


## 33 二叉搜索树的后序遍历序列

1. 先划分出左子树和右子树
2. 分别判断左子树和右子树是不是二叉搜索树
3. 注意情况：空树、不符合二叉搜索树定义的情况（右子树存在比根节点大的值）
// 递归判断左右子树就可以了
public boolean VerifySquenceOfBST(int [] sequence) {
return VerifySquenceOfBST(sequence,0,sequence.length-1);
}
public boolean VerifySquenceOfBST(int [] sequence,int begin,int end) {
if(sequence==null || sequence.length <=0){return false;}
int root = sequence[end];
int i=begin;
for(;i<end;i++){
if(sequence[i]>root) break;
}
// 确定右子树中的值都大于根节点
for(int j=i;j<end;j++){
if(sequence[j]<root) return false;
}
boolean left = true, right = true;
if(i>0 && i!= begin){
left = VerifySquenceOfBST(sequence,begin,i-1);
}
if(i<end){
right=VerifySquenceOfBST(sequence,i, end-1);
}
return left&&right;
}


## 34 二叉树中和为某一值的路径

import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public  ArrayList<ArrayList<Integer>> paths = new ArrayList<ArrayList<Integer>>();
public  ArrayList<Integer> path = new ArrayList<Integer>();
public  int curValue = 0;
public  ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null || target==0){
return paths;
}
curValue += root.val;
boolean isleaf = root.left==null && root.right==null;
if(isleaf && curValue==target){
}
if(root.left!=null){
FindPath( root.left,target);
}
if(root.right!=null){
FindPath( root.right,target);
}
path.remove(path.size()-1);
curValue -= root.val;
return paths;
}
}


>>>>>>> 回到目录

## 36 二叉搜索树与双向链表

	TreeNode lastNode = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null)
return null;
if(pRootOfTree.left==null&&pRootOfTree.right==null)
return pRootOfTree;
ConvertTree(pRootOfTree);
}
}
public void ConvertTree(TreeNode root) {
TreeNode current = root;
if(current.left != null){
ConvertTree(current.left);
}
current.left = lastNode;
if(lastNode != null){
lastNode.right = current;
}
lastNode = current;
if(current.right != null){
ConvertTree(current.right);
}
}

> 别人的一个简洁的答案
lastLeft 直接保存了最左边的一个节点

public class Solution {  //类似树的线索化,相当简洁
TreeNode pre=null;
TreeNode lastLeft=null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null){
return null;
}
Convert(pRootOfTree.left);
pRootOfTree.left=pre;
if(pre!=null)pre.right=pRootOfTree;
pre=pRootOfTree;
lastLeft=lastLeft==null?pRootOfTree:lastLeft;
Convert(pRootOfTree.right);
return lastLeft;
}
}


## * 38 字符串的排列

(1) 遍历出所有可能出现在第一个位置的字符（即：依次将第一个字符同后面所有字符交换）；
(2) 固定第一个字符，求后面字符的排列（即：在第1步的遍历过程中，插入递归进行实现）。

(1) 先确定递归结束的条件，例如本题中可设begin == str.size() - 1;
(2) 形如 aba 或 aa 等特殊测试用例的情况，vector在进行push_back时是不考虑重复情况的，需要自行控制；
(3) 输出的排列可能不是按字典顺序排列的，可能导致无法完全通过测试用例，考虑输出前排序，或者递归之后取消复位操作。

[外链图片转存失败(img-DQKVqDKp-1562320247848)(https://github.com/kathy775/hellokiki/blob/master/img/note/nowcoder/jianOffer38.png?raw=true)]

1. 输出要按字典排序，用TreeSet
2. set转ArrayList
import java.util.*;
public class Solution {
public  Set<String> resultSet = new TreeSet<>();
public  int begin = 0;
public  ArrayList<String> Permutation(String str) {
if(str==null || str.length()==0){
return new ArrayList<String>();
}
char tmp;
char[] chArr = str.toCharArray();
for(int i=begin;i<str.length();i++){
swap(chArr,begin,i);
begin ++;
str = new String(chArr);
Permutation(str);
begin--;
swap(chArr,begin,i);
}
return new ArrayList<String>(resultSet);
}
public void swap(char[] chArr,int i,int j){
char tmp = chArr[j];
chArr[j] = chArr[i];
chArr[i] = tmp;
}
}


>>>>>>> 回到目录

# 5 优化时间和空间效率

## 39 数组中出现次数超过一半的数字

1. 方法一 快排（递归）
利用快排的方法，得到位于 mid 的值。

2. 方法二 统计次数
2.1 见书P208
满足题意的数字，它的出现次数大于其他所有数字出现次数的和。
2.2 直接 HashMap 统计出每个数字的出现次数。只要判断 HashMap 中是否存在一个 key 对应的 value 大于长度一半。

public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int num = partition(array,0,array.length-1);
return isThanHalf(array, num);
}
// 快排，返回位于数组中 mid 的数
public  int partition(int [] array,int start,int end){
int pivote = array[start];
int mid = array.length >> 1;
int startTmp = start, endTmp = end;
while(start < end){
while(start < end && array[end]>=pivote){
end--;
}
array[start] = array[end];
while(start < end && array[start]<=pivote){
start++;
}
array[end] = array[start];
}
array[start] = pivote;
if(start == mid){
return array[mid];
}else if(start<mid){
return partition(array,start+1,endTmp);
}else {
return partition(array,startTmp,start-1);
}
}
// 判断位于 mid 的数的出现次数是否超过一半
public  int isThanHalf(int [] array,int num){
int count = 0;
for(int i : array){
if(i == num) count++;
}
if(count > (array.length>>1)) return num;
else return 0;
}
}


## 40 最小的K个数

import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
if(k>input.length || input.length==0) return result;
int[] a = partition(input,0,input.length-1,k);
for(int i=0;i<k;i++){
}
return result;
}
public int[] partition(int [] array,int start,int end,int k){
if(k>array.length-1) k=array.length-1;
int pivote = array[start];
int startTmp = start, endTmp = end;
while(start < end){
while(start < end && array[end]>=pivote){
end--;
}
array[start] = array[end];
while(start < end && array[start]<=pivote){
start++;
}
array[end] = array[start];
}
array[start] = pivote;
if(start == k){
return array;
}else if(start<k){
return partition(array,start+1,endTmp,k);
}else {
return partition(array,startTmp,start-1,k);
}

}
}


## * 43 整数中1出现的次数（从1到n整数中1出现的次数）

https://www.nowcoder.com/questionTerminal/bd7f978302044eee894445e244c7eee6

① 如果百位上数字为0，百位上可能出现1的次数由更高位决定。比如：12013，则可以知道百位出现1的情况可能是：100199，11001199,21002199，，…，1110011199，一共1200个。可以看出是由更高位数字（12）决定，并且等于更高位数字（12）乘以 当前位数（100）。
② 如果百位上数字为1，百位上可能出现1的次数不仅受更高位影响还受低位影响。比如：12113，则可以知道百位受高位影响出现的情况是：100199，11001199,21002199，，…，1110011199，一共1200个。和上面情况一样，并且等于更高位数字（12）乘以 当前位数（100）。但同时它还受低位影响，百位出现1的情况是：12100~12113,一共114个，等于低位数字（113）+1。
③ 如果百位上数字大于1（29），则百位上出现1的情况仅由更高位决定，比如12213，则百位出现1的情况是：100199,11001199，21002199，…，1110011199,1210012199,一共有1300个，并且等于更高位数字+1（12+1）乘以当前位数（100）。

（12 + 1）*1

low+1，是指10 … 1? (这个大小等于low)，+1 是指10的时候十位的1
high*i ，110 … 119 这段数字十位上是1

public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int low=0,high=0,cur=0,i=1,count=0;
while(n/i != 0){
low = n-(n/i)*i;
high = n/(i*10);
cur = n/i%10;
if(cur == 0){
count += high*i;
}else if(cur == 1){
count += low+1+high*i;
}else{
count += (high+1)*i;
}
i = i*10;
}
return count;
}
}


## 45 把数组排成最小的数

	public String PrintMinNumber(int [] numbers) {
for(int i=0;i<numbers.length;i++){
for(int j=i+1;j<numbers.length;j++){
int num1 = Integer.parseInt(numbers[i]+""+numbers[j]);
int num2 = Integer.parseInt(numbers[j]+""+numbers[i]);
if(num1 > num2){
int tmp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = tmp;
}
}
}
String result = "";
for(int i : numbers){
result += i;
}
return result;
}


## 49 丑数

1 2 3 4 5 6 [7] 8 9 10 [11] 12

    public static int GetUglyNumber_Solution(int index) {
if(index < 7)return index;
int[] res = new int[index];
res[0]=1;
int t2=0,t3=0,t5=0;
for(int i=1;i<index;i++){
res[i] = Math.min(res[t2]*2, Math.min(res[t3]*3, res[t5]*5));
if(res[i] == res[t2]*2) t2++;
if(res[i] == res[t3]*3) t3++;
if(res[i] == res[t5]*5) t5++;
}
return res[index-1];
}


## 第一个只出现一次的字符

public class Solution {
public int FirstNotRepeatingChar(String str) {
for(int i=0;i<str.length();i++){
if(str.indexOf(str.charAt(i)) ==str.lastIndexOf(str.charAt(i)))
return i;
}
return -1;
}
}


## * 51 数组中的逆序对

public class Solution {
public int InversePairs(int [] array) {
if(array.length <=0) return 0;
int[] copy = new int[array.length];
for(int i=0;i<array.length;i++) copy[i] = array[i];
int count = mergeCount(array,copy,0,array.length-1);
return count%1000000007;
}
public int mergeCount(int [] array,int [] copy,int start,int end) {
if(start==end){
copy[start]=array[start];
return 0;
}
int mid = (end-start)/2;
int left = mergeCount(copy,array,start,start+mid);
int right = mergeCount(copy,array,start+mid+1,end);

int i = start+mid;
int j = end,index = end;
int count = 0;
while(i>=start && j>=start+mid+1){
if(array[i] > array[j]){
copy[index--]= array[i--];
count += j-start-mid;
}else copy[index--]= array[j--];
}
for(;i>=start;i--)copy[index--]= array[i];
for(;j>=start+mid+1;j--)copy[index--]= array[j];
return count+left+right;
}
}


## 52 两个链表的第一个公共结点

    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
while(p1 != p2){
}
return p1;
}


## 53 数字在排序数组中出现的次数

（方法1）因为data中都是整数，所以可以稍微变一下，不是搜索k的两个位置，而是搜索k-0.5和k+0.5

(参考别人的)

（方法2）用二分法找到数字k第一次出现的位置和最后一次出现的位置，相减。

    public int GetNumberOfK(int [] array , int k) {
return binarySearch(array,k+0.5)-binarySearch(array,k-0.5);
}
public int binarySearch(int [] array , double k) {
int start = 0,end = array.length-1;
int mid = 0;
while(start<=end){ // 注意这里的条件
mid = (end-start)/2+start;
if(array[mid]>k) end = mid-1;
else if(array[mid]<k) start = mid+1;
}
return start;
}


0~n-1 中缺失的数字

## 二叉树的深度

    public int TreeDepth(TreeNode root) {
return root==null?0:(Math.max(TreeDepth(root.left),TreeDepth(root.right))+1);
}


## 平衡二叉树

public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
return getHeight(root)!=-1;
}
public int getHeight(TreeNode root) {
if(root==null)return 0;
int left = getHeight(root.left);
if(left == -1) return -1;
int right = getHeight(root.right);
if(right == -1) return -1;
return Math.abs(left-right)>1?-1:Math.max(left,right)+1;
}
}


## 57 和为S的两个数字

    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> result = new ArrayList<>();
if(array==null || array.length==0||sum==0)return result;
int start = 0,end = array.length-1;
while(end >= start){
int tmp=array[start]+array[end];
if(tmp == sum){
break;
}else if(tmp<sum)start++;
else end --;
}
return result;
}


## 57_2 和为S的连续正数序列

import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer> > result = new ArrayList<>();
int start=1,end = 2,num=3;
while(start < end){
if(num < sum){
end++;num+=end;
}else if(num > sum){
num-=start;start++;
}else{
ArrayList<Integer> nums = new ArrayList<Integer>();
for(int i=start;i<=end;i++){
}
num-=start;start++;end++;num+=end;
}
}
return result;
}
}


## 58 翻转单词顺序列

O(n), n是单词个数

public class Solution {
public String ReverseSentence(String str) {
if(str.replace(" ","").length()==0)return str;
String[] sarr = str.split(" ");
String result = "";
for(int i=sarr.length-1;i>=0;i--){
result+=sarr[i]+" ";
// if(i!=0) result+=" ";
}
return result.substring(0,result.length()-1);
}
}


startend指针，将sarr进行交换。

## 58_2 左旋转字符串

public class Solution {
public String LeftRotateString(String str,int n) {
if(n<0 || n>str.length()) return str;
String s1 = str.substring(0,n);
String s2 =  str.substring(n);
return s2+s1;
}
}


    string LeftRotateString(string str, int n)
{
int len = str.size();
if(len == 0) return str;
n %= len;
for(int i = 0, j = n - 1; i < j; ++i, --j) swap(str[i], str[j]);
for(int i = n, j = len - 1; i < j; ++i, --j) swap(str[i], str[j]);
for(int i = 0, j = len - 1; i < j; ++i, --j) swap(str[i], str[j]);
return str;
}


## 61 扑克牌顺子

1. 先排序
2. 统计0的个数zeroNum
3. 统计gapNum，也就是相邻数字之间的差。
zeroNum小于gapNum，则输出false
import java.util.Arrays;
public class Solution {
public  boolean isContinuous(int [] numbers) {
if(numbers.length==0) return false;
Arrays.sort(numbers);
int zeroNum = 0,gapNum = 0;
for(int i=0;i<numbers.length && numbers[i]==0;i++) zeroNum++;
int start=zeroNum,end=start+1;
// 为什么是 start=zeroNum？
// 因为数组前面的0不用统计gap
while(end<numbers.length){
if(numbers[start]==numbers[end]) return false;
gapNum += numbers[end]-numbers[start]-1;
start=end;end++;
}
return gapNum>zeroNum?false:true;
}
}


## * 62 圆圈中最后剩下的数

public class Solution {
public int LastRemaining_Solution(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;
}
}


06-12 81

06-24 178

02-03 3万+

04-07 292

09-22 2804

06-03 58

12-15 2177

#### 剑指Offer Java版 66题刷题笔记汇总

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

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