03. 数组中重复的数字
题目描述:
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。 数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
思路:认真看题目描述!!!
我的思路(菜逼思路):
也是最简单的思路,直接排序,然后查找,找到两个相邻元素相同,则为重复元素,直接返回即可(时间复杂度为 O(nlogn),空间复杂度为 O(n))
进阶思路:
通过看题目描述可知,数组长度为 n,且所有数字都在 0~n-1 的范围内,所以,我们可以每次判断索引为 i 的元素是否为 i,如果不是(假定该索引元素为 n),则判断该元素索引为 n 的元素是否相等,如果相等,则为重复元素,直接返回;如果不相等,则将该元素与索引为 n 的元素交换,直到换到索引为 i 的元素是 i 为止(时间复杂度为 O(n),空间复杂度为 O(1))
如:
[2, 3, 1, 0, 2, 5, 3]
索引 0 的元素为 2
则将其索引为 2 的元素交换
此时:[1, 3, 2, 0, 2, 5, 3]
索引 0 的元素为 1
将其与索引为 1 的元素交换
此时:[3, 1, 2, 0, 2, 5, 3]
索引 0 的元素为 3
将其与索引为 3 的元素交换
此时:[0, 1, 2, 3, 2, 5, 3]
索引 0 的元素为 0
继续判断下一个
... ... ... ... ... ... ...
一直索引 4,其元素为 2
则将其与索引为 2 的元素交换
此时索引 2 的元素为 2
与该数据相等,所以该数据重复,直接返回
题解:
//题解(菜逼思路解法)
class Solution {
public int solution(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
Arrays.sort(nums);
int index = 0;
while (index < nums.length - 1) {
if (nums[index] == nums[++index]) {
break;
}
}
return nums[index];
}
}
//题解2(进阶思路解法)
class Solution {
public int solution(int[] nums) {
if (nums == null || nums.length < 2) {
return 0;
}
int index = 0;
int temp = 0;
while (index < nums.length) {
if (nums[index] != index) {
if (nums[index] == nums[nums[index]]) {
break;
}
temp = nums[index];
nums[index] = nums[nums[index]];
nums[temp] = temp;
} else {
index++;
}
}
return nums[index];
}
}
04. 二维数组中的查找
题目描述:
在一个 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。
思路:认真看题目描述!!!
我的思路(菜逼思路):
暴力解法,直接遍历二维数组所有元素,查找是否存在目标元素(时间复杂度为 O(nm),空间复杂度为 O(1))
进阶思路(二叉查找树):
仔细观察示例矩阵,可发现,从右上角看的话,该矩阵类似于一棵二叉查找树(左子数的节点都小于根节点,右子树的节点都大于根节点),可以借助于二叉查找树的思想对目标元素进行查找(时间复杂度为 O(n+m),空间复杂度为 O(1)):
如果目标元素小于当前元素,则往当前元素左侧查找;
如果目标元素大于当前元素,则往当前元素右侧查找;
如果目标元素等于当前元素,则返回true;
如果最终未查找到目标元素,则返回false。
题解:
//题解1(菜逼思路解法)
class Solution {
public boolean solution(int[][] nums,int target) {
if (nums == null || nums.length == 0) {
return false;
}
for (int[] num : nums) {
for (int i : num) {
if (i == target) {
return true;
}
}
}
return false;
}
}
//题解2(进阶思路解法)
class Solution {
public boolean solution(int[][] nums,int target) {
if (nums == null || nums.length == 0) {
return false;
}
int i = 0;
int j = nums[i].length - 1;
while (i < nums.length && j >= 0) {
if (nums[i][j] == target) {
return true;
} else if (nums[i][j] < target) {
i++;
} else {
j--;
}
}
return false;
}
05. 替换空格
请实现一个函数,把字符串
s
中的每个空格替换成 “%20”。
示例:
输入:s = "We are happy."
输出:"We%20are%20happy."
思路:
我的思路:
没有找到思路!我可真是个小菜鸡!
思路 1:
创建一个 StringBuilder 对象,遍历字符串,如果不是空格,则直接拼接,如果是空格,则拼接 "%20",最后将 StringBuileder 转换成字符串返回即可
思路 2:
先遍历一遍字符串,查找其中空格个数
初始化一个字符数组,长度为字符串的长度加上两倍空格长度(空格本身占一个长度,这样才能放下替换空格的 "%20")
创建两个指针分别指向字符串的末尾以及数组的末尾
逆序遍历字符串,如果不是空格,则直接填入数组,如果是空格,则分别向数组中填入 '0'、'2'、'%'
最后将字符数组转为字符串返回即可
题解:
//题解1
class Solution {
public String solution(String s) {
if (s == null || s.length() == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != ' ') {
sb.append(s.charAt(i));
} else {
sb.append("%20");
}
}
return sb.toString();
}
}
//题解2
class Solution() {
public String solution(String s) {
if (s == null || s.length() == 0) {
return "";
}
int count = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
count++;
}
}
char[] ch = new char[s.length() + 2 * count];
int i = s.length() - 1;
int j = ch.length - 1;
//因为前面数组的长度是绝对避免产生数组下标越界异常的
//所以此处循环只需判断 i 即可
while (i >= 0) {
if (s.charAt(i) != ' ') {
ch[j--] = s.charAt(i--);
} else {
ch[j--] = '0';
ch[j--] = '2';
ch[j--] = '%';
i--;
}
}
return String.valueOf(ch);
}
}
06. 从尾到头打印链表
题目描述:
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例:
输入:head = [1,3,2]
输出:[2,3,1]
思路:
我的思路:
先遍历链表,获取链表长度,创建数组
再次遍历链表,获取链表节点的数据,将其从后往前依次填入数组中
另一思路:
使用栈,先将链表节点全部存入栈中,然后依次取出,将值存入数组
题解:
//题解1
class Solution {
public int[] solution(ListNode head) {
ListNode node = head;
int len = 0;
while (node != null) {
len++;
node = node.next;
}
int[] nums = new int[len];
node = head;
while (node != null) {
nums[--len] = node.val;
node = node.next;
}
return nums;
}
}
//题解2
class Solution {
public int[] solution(ListNode head) {
Stack<ListNode> stack = new Stack<>();
ListNode node = head;
while (node != null) {
stack.push(node);
node = node.next;
}
int[] nums = new int[stack.size()];
int index = 0;
while (stack.size() != 0) {
nums[index++] = stack.pop().val;
}
return nums;
}
}
10. 斐波拉契数列【青蛙跳台阶】
题目描述:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
示例:
输入:n = 2
输出:1
输入:n = 5
输出:5
思路:
我的思路(递归):
最容易想到的肯定是递归了,但是递归会随着传入参数值的增大,程序执行的时间呈几何级数递增
进阶思路(动态规划):
状态定义: 设 dp 为一维数组,其中 dp[i] 的值代表 斐波那契数列第 i 个数字 。
转移方程: dp[i + 1] = dp[i] + dp[i - 1] ,即对应数列定义 f(n + 1) = f(n) + f(n - 1) ;
初始状态: dp[0] = 0, dp[1] = 1 ,即初始化前两个数字;
返回值: dp[n] ,即斐波那契数列的第 n 个数字。
由于 dp 列表第 i 项只与第 i-1 和第 i-2 项有关,因此只需要初始化三个整形变量 res, pre, temp,利用辅助变量 res 使 pre, temp 两数字交替前进即可.
题解:
//题解1
class Solution {
public int solution(int n) {
if (n == 1) return 1;
if (n == 0) return 0;
return solution(n - 1) + solution(n - 2);
}
}
//题解2
class Solution {
public int solution(int n) {
int res = 0;
int pre = 1;
int temp = 0;
for (int i = 0; i < n; i++) {
res = temp + pre;
temp = pre;
pre = res;
}
return temp;
}
}
11. 旋转数组的最小数字
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例:
输入:[3,4,5,1,2]
输出:1
输入:[2,2,2,0,1]
输出:0
思路:
我的思路 1:
先将数组排序,然后输出数组的第一个元素(其实没有意义,因为本来就是已排序的数组的旋转,此处不再实现)
我的思路 2:
该数组是已经排好序数组的旋转,则虽然旋转后位置虽然变了,但是除了旋转位置外,其他位置都是前一个元素小于等于后一个元素,所以我们只需找到旋转位置,即为最小元素;如果没找到这个位置,则说明当前数组并未发生旋转,所以最小数字就是数组第一个元素
进阶思路(二分查找):
每次判断数组中间元素与最右侧元素的大小关系(只能与右侧比较,旋转后,小元素都会转到右侧,我一开始就是尝试的与左侧元素比较,结果测试用例 [1, 3, 5] 没通过):
如果中间元素大于最右侧元素,则说明最小数字在右侧,更新左侧索引为中间索引加1
如果中间元素小于最右侧元素,则说明最小数字在左侧,更新右侧索引为中间索引
如果中间元素等于最右侧元素,此时无法判断最小数字的区域,所有,将右侧索引左移,再重新比较
题解:
//题解1
class Solution {
public int solution(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] > nums[i + 1]) {
return nums[i + 1];
}
}
return nums[0];
}
}
//题解2
class Solution {
public int solution(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int l = 0;
int r = nums.length - 1;
int mid = 0;
while (l < r) {
mid = l + (r - l) >> 2;
if (nums[mid] > nums[r]) {
l = mid + 1;
} else if (nums[mid] < nums[l]) {
r = mid;
} else {
r--;
}
}
return nums[l];
}
}