记录做题思路
记录平时做题的思路,加深印象。
如果本文中有错误的地方,或者你有更好的思路,希望你可以提出来,我们一起讨论。
目录
前言
目前做题主要使用的是LeetCode,如果以后有用到别的网站或者书籍,会标记说明的。
使用的是Java语言。
1.两数之和
题目描述:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
测试实例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
题解:
1、使用双指针解决,先给数组排序,取数组的第一个变量和最后一个变量,然后根据相加的值判断,如果大于目标值,则后面的指针向前移动,小于目标值,则前面的指针向后移动。
发现这个解法不好处理题目的返回要求,题目需要返回对应的数据下标,但是这个思路需要排序,排序会改变下标对应的数据,除非再拿个集合来存储修改后和原先数据对应的下标地址,这个就麻烦了很多,所以不予考虑这个解法。
2、使用哈希表去存储数据,遍历数组,判断表里的k有没有target - 当前数值,如果有,返回即可,没有的话就把k存储为当前值需要的另一部分的值,v为当前下标。
- 时间复杂度 : O(n)
- 空间复杂度 : O(n)
//解法一
public int[] twoSum(int[] nums, int target) {
if(nums == null && nums.length == 0)
return null;
Arrays.sort(nums);
int start = 0, end = nums.length - 1;
for(int i = 0;i < nums.length;i++){
if(nums[start] + nums[end] == target){
return new int[]{start,end};
}else if(nums[start] + nums[end] > target){
end--;
}else{
start++;
}
}
return null;
}
//解法二
public int[] twoSum(int[] nums, int target) {
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i < nums.length;i++){ //循环数组寻找需要的数据
if(map.containsKey(target - nums[i])){ //判断当前的值所对应的另一部分数据是否已经存储在哈希表里了,如果有说明找到了,返回即可,如果没有就把当前值需要的另一半数据存储进哈希表,然后继续寻找
return new int[]{map.get(target - nums[i]),i};
}else {
map.put(nums[i],i);
}
}
return null;
}
2. 两数相加
题目描述:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
测试实例:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
题解:
1、该题是用链表存储的数据,是属于比较简单的,主要需要考虑数据相加的进位问题即可解决。
建议去力扣的评论区看题解,我这题写的比较繁琐了。
- 时间复杂度 : O(n)
- 空间复杂度 : O(1)
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode ansNode = new ListNode(); //新建一个链表来存储最后的结果返回
ListNode head = ansNode;
int carry = 0; //存储进位信息
ansNode.val = (carry + l1.val + l2.val) % 10; //先处理第一个节点信息
carry = (l1.val + l2.val + carry) >= 10 ? 1 : 0;
while(l1.next != null && l2.next != null){ //只要两个链表都还有数据就运行
//取出下一个数据来运算
l1 = l1.next;
l2 = l2.next;
ansNode.next = new ListNode();
ansNode = ansNode.next;
//处理该节点信息并判断是否进位
ansNode.val = (carry + l1.val + l2.val) % 10;
carry = (l1.val + l2.val + carry) >= 10 ? 1 : 0;
}
while(l1.next != null){ //第一个链表还有数据就继续运算
l1 = l1.next;
ansNode.next = new ListNode();
ansNode = ansNode.next;
ansNode.val = (carry + l1.val) % 10;
carry = (l1.val + carry) >= 10 ? 1 : 0;
}
while(l2.next != null){ //第二个链表有数据就运行
l2 = l2.next;
ansNode.next = new ListNode();
ansNode = ansNode.next;
ansNode.val = (carry + l2.val) % 10;
carry = (l2.val + carry) >= 10 ? 1 : 0;
}
if(carry == 1){ //还有进位就加一个节点
ansNode.next = new ListNode();
ansNode = ansNode.next;
ansNode.val = 1;
}
return head;
}
}
3. 无重复字符的最长子串
题目描述:
给定一个字符串
s
,请你找出其中不含有重复字符的 最长子串 的长度。
测试实例:
输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是"abc",所以其长度为3.
题解:
1、根据该题题意可知,需要求字符串中最长的并且连续的子串,所以第一个想到了滑动数组解决。
分别需要两个指针,left、right,还需要一个set集合来判断滑动数组里是否有重复的字符。
这时可能有两种情况,第一种,新加入的字符在set里面不存在,则直接加进去,right指针+1;第二种,新加入的字符在set里面存在,则先不加入进去,把set容器里的left位置的字符先弹出,left+1,然后再做判断。
- 时间复杂度 : O(n)
- 空间复杂度 : O(n)
public int lengthOfLongestSubstring2(String s) {
if (s == null || s.length() < 1)
return 0;
Set<Character> set = new HashSet<>();
int left = 0;
int right = 0;
int max = 0;
for (int i = 0; i < s.length();) {
if (!set.contains(s.charAt(i))){ //判断set里面是否不包含该字符
set.add(s.charAt(i));
right++;
i++;
max = Math.max(max,set.size()); //只有在增加字符的时候才需要判断最大的不重复子串
}else {
set.remove(s.charAt(left)); //重复就在容器里删除最左边的字符
left++;
}
}
return max;
}
4. 寻找两个正序数组的中位数
题目描述:
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
测试实例:
输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
题解:
1、傻瓜式解题就是建立一个新数组,把两个数组放一起后点排序函数,就解决了,但很明显是错的,连题目里说的两个数组都是正序的都没用上,时间复杂度也是nlogn的。
稍微优化一下,把两个数组放一个数组里面的时候使用两个指针来添加,这样放进去的时候直接就是排好序的。
优化:题目需要的是中位数,所以没必要把两个数组的所有数据都存放进新数组里面,存储一半即可,这部分优化就留给读者自己解决了。
- 时间复杂度 : O(n+m)
- 空间复杂度 : O(n+m)
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length + nums2.length;
int[] arr = new int[n];
int n1 = 0,n2 = 0; //一个数组一个指针来记录存放进新数组的位置
for (int i = 0; i < n; i++) { //存放进新数组
if (n1 == nums1.length){ //当num1数组已经取出了所有的数据,剩下的只能是num2数组里的数据了,直接再用一个循环取num2的数据,取完直接跳出循环即可
while (i < n){
arr[i++] = nums2[n2++];
}
break;
}
if (n2 == nums2.length){ //同上
while (i < n){
arr[i++] = nums1[n1++];
}
break;
}
if (nums1[n1] < nums2[n2]){ //当两边数组都还有数据时就按照指针大小取数据
arr[i] = nums1[n1++];
}else {
arr[i] = nums2[n2++];
}
}
//System.out.println(Arrays.toString(arr)); //测试程序
if (n % 2 == 0) { //单数直接返回,双数相加后返回
return ((double) arr[n / 2 - 1] + arr[n / 2]) / 2;
}else {
return arr[n / 2];
}
}
这能击败百分之百是我没想到的,这玩意果然不准。
5. 最长回文子串
题目描述:
给你一个字符串
s
,找到s
中最长的回文子串。
测试实例:
输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
题解:
1、暴力枚举,没什么好说的。
public String longestPalindrome(String s) {
if (s.length() < 2)
return s;
//设置两个变量,一个存起始位置,一个存最大回文数
int lengthMax = 0;
int index = 0;
//每一个子串都拿出来判断
for (int i = 0; i < s.length() - 1; i++) {
for (int j = i + 1; j <= s.length(); j++) {
if (j - i > lengthMax && huiwen(s.substring(i,j))){ //子串小于最大回文数就没必要比对了,大于就再比对是否是回文串
index = i;
lengthMax = j - i;
}
}
}
return s.substring(index,index + lengthMax);
}
//判断是否是回文串
private boolean huiwen(String str){
for (int i = 0; i < str.length() / 2; i++) {
if (str.charAt(i) != str.charAt(str.length() - i - 1))
return false;
}
return true;
}
2、休息