前言
本系列博客的刷题顺序根据牛客网的这篇文章来安排,附上自己整理的解题思路。
剑指 Offer 总结 - leetcode 剑指offer系列
剑指 Offer 03. 数组中重复的数字
题目
解析
利用HashSet集合元素不重复的特性
class Solution {
public int findRepeatNumber(int[] nums) {
HashSet<Integer> set = new HashSet<>();
for (int num : nums) {
if (set.contains(num)) {
return num;
} else {
set.add(num);
}
}
return -1;
}
}
剑指 Offer 04. 二维数组中的查找
题目
解析
以第一行最后一列位置的数开始,当目标数小于这个位置的数,列数减一;当目标数大于这个数,行数加一;相等就返回true
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return false;
}
int rows = matrix.length, columns = matrix[0].length;
int row = 0, column = columns - 1;
while (row < rows && column >= 0) {
int num = matrix[row][column];
if (num > target) {
column--;
continue;
} else if (num < target) {
row++;
continue;
} else {
return true;
}
}
return false;
}
}
剑指 Offer 11. 旋转数组的最小数字
题目
解析
傻瓜式……
class Solution {
public int minArray(int[] numbers) {
int minValue = numbers[0];
for (int i = 1; i < numbers.length; i++) {
if (minValue > numbers[i]) {
minValue = numbers[i];
}
}
return minValue;
}
}
剑指 Offer 14- I. 剪绳子
题目
解析
当绳子大于4时,尽可能的把它截成数字为3的小段,乘积最大。
class Solution {
public int cuttingRope(int n) {
if(n < 4) return n - 1;
int res = 1;
while (n > 4) {
res *= 3;
n -= 3;
}
return res * n;
}
}
剑指 Offer 14- II. 剪绳子 II
题目
解析
和上一个一样,结果取余
class Solution {
public int cuttingRope(int n) {
if (n < 4) {
return n - 1;
}
Long res = 1L;
int k = (int) 1e9+7;
while (n > 4) {
res = res * 3 % k;
n -= 3;
}
return (int) (res * n % k);
}
}
剑指 Offer 17. 打印从 1 到最大的 n 位数
题目
解析
首先得出最大的数k,然后指定一个k大小的数组来存放这个数组即可
class Solution {
public int[] printNumbers(int n) {
int k = 1;
for (int i = 1; i <= n; i++) {
k *= 10;
}
k -= 1;
int[] res = new int[k];
for (int i = 0; i < res.length; i++) {
res[i] = i + 1;
}
return res;
}
}
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
题目
解析
类似于快排的思路,这里指定数组开始和最后的索引,从前往后遍历数组,同时从后往前遍历。当遍历到的后面的数为奇数,和当前left索引即前面的数交换位置;当遍历到的前面的数是偶数,和当前right索引即后面的数交换位置。左右索引重合退出遍历。
class Solution {
public int[] exchange(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
while (left < right && nums[right] % 2 == 0) {
right--;
}
while (left < right && nums[left] % 2 == 1) {
left++;
}
if (left < right) {
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
}
return nums;
}
}
剑指 Offer 39. 数组中出现次数超过一半的数字
题目
解析
投票法,简单来讲,遍历数组中的数,为每一个遍历到的数投一票(count + 1),当遍历到同样的数时,再投一票,不然减一票。最后的x必然是数组中次数超过一半的数
class Solution {
public int majorityElement(int[] nums) {
int count = 0, x = 0;
for (int num : nums) {
if (count == 0) {
x = num;
}
count += x == num ? 1 : -1;
}
return x;
}
}
剑指 Offer 40. 最小的 k 个数
题目
解析
快排,然后copyOf
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
quickSort(arr, 0, arr.length - 1);
return Arrays.copyOf(arr, k);
}
private void quickSort(int[] arr, int l, int r) {
// 子数组长度为 1 时终止递归
if (l >= r) return;
// 哨兵划分操作(以 arr[l] 作为基准数)
int i = l, j = r;
while (i < j) {
while (i < j && arr[j] >= arr[l]) j--;
while (i < j && arr[i] <= arr[l]) i++;
swap(arr, i, j);
}
swap(arr, i, l);
// 递归左(右)子数组执行哨兵划分
quickSort(arr, l, i - 1);
quickSort(arr, i + 1, r);
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
剑指 Offer 41. 数据流中的中位数
题目
解析
构建大顶堆和小顶堆,大顶堆存放较小的一半数据,小顶堆存放较大的一半数据。
首先把它们当成队列来处理(poll方法)。A堆即小顶堆构建完成,把最小的数据给B堆;B堆即大顶堆构建完成,把最大的数给A堆。
最后,当遍历数组完成,这时把这两个堆当成栈处理(peek方法)当A,B堆size不同,弹出A堆堆顶数作为中位数,否则两个堆都弹出堆顶数。相加除以二。
class MedianFinder {
Queue<Integer> A, B;
public MedianFinder() {
A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
}
public void addNum(int num) {
if(A.size() != B.size()) {
A.add(num);
B.add(A.poll());
} else {
B.add(num);
A.add(B.poll());
}
}
public double findMedian() {
return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
}
}
剑指 Offer 43. 1 ~ n 整数中 1 出现的次数
题目
解析
class Solution {
public int countDigitOne(int n) {
return f(n);
}
private int f(int n) {
if (n <= 0) {
return 0;
}
String s = String.valueOf(n);
int high = s.charAt(0) - '0';
int pow = (int) Math.pow(10, s.length() - 1);
int low = n - high * pow;
if (high == 1) {
return f(pow - 1) + 1 + low + f(low);
} else {
return pow + high * f(pow - 1) + f(low);
}
}
}