1.两数之和
题解:
①题目简单的点在于每种输入对应一个答案,排除复杂情况 (现在想数组中就算有重复的数存在,循环从前面开始,重复的数只会取前面的数,不存在复杂情况)
②不能重复利用数组中相同的元素,即排除元素自身相加减
基于以上两点,最先想到的是暴力破解,双重循环,筛选数值
补充知识点:要返回下标形式[i,j]
所以函数里面用 return new int[]{i,j};
看了题解之后,还有另外两种方法,一遍哈希表和两边哈希表,代码都附上
代码:
暴力破解法
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] == target - nums[i]) {
return new int[] { i, j };
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
时间复杂度 O(n*n),空间复杂度O(1)
ps:不会表示平方那个2,就用n*n代替了
一遍哈希表
刚开始的map是空的,随着循环填充,然后一步步判断。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map=new HashMap<>();
int temp=0;
for(int i=0;i<nums.length;i++){
temp=target-nums[i];
if(map.containsKey(temp))
return new int[]{map.get(temp),i};
map.put(nums[i],i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
时间复杂度 O(n),空间复杂度O(n)
两遍哈希表
一开始就把数组赋值给map;
然后一遍循环比较差值是否在map中,并且还要保证不是同一个数
这个思路比一边哈希表好理解一点
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map=new HashMap<>();
for(int j=0;j<nums.length;j++)
map.put(nums[j],j);
int temp=0;
for(int i=0;i<nums.length;i++){
temp=target-nums[i];
if(map.containsKey(temp) && (map.get(temp)!=i))
return new int[]{map.get(temp),i};
}
throw new IllegalArgumentException("No two sum solution");
}
}
时间复杂度 O(n),空间复杂度O(n)
综合比较
运行后,三种方法的运行时间以及运行实际占用内存如图:
从上到下依次是:两遍哈希,一遍哈希,暴力破解
2.两数之和
题解:
①数字用链表存储,一个节点存储一位数,逆序存储,这个存储顺序遍历起来其实跟我们数学逻辑上两数相加的顺序是一致的,这样的话只要考虑进位问题以及链表是否为空。
②返回也要返回一个链表。这里引入知识点:哑节点(dummyNode)
-
链表的第一个node,因为没有前驱节点,所以该node需要特殊处理,会导致额外的代码量。如果创建一个dummy,将其作为第一个node的前驱节点,这样链表中所有的node都可以也能够同样的逻辑来处理了。
即,哑节点一直是头结点的前节点。简化了代码逻辑,不需要考虑头节点的特殊情况。例如插排的时候(https://blog.csdn.net/TypantK/article/details/88562891) -
链表返回,return dummyNode.next
代码:
/* -----------------------------------
* WARNING:
* -----------------------------------
* Your code may fail to compile
* because it contains public class
* declarations.
* To fix this, please remove the
* "public" keyword from your class
* declarations.
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);//定义哑节点
ListNode p=l1,q=l2,cur=dummyHead;
int carry=0;
while(p!=null||q!=null){
int x=(p!=null)?p.val:0;
int y=(q!=null)?q.val:0;
int sum=x+y+carry;
carry=sum/10;
cur.next=new ListNode(sum%10);
if(p!=null)p=p.next;
if(q!=null)q=q.next;
cur=cur.next;
}
if(carry>0){
cur.next=new ListNode(carry);
}
return dummyHead.next;
}
}
3.无重复字符的最长子串
题解:
ps:知识点:
函数charAt(i);返回字符串指定索引处的字符。索引范围为从 0 到 length() - 1。
方法有三个
法一 暴力破解法
遍历字符全的所有子串,看看子串里面有没有重复的字符,没有的话记录一下子串的长度,遍历完,得到最长子串的长度。
class Solution {
public int lengthOfLongestSubstring(String s) {
int n=s.length();
int ans=0;
for (int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(isrepeat(s,i,j)){
ans=Math.max(ans,j-i);
}
}
}
return ans;
}
public boolean isrepeat(String s,int start,int end){
Set<Character> set = new HashSet<>();
for (int i = start; i < end; i++) {
Character ch = s.charAt(i);
if (set.contains(ch)) return false;
set.add(ch);
}
return true;
}
}
时间复杂度:O(nnn)
法二:滑动窗口
法三:优化滑动窗口