我是一个算法小白。在学习了Java的基本语法之后,为了学以致用,今天开始在LeetCode上进行Java刷题。为了记录自己的刷题经验和心得,我打算在CSDN这个平台上写笔记,公共的平台使我更认真对待自己的笔记,也是一个见证自己成长的好地方。
开始刷题后,发现学习时很多本以为学会的地方还是忘记如何使用,不过也有很多读书时的想法在刷题过程中得到验证。算法是程序的灵魂,这句话总结得真是一点不错。希望在经过练习之后,我也能在力扣的算法赛场上证明自己(ง •_•)ง。
两数之和给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
自己暴力求解的就没有必要细说了,谁都想得到。从第一个开始依次固定元素,和他后边的每个元素尝试相加,这样最坏需要有(n-1)*(n-2)/2次查找,n是数组的元素个数。这样时间复杂度是O(N^2),空间复杂度是O(1)。
官方给出的一个优秀解法用到了哈希表,感谢Java提供的数据结构支持。
建立整型与整型映射的哈希表,键用来表示数据,值用来表示数据的下标。用for循环遍历数组元素,如果target-num[i]出现在了哈希表的Key中,就可以返回此键对应的下标hashTable.get(target-num[i])和当前数组元素的下标i,否则就把这对新的键-值对添加到哈希表中。
核心步骤就是这样,由于题目必然会有符合要求的结果,因此也不必考虑过多的特殊情况,题目代码如下:
class Solution {
public int[] twoSum(int[] nums, int target){
Map hashTable = new HashMap();
for(int i=0;i
链表形式的两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
学校里密码学的课上多了,看到题目之后,先想到的是加法运算需要写几进制的,看了看下边的图例和样例,应该就是普通的十进制加法。从前往后把两个链表里的数字和进位位相加,用模10的方法保留本位和,用/10的方法得到进位位是0还是1。
再看看ListNode的类定义:
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
只有数值和下一位指针两个元素,代码如下:
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head=null,tail=null;
int carry=0;
while(l1!=null || l2!=null){
int n1 = l1!=null? l1.val : 0;
int n2 = l2!=null? l2.val : 0;
int sum =n1+n2+carry;
if(head==null){
head=tail=new ListNode(sum%10);
}else{
tail.next=new ListNode(sum%10);
tail=tail.next;
}
carry=sum/10;
if(l1!=null)l1=l1.next;
if(l2!=null)l2=l2.next;
}
if(carry>0)tail.next=new ListNode(carry);
return head;
}
}
这段代码来自官方解答,虽然和我自己写的基本思路一样,但是明显这个代码更精炼,有几个值得我学习的地方:
首先,int n1 = l1!=null? l1.val : 0;这一条语句实现的是两个链表的对齐。在这种方式下,就无需考虑那一条链表位数少,先到尾部;而我在写程序时,没有对齐,而是在一个链表到达尾部之后,把另一条链表的高位依次接到结果链表上,没有复用到前期的代码,而且增加了很多臃肿的判断。第二就是变量的声明和使用恰到好处。从前在看书的时候,接受了一个观点,就是变量都拿到最前面声明是最好的;而在这个程序里,像n1,n2,sum这些变量随用随设,明显对编程人员更加友好。其实原来的执念还有一个原因,就是我总觉得把变量提前声明,尤其是先在循环之外声明了,有助于提高执行速度,学习了计组,还是不知道这种想法有无道理。但是在这种情况下,可读性所占的评价比重明显更胜一筹。第三就是很多老哥会忘记最高位的进位这种边界情况确实值得注意。
无重复字串的最长字串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
根据题目,字符串包含ASCII的可打印字符,也可能是空串。再次感谢Java提供的HashSet数据结构,使用滑动窗口,代码如下。
class Solution {
public int lengthOfLongestSubstring(String s) {
Set occ = new HashSet();
int rk=-1,ans=0;
for(int i=0;i
i代表左指针,rk代表右指针。右指针依次右移,把不在集合中的元素纳入集合,字串长度+1;当出现重复元素时,保留最长记录,左指针所指元素从集合删去并右移一位;直到右指针到达终点。
此做法利用了集合,回头看力扣的评论区,有位老哥是这样做的:
class Solution {
public int lengthOfLongestSubstring(String s) {
int[] last=new int[128];
int start=0,res=0;
for(int i=0;i
首先,有人说这个128不准确,字符打印出来可能不止128个,不过我看题目所给信息,按常理应该都是ASCII码,而且这个假设也被验证是正确的。
这段代码是第一个的改进版,就是说还是采用滑动窗口的基本思路,计算子串长度仍使用i-start+1。这个last数组存放的是字符位置相关信息,准确地说,是下一次出现此字符时,参与计算的子串开始位置。
比如说,i=0对应的字母是a,那么last[97]存放的就是1,假设这期间没有出现任何重复字符,那么在i=10再次遇到a时,start通过Math.max(0,1)得到1,即这段子串从a的下一位开始计算长度,避免了串内重复字符的出现。
这个方法使用最简单的数据结构降低了代码执行时间,评论区还有很多优质讨论内容,不得不说LeetCode牛人真多。
写了三个多小时,就写了这么点,明天再接再厉吧!



