LeetCode刷题笔记(2)
背景
今天刷题刷了大概4道半,双指针的题目结束了,刚刚开始了快速选择的题目。
141、给定一个链表,判断链表中是否有环
//ListNode的数据格式
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public class LinkedListCycle141 {
private Object HashSet;
public boolean hasCycle(ListNode head){
Set<ListNode> nodesSeen = new HashSet<>();
//遍历ListNode哈希表中存储每个节点的内存地址
while (head != null){
if(nodesSeen.contains(head)){
return true;
}else{
nodesSeen.add(head);
}
head = head.next;
}
return false;
}
//总结:这个方法利用Hash表中每个节点都有独特的引用(或者内存地址)
/*
方法二:双指针
通过使用具有 不同速度 的快、慢两个指针遍历链表,
空间复杂度可以被降低至 O(1)。
慢指针每次移动一步,而快指针每次移动两步。
如果列表中不存在环,最终快指针将会最先到达尾部,此时我们可以返回 false。
*/
public boolean hasCycle2(ListNode head){
if(head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next;
while(slow != fast){
if(fast == null || fast.next == null){
return false;
}
slow = slow.next;
fast=fast.next.next;
}
return true;
}
}
//总结:这个方法利用快指针和慢指针,主要的思想就是如果是环,那么快指针一定能够追上慢指针。困惑的地方在于最后为什么是fast.next.next
88、MergeSortedArra
/**
- Input:
- nums1 = [1,2,3,0,0,0], m = 3
- nums2 = [2,5,6], n = 3
- Output: [1,2,2,3,5,6]
*/
/*
方法二 : 双指针 / 从前往后
最直接的算法实现是将指针p1 置为 nums1的开头,
nums2的开头,在每一步将最小值放入输出数组中。
*/
public class MergeSortedArray88 {
public void merge(int[] nums1,int m,int[] nums2,int n){
int[] nums11_copy = new int[m];
System.arraycopy(nums1,0,nums11_copy,0,m);
int p1 = 0;
int p2 = 0;
int p= 0;
while((p1<m)&&(p2<n))//nums11_copy与nums2进行比较
nums1[p++] = (nums11_copy[p1] < nums2[p2] )? nums11_copy[p1++]:nums2[p1++];
if(p1<m)
System.arraycopy(nums11_copy,p1,nums1,p1+p2,m+n-p1-p2);
if(p2<n)
System.arraycopy(nums2,p2,nums1,p1+p2,m+n-p1-p2);
}
//总结:这个方法比较繁琐,另外开辟了一个nums11_copy数组,
//比较nums1和nums2中的元素,谁小就把谁放到nums11_copy当中。
/*
方法三 从后往前
这里的指针 p 用于追踪添加元素的位置。
*/
public void merge2(int[] nums11,int m, int[] nums22, int n){
int p1 = m-1;
int p2 = n-1;
int p = m+n-1;
while ((p1 >= 0) && (p2 >= 0))
nums11[p--] = (nums11[p1]<nums22[p1]) ? nums22[p2--]:nums11[p1--];
System.arraycopy(nums22,0,nums11,0,p2+1);
//总结:从后往前比第一个方法方便得多,
//p用来追踪添加元素的位置,p1和p2分别是针对nums11和nums22
}
}
524、查看是否有匹配的字符
/**
- Input:
- s = “abpcplea”, d = [“ale”,“apple”,“monkey”,“plea”]
- Output:
- “apple”
*/
import java.util.List;
/*
解题思路:
只要利用两个指针i,j,一个指向s字符串,
一个指向sstr字符串,每一次查找过程中,i依次后移,
若i,j对应的两个字符相等,则j后移,
如果j可以移到sstr.length(),
那么说明sstr中对应的字符s中都有,
即s中删除一些字符后,
可以得到sstr字符串,
最后一步就是比较当前的结果字符与找到的sstr字符,
按照题目的需求来决定是否改变结果字符,是不是还挺简单的呀。
*/
public class StringfindLongestWord524 {
public String findLongestWord(String s, List<String> d) {
String str = "";
for (String sstr : d) {
for (int i = 0, j = 0; i < s.length() && j < sstr.length(); i++) {
if (s.charAt(i) == sstr.charAt(j)) j++;
if (j == sstr.length()) {
if (sstr.length() > str.length() || (sstr.length() == str.length() && str.compareTo(sstr) > 0))
str = sstr;
}
}
}
return str;
}
// 总结:这一步比较特殊,
//主要是为了满足题目需求的长度最长而且序号在前
//if (sstr.length() > str.length() || (sstr.length() == str.length() && str.compareTo(sstr) > 0))
//如果之后有新的满足条件的sstr且长度比较长,可以覆盖之前的,如果两个sstr相等的话,jiuliyongstr.compareTo(sstr)>0
/*
方法二
*/
public String findLongestWord2(String s, List<String> d) {
String longeatWord = "";
for (String target:d){
int l1 = longeatWord.length(),l2 = target.length();
if(l1>l2||(l1==l2&&longeatWord.compareTo(target)<0)){
continue;
}
if(isSubstr(s,target)){
longeatWord = target;
}
}
return longeatWord;
}
private boolean isSubstr(String s, String target) {
int i = 0,j = 0;
while (i < s.length() && j <target.length()){
if(s.charAt(i)==target.charAt(j)){
j++;
}
i++;
}
return j==target.length();
}
}
//总结:isSubstr是否是子串,如果s中有target里面的元素那么j和i都后移,如果没有的话仅仅是i后移,return j==target.length()非常好,值得学习。
680、可以删除一个字符,判断是否能构成回文字符串。
/*
贪心算法
在允许最多删除一个字符的情况下,同样可以使用双指针,通过贪心算法实现。
初始化两个指针 low 和 high 分别指向字符串的第一个字符和最后一个字符。
每次判断两个指针指向的字符是否相同,如果相同,则更新指针,
令 low = low + 1 和 high = high - 1,
然后判断更新后的指针范围内的子串是否是回文字符串。如果两个指针指向的字符不同,
则两个字符中必须有一个被删除,此时我们就分成两种情况:即删除左指针对应的字符,
留下子串 s[low + 1], s[low + 1], ..., s[high],或者删除右指针对应的字符,留下子串 s[low], s[low + 1], ..., s[high - 1]。当这两个子串中至少有一个是回文串时,就说明原始字符串删除一个字符之后就以成为回文串。
*/
public class validPalindrome680 {
int del = 0;
public boolean validPalindrome(String s ){
int i = 0,j= s.length()-1;
while(i < j){
if(s.charAt(i)==s.charAt(j)){
i++;
j--;
}else{
if(del == 0){
del++;
return validPalindrome(s.substring(i,j))||validPalindrome(s.substring(i+1,j+1));
//public String substring(int beginIndex, int endIndex)
//返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,
// endIndex:到指定的 endIndex-1处结束。
//参数:beginIndex - 开始处的索引(包括)
// endindex 结尾处索引(不包括)。
}
}
return false;
}
return true;
}
}
//总结:substring() 方法用于提取字符串中介于两个指定下标之间的字符。
//return validPalindrome(s.substring(i,j))||validPalindrome(s.substring(i+1,j+1))