剑指offer
日期 | 记录(更新题号) |
---|---|
2020年10月12日 | 03. 数组中重复的数字 04. 二维数组中的查找 05. 替换空格 06. 从尾到头打印链表 |
2020年10月13日 | 09. 用两个栈实现队列 10- I. 斐波那契数列 10- II. 青蛙跳台阶问题 |
2020年10月15日 | 11. 旋转数组的最小数字 |
03. 数组中重复的数字
(1)题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-th-tribonacci-number
(2)思路与解法
解法一:用一个长度为n的数组记录每个数字出现的次数
class Solution {
public int findRepeatNumber(int[] nums) {
int[] temp = new int[nums.length];
for(int i = 0; i < nums.length; i++) {
temp[nums[i]]++;
}
int res = 0;
for(int i = 0; i < nums.length; i++) {
if(temp[i] > 1) {
res = i;
break;
}
}
return res;
}
}
解法二:利用set集合的特性,将数组中的数组插入到集合中,如果插入失败,则说明集合中已有这个数,返回当前数字即可。
class Solution {
public int findRepeatNumber(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
int repeat = -1;
for (int num : nums) {
if (!set.add(num)) {
repeat = num;
break;
}
}
return repeat;
}
}
解法三(书中推荐解法):不额外占用空间。如果这个数组中没有重复的数字,那么这个数组排序之后,数字i应该出现在下标为i的位置。
从头到尾遍历整个数组,当扫描到下标为i的数字的时候,首先比较这个数字(用m表示)是不是等于i,如果不是,拿它和第m个数字比较,如果相等,则找出了一个重复的数字;如果不相等,等交换两个数字,并继续向后遍历。
class Solution {
public int findRepeatNumber(int[] nums) {
int res = -1;
for(int i = 0; i < nums.length; i++) {
if(i != nums[i]) {
int temp = nums[i];
if(nums[temp] == temp){
res = temp;
break;
}
else {
nums[i] = nums[temp];
nums[temp] = temp;
}
}
}
return res;
}
}
04. 二维数组中的查找
(1)题目描述
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
(2)思路与解法
解法一:暴力求解
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int n = matrix.length;
if(n == 0) {
return false;
}
int m = matrix[0].length;
if(m == 0) {
return false;
}
for(int i = 0; i < n; i++) {
if(matrix[i][0] > target) {
return false;
}
else if(matrix[i][m-1] < target) {
continue;
}
for(int j = 0; j < m; j++) {
if(target == matrix[i][j]){
return true;
}
}
}
return false;
}
}
解法二(书中推荐解法):从右上角开始遍历,当前数比目标数组大,则向左边遍历,当前数比目标数小,则向下遍历。
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 i = 0, j = columns - 1;
while(i < rows && j >= 0) {
if(matrix[i][j] == target) {
return true;
}else if(matrix[i][j] > target) {
j--;
}else if(matrix[i][j] < target) {
i++;
}
}
return false;
}
}
05. 替换空格
(1)题目描述
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = “We are happy.”
输出:“We%20are%20happy.”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof
(2)思路与解法
解法一:纯粹耍赖了…但是java字符串长度不可变,也只能这样了
class Solution {
public String replaceSpace(String s) {
return s.replaceAll(" ", "%20");
}
}
解法二:双指针,在现有字符串的基础上修改,用C++实现。
06. 从尾到头打印链表
(1)题目描述
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof
(2)思路与解法
解法一:用辅助栈:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int[] reversePrint(ListNode head) {
Stack<ListNode> stack = new Stack<ListNode>();
ListNode node = head;
while(node != null) {
stack.push(node);
node = node.next;
}
int size = stack.size();
int[] res = new int[size];
for(int i = 0; i < size; i++) {
res[i] = stack.pop().val;
}
return res;
}
}
解法二:递归
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
ArrayList<Integer> tmp = new ArrayList<Integer>();
public int[] reversePrint(ListNode head) {
recur(head);
int[] res = new int[tmp.size()];
for(int i = 0; i < tmp.size(); i++) {
res[i] = tmp.get(i);
}
return res;
}
void recur(ListNode head) {
if(head == null) return;
recur(head.next);
tmp.add(head.val);
}
}
09. 用两个栈实现队列10- I. 斐波那契数列
(1)题目描述
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-th-tribonacci-number
(2)思路与解法
入队列时,第一个栈做push,出队列时,将第一个栈pop并push到第二栈,第二个栈pop。
class CQueue {
Stack<Integer> st1;
Stack<Integer> st2;
public CQueue() {
st1 = new Stack<Integer>();
st2 = new Stack<Integer>();
}
public void appendTail(int value) {
while(!st2.empty()) {
st1.push(st2.pop());
}
st1.push(value);
}
public int deleteHead() {
if(st2.empty() && st1.empty()) {
return -1;
}
while(!st1.empty()) {
st2.push(st1.pop());
}
return st2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
09. 用两个栈实现队列
(1)题目描述
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:1
示例 2:
输入:n = 5
输出:5
提示:
0 <= n <= 100
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-th-tribonacci-number
(2)思路与解法
解法一:使用递归会超时,就放弃了。用一个额外的数组来保存下来了。注意题目中的“答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。”。
class Solution {
public int fib(int n) {
if(n == 0) {
return 0;
}
if(n == 1) {
return 1;
}
int[] res = new int[n+1];
res[0] = 0;
res[1] = 1;
for(int i = 2; i <= n; i++) {
res[i] = (res[i-1] + res[i-2]) % 1000000007;
System.out.println(res[i]);
}
return res[n];
}
}
解法二:动态规划,不占用额外的内存空间。
class Solution {
public int fib(int n) {
public int fib(int n) {
int a = 0, b = 1, sum = 0;
for(int i = 0; i < n; i++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
}
10- II. 青蛙跳台阶问题
(1)题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
示例 1:
输入:n = 2
输出:2
示例 2:
输入:n = 7
输出:21
示例 3:
输入:n = 0
输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-th-tribonacci-number
(2)思路与解法
动态规划,不占用额外的内存空间。其实就是个斐波那契数列,只不过第一个数字是1,而不是0,思路和上一题一模一样。
class Solution {
public int fib(int n) {
public int fib(int n) {
int a = 1, b = 1, sum = 0;
for(int i = 0; i < n; i++) {
sum = (a + b) % 1000000007;
a = b;
b = sum;
}
return a;
}
}
}
11. 旋转数组的最小数字
(1)题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-th-tribonacci-number
(2)思路与解法
解法一:从头遍历整个数组,找到第一个比前一个 数字小的即为结果。
class Solution {
public int minArray(int[] numbers) {
int res = numbers[0];
int len = numbers.length;
for(int i = 0; i < len - 1; i++) {
if(numbers[i] > numbers[i+1]) {
res = res > numbers[i+1] ? numbers[i+1] : res;
break;
}
}
return res;
}
}
解法二:可以将整个数组看做是两个递增的数组,用两个指针分别指向头i和尾部j,取中点m=(i+j)/2,如果nums[j]>nums[m],是在第二个数组中;如果是nums[m] > nums[j],则是在第一个数组中。
class Solution {
public int minArray(int[] numbers) {
int i = 0;
int j = numbers.length - 1;
while(i < j) {
if(numbers[j] < numbers[(i+j)/2]) {
i = (i + j) / 2 + 1;
} else if(numbers[j] > numbers[(i+j)/2]) {
j = (i + j) / 2;
} else {
j--;
}
}
return numbers[j];
}
}
解法二可以参考面试题11. 旋转数组的最小数字(二分法,清晰图解)