目录
03,数组中重复数字
思路一:
使用一个辅助的数组,数组默认初始化为0,然后遍历数字数组,每遇到一个数字,就把这个数字对应的空数组里面的数值修改为1,当再次遍历到这个数值时,说明重复,直接的返回即可。
class Solution {
public int findRepeatNumber(int[] nums) {
int [] tempArr = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
if (tempArr[nums[i]] == 0) {
tempArr[nums[i]] = 1;
} else {
return nums[i];
}
}
return 0;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)
思路二:
由于只需要找出数组中任意一个重复的数字,因此遍历数组,遇到重复的数字即返回。为了判断一个数字是否重复遇到,使用集合(哈希表)存储已经遇到的数字,如果遇到的一个数字已经在集合中,则当前的数字是重复数字。
- 初始化集合为空集合,重复的数字 repeat = -1
- 遍历数组中的每个元素:
- 将该元素加入集合中,判断是否添加成功。
- 如果添加失败,说明该元素已经在集合中,因此该元素是重复元素,将该元素的值赋给 repeat,并结束遍历。
class Solution {
public int findRepeatNumber(int[] nums) {
Set set=new HashSet();
int repeat=-1;//初始化重复的元素为-1
for(int num:nums){
if(!set.add(num)){
//如果添加成功,方法返回true,否则返回false
repeat=num;
}
}
return repeat;
}
}
- 时间复杂度:O(n)
- 遍历数组一遍。使用哈希集合(HashSet),添加元素的时间复杂度为 O(1),故总的时间复杂度是 O(n)。
-
空间复杂度:O(n)。
-
不重复的每个元素都可能存入集合,因此占用 O(n)额外空间。
-
04,二维数组中查找
待补充
05,替换空格
思路一:先求出题目中字符串的长度,然后对字符串中逐个字符进行判断,如果遇到空字符,就在新的字符串末尾追加%20,直到遍历完整个字符串,然后把新的字符串赋值给s串即可。
class Solution {
public String replaceSpace(String s) {
int len=s.length();
String str=s;
s="";
for(int i=0;i<len;i++){
if(str.charAt(i)== ' '){
s=s.concat("%20");
}else{
s=s.concat(str.charAt(i)+"");
}
}
return s;
}
}
-
时间复杂度:O(n),对字符串数组进行一次遍历。
-
空间复杂度:O(n),申请和字符串数组相同容量的空间。
思路二:
可以先把字符串对象转换为一个字符数组,然后对数组中的字符逐个遍历,使用StringBuilder来接受新的字符串对象,每次遇到空格时,就追加%20即可。
class Solution {
public String replaceSpace(String s) {
char array[]=s.toCharArray();//字符串转换字符数组方法
//stringBuilder每一次都在原始字符串上追加,不会创建新的字符串
StringBuilder stringBuilder=new StringBuilder();
for (char ch:array){//增强的for循环
if(ch == ' ')
{
stringBuilder.append("%20");
}else {
stringBuilder.append(ch);
}
}
s=stringBuilder.toString();
return s;
}
}
- 时间复杂度:O(n);
- 空间复杂度:O(n);
06,倒序打印链表
思路:借助于栈先进后出的特点,遍历链表,把链表元素全部压入栈中,然后在逐个的把栈中元素放到数组中返回。
class Solution {
//此链表带头节点
public int[] reversePrint(ListNode head) {
//借助于栈的先进后出
Stack stack=new Stack();
while (head!= null){
stack.push(head.val);
head=head.next;
}
int arr[]=new int[stack.size()];
int i=0;
while (!stack.isEmpty()){
arr[i++]=(int)stack.pop();
}
return arr;
}
}
- 时间复杂度:O(n);
- 空间复杂度:O(n);
拓展思路:可以使用数组,先遍历链表长度,然后开辟数组空间,从后往前赋值数组。
07,重建二叉树
待补充
09,两个栈实现队列
思路:
让第一个栈的栈底作为队列的尾部,第一个栈的栈顶作为队列的头部,每一次入队列元素直接入栈顶即可,当需要删除队列尾部的元素的时候,借助于第二个栈,先把第一个栈元素全部压入第二个栈,然后抛出第二个栈的栈顶元素,最后重新把第二个栈的所有元素压入第一个栈即可。
class CQueue {
Stack stack01;
Stack stack02;
//栈元素进行初始化,把所有元素全部放在stack01中
public CQueue() {
stack01=new Stack<Integer>();
stack02=new Stack<Integer>();
}
public void appendTail(int value) {
stack01.push(value);
}
public int deleteHead() {
int value=0;
if(stack01.isEmpty())
value=-1;
else {
while (!stack01.isEmpty()){
stack02.push(stack01.pop());
}
value=(int)stack02.pop();//删除栈顶的元素
while (!stack02.isEmpty()){
stack01.push(stack02.pop());
}
}
return value;
}
}
- 入队列元素:时间复杂度:O(1)
- 出队列元素:时间复杂度:O(N)
10-1,斐波那契数列(动态规划)
思路:根据题目说明,如果使用递归调用的话,当n大于30时候,很可能会超时,因为做了很多的重复计算,所以在这里考虑使用动态规划法:
- 因为f0=0,f1=1,所以f2=f0+f1=1,f3=f1+f2=2,依此递增循环推导下去,最终就能推导出fn的值
- 因为结果需要对1000000007取模,所以为了防止溢出,每次求出fn>=1000000007时,则可将fn减去1000000007,相当于在此处取模,这跟最终得到结果再取模结果是一样的, 因为fn最终肯定等于求得的值加上m个1000000007的和。
- 循环结束res即为最终取模后的结果。
class Solution {
public int fib(int n) {
int f_2=0,f_1=1;
int res=0;
for(int i=0;i<n;i++){
res=f_2+f_1;
if(res>1000000007)
res-=1000000007;
f_1=f_2;
f_2=res;
}
return res;
}
}
- 时间复杂度:O(n);
- 空间复杂度:O(1);
10-2,青蛙跳台阶问题(递归记忆法)
思路:
- 设跳上 n级台阶有 f(n)种跳法。在所有跳法中,青蛙的最后一步只有两种情况: 跳上 1级或 2级台阶。
- 当为 1级台阶: 剩n−1个台阶,此情况共有 f(n−1)种跳法;
- 当为 2级台阶: 剩 n−2个台阶,此情况共有 f(n−2)种跳法。
- f(n)为以上两种情况之和,即 f(n)=f(n−1)+f(n−2),以上递推性质为斐波那契数列。本题可转化为 求斐波那契数列第n项的值
- 青蛙跳台阶问题: f(0)=1,f(0)=1,f(0)=1
- 斐波那契数列问题: f(0)=0, f(1)=1, f(2)=1
class Solution {
public int numWays(int n) {
//本题目实际就是菲波那切数列的变体,使用数组方法
if(n == 0 || n == 1)
return 1;
else {
int[] arr = new int[n + 1];
arr[0] = 1;
arr[1] = 1;
for (int i = 2; i < n + 1; i++) {
arr[i] = (arr[i - 1] + arr[i - 2])%1000000007;
}
return arr[n];
}
}
- 时间复杂度:O(n);
- 空间复杂度:O(n);
小结:对于类似菲波那切数列这种问题的解法
- 使用递归的方法,把求解f(n)的问题转化为求解f(n-1)和f(n-2)两个子问题,递归进行计算,使用f(0)和f(1)作为出口条件。
- 缺点:递归进行大量的重复计算,浪费时间。
- 记忆递归法:这种方法是在递归方法的基础上增加一个辅助数组,每一次把计算的结果保存在数组中,下次需要在计算某一个值的时候直接拿出来使用,不需要重复计算。
- 缺点:保存计算出的数值需要O(n)的空间。
- 动态规划法:只记录最近需要的数值,不需要开辟空间,时间复杂度是O(1)