剑指offer(简单到复杂顺序题解)
15.二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res=0;
while(n!=0){
res += n & 1;
n >>>= 1;
}
return res;
}
}
09.用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
class CQueue {
LinkedList<Integer> A, B;
public CQueue() {
A = new LinkedList<Integer>();
B = new LinkedList<Integer>();
}
public void appendTail(int value) {
A.addLast(value);
}
public int deleteHead() {
if(!B.isEmpty()) return B.removeLast();
if(A.isEmpty()) return -1;
while(!A.isEmpty())
B.addLast(A.removeLast());
return B.removeLast();
}
}
57.和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。序列内的数字由小到大排列,不同序列按照首个数字从小到大排列
输入:target = 9
输出:[[2,3,4],[4,5]]
class Solution {
public int[][] findContinuousSequence(int target) {
List<int[]> vec = new ArrayList<int[]>();
int sum = 0, limit = (target - 1) / 2; // (target - 1) / 2 等效于 target / 2 下取整
for (int i = 1; i <= limit; ++i) {
for (int j = i;; ++j) {//死循环
sum += j;
if (sum > target) {
sum = 0;
break;
} else if (sum == target) {
int[] res = new int[j - i + 1];//确定数组容量
for (int k = i; k <= j; ++k) {
res[k - i] = k;
}
vec.add(res);
sum = 0;
break;
}
}
}
return vec.toArray(new int[vec.size()][]);
}
}
68.二叉树的最近公共祖先
思路:若 root 是p,q 的 最近公共祖先 ,则只可能为以下情况之一:
p 和 q 在 root的子树中,且分列 root 的 异侧(即分别在左、右子树中);
p=root ,且 q 在 root 的左或右子树中;
q=root ,且 p 在 root 的左或右子树中;
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(left == null)return right;
if(right == null) return left;
return root;
}
}
68.2 二叉搜索树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while(root != null){
if(root.val < p.val && root.val < q.val){
root = root.right;
}else if(root.val > p.val && root.val > q.val){
root = root.left;
}else break;
}
return root;
}
}
32.2从上到下打印二叉树
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
List<Integer> tmp = new ArrayList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
}
39.数组中出现次数超过一半的数字
该题有三种方法:1.哈希表统计表法 2.数组排序法 3.摩尔投票法
其中摩尔投票法为最优解法
class Solution {
public int majorityElement(int[] nums) {
int votes = 0,x = 0;
for(int item:nums){
if(votes==0) x=item;
votes += item == x?1:-1;
}
return x;
}
}
class Solution {
public int majorityElement(int[] nums) {
int x = 0, votes = 0, count = 0;
for(int num : nums){
if(votes == 0) x = num;
votes += num == x ? 1 : -1;
}
// 验证 x 是否为众数
for(int num : nums)
if(num == x) count++;
return count > nums.length / 2 ? x : 0; // 当无众数时返回 0
}
}
03.数组中重复数字
要求输出任意一个重复数字即可
1.暴力法
class Solution {
public int findRepeatNumber(int[] nums) {
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i]==nums[j])return nums[i];
}
}
return 1;
}
}
2.哈希查找
public int findRepeatNumber(int[] nums) {
// 1. 初始化一个哈希表
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
// 2. 判断当前元素是否已经存在
if (set.contains(nums[i])) {
// 如果存在,则直接返回
return nums[i];
}
// 否则的话,将当前元素放入到哈希表中,方便后续的查找判重
set.add(nums[i]);
}
return -1;
}
3.原地置换
如果没有重复数字,那么正常排序后,数字i应该在下标为i的位置,所以思路是重头扫描数组,遇到下标为i的数字如果不是i的话,(假设为m),那么我们就拿与下标m的数字交换。在交换过程中,如果有重复的数字发生,那么终止返回ture
class Solution {
public int findRepeatNumber(int[] nums) {
int temp;
for(int i=0;i<nums.length;i++){
while (nums[i]!=i){
if(nums[i]==nums[nums[i]]){
return nums[i];
}
temp=nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
}
57.和为s的两个数
输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。
对撞指针法
class Solution {
public int[] twoSum(int[] nums, int target) {
int i=0,j=nums.length-1;
while(i < j){
int s=nums[i] + nums[j];
if(s < target){
i++;
}else if(s > target){
j--;
}else{
return new int[]{nums[i],nums[j]};
}
}
return new int[0];
}
}
62.圆圈中最后剩下的数字(约瑟夫环)
动态规划问题
递推公式
dp[i] =(dp[i-1]+m)%n
dp[1]=0;
class Solution {
public int lastRemaining(int n, int m) {
int x = 0;
for(int i=2;i<=n;i++ ){
x=(x + m) % i;
}
return x;
}
}