刷题目录参考github上的一个刷题目录,链接:https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md
同时也会记录自己刷的一些leetcode题目,自用方便复习
文章目录
一、github上的刷题目录
1.算法思想
双指针
167. 两数之和 II - 输入有序数组(Easy)
给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
思路:
使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
如果两个指针指向元素的和 sum == target,那么得到要求的结果;
如果 sum > target,移动较大的元素,使 sum 变小一些;
如果 sum < target,移动较小的元素,使 sum 变大一些。
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i=0,j=numbers.length-1;
while(i<j){
int sum=numbers[i]+numbers[j];
if(sum==target) {//刚好符合条件
return new int[]{i + 1, j + 1};
}else if(sum<target){
i++;//左标识向左移动
}else{
j--;//右标识向右移动
}
}
return null;
}
}
633.平方数之和(Easy)
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c。
示例1:
输入: 5
输出: True
解释: 1 * 1 + 2 * 2 = 5
示例2:
输入: 3
输出: False
思路:
设置i和j两个索引,i指向0,j指向c的平方,同上一题一样缩小范围。
class Solution {
public boolean judgeSquareSum(int c) {
//设置i和j两个索引,i指向0,j指向c的平方
int i=0,j=(int) Math.sqrt(c);
while (i<=j){//i可能等于j
int sum=i*i+j*j;
if(sum==c){
return true;
}else if(sum<c){
i++;//i向左移动
}else{
j--;//j向右移动
}
}
return false;
}
}
345. 反转字符串中的元音字母(Easy)
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
示例 1:
输入: “hello”
输出: “holle”
示例 2:
输入: “leetcode”
输出: “leotcede”
说明:
元音字母不包含字母"y"。
思路:
使用双指针指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。
class Solution {
public String reverseVowels(String s) {
//设置i和j两个索引,i指向0,j指向s的末尾
int i=0,j=s.length()-1;
char[] result=new char[s.length()];
while (i<=j){//i和j可以相等
char ci=s.charAt(i);
char cj=s.charAt(j);
if( ci != 'a' && ci != 'e' && ci != 'i' && ci != 'o' && ci != 'u' && ci != 'A' && ci != 'E' && ci != 'I' && ci != 'O' && ci != 'U'){
result[i++] = ci;
}else if( cj != 'a' && cj != 'e' && cj != 'i' && cj != 'o' && cj != 'u' && cj != 'A' && cj != 'E' && cj != 'I' && cj != 'O' && cj != 'U'){
result[j--] = cj;
}else{//两个索引都指向元音字母
result[i++]=cj;
result[j--]=ci;
}
}
return new String(result);
}
}
680. 验证回文字符串 Ⅱ(Easy)
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
示例 1:
输入: “aba”
输出: True
示例 2:
输入: “abca”
输出: True
解释: 你可以删除c字符。
注意:
字符串只包含从 a-z 的小写字母。字符串的最大长度是50000。
思路:
逐个判断前后的字符是否相等。当索 i 和索引 j 对应的字符不相等时,则分别判断区间【i,j)和(i,j】的序列是否为回文串,如果是则可以就说明去掉j(或i)后的字符串就是回文串,否则就不是。
class Solution {
public boolean validPalindrome(String s) {
for (int i = 0,j = s.length()-1; i < j; i++,j--) {//前后判断是否为回文串
if(s.charAt(i)!=s.charAt(j))//如果有不同,判断[i,j)和(i,j]是否为回文串
return judgement(s,i,j-1) || judgement(s,i+1,j);
}
return true;//如果i和j的指向相等
}
//判断区间[i,j]字符串是否是回文串
public boolean judgement(String s,int i,int j){
while (i<j){
if (s.charAt(i++) != s.charAt(j--)) {
return false;
}
}
return true;
}
}
88. 合并两个有序数组(Easy)
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
思路:
设置两个索引index1和index2分别指向nums1和nums2的最后一个元素,设置indexsum指向合并后的元素,比较index1和index2所指向元素的大小,元素大的赋值。如果一个数组赋值完毕,另一个数组则直接全部赋值
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int index1=m-1,index2=n-1;
int indexsum=m+n-1;//indexsum表示合并完的数组的最后一个下标值
while (index1>=0||index2>=0){
if(index1<0){
nums1[indexsum--] = nums2[index2--];
}else if(index2<0){
nums1[indexsum--]=nums1[index1--];
}else if(nums1[index1]<nums2[index2]){
nums1[indexsum--]=nums2[index2--];
}else{
nums1[indexsum--]=nums1[index1--];
}
}
}
}
141. 环形链表(Easy)
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
思路:
通过使用具有不同速度的快、慢两个指针遍历链表,空间复杂度可以被降低至 O(1)。慢指针每次移动一步,而快指针每次移动两步。如果列表中不存在环,最终快指针将会最先到达尾部,此时我们可以返回 false。如果存在环,那么这两个指针一定会相遇。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null||head.next==null)
return false;
ListNode slow=head;
ListNode fast=head.next;
while (slow!=fast){//不断比较slow和fast是否相等
if(fast==null||fast.next==null)//因为fast比slow快,所以只需判断fast
return false;
slow=slow.next;
fast=fast.next.next;
}
return true;
}
}
还有一种方法是用哈希表,见leetcode解题。
524. 通过删除字母匹配到字典里最长单词(Medium)
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:
s = “abpcplea”, d = [“ale”,“apple”,“monkey”,“plea”]
输出:
“apple”
示例 2:
输入:
s = “abpcplea”, d = [“a”,“b”,“c”]
输出:
“a”
说明:
1.所有输入的字符串只包含小写字母。
2.字典的大小不会超过 1000。
3.所有输入的字符串长度不会超过 1000。
思路:
比较两个字符串的方法如下图所示:
class Solution {
public String findLongestWord(String s, List<String> d) {
String longestWord="";//设置最大串
for(String x:d){
int l1 = longestWord.length();
int l2 = x.length();
//如果最大字符串longestWord比待比较字符串x长,或最大字符串longestWord比待比较字符串x字典序靠前,则跳过
//compareTo()按字典顺序比较两个字符串。如果按字典顺序此 String 对象在参数字符串之前,则比较结果为一个负整数。
if( l1 > l2 ||(l1 == l2 && longestWord.compareTo(x) < 0))
continue;
//否则判断待比较字符串x与字符串s是否符合条件
if(isStrEqual(s,x))
longestWord = x;
}
return longestWord;
}
//判断s和x是否符合条件,j指向s,i指向x
public boolean isStrEqual(String s,String x){
int j=0,i=0;
while (j < s.length() && i < x.length()){
if(s.charAt(j)==x.charAt(i))
i++;
j++;
}
return i==x.length();//如果i==x.length()说明字符串x判断完成
}
}
排序
贪心思想
二分查找
分治
搜索
动态规划
数学
2.数据结构相关
链表
160. 相交链表(Easy)
编写一个程序,找到两个单链表相交的起始节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
注意:
如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。
思路:双指针法
创建两个指针 pA 和 pB,分别初始化为链表 A 和 B 的头结点。然后让它们向后逐结点遍历。
当 pA 到达链表的尾部时,将它重定位到链表 B 的头结点 (你没看错,就是链表 B); 类似的,当 pB 到达链表的尾部时,将它重定位到链表 A 的头结点。
若在某一时刻 pA 和 pB 相遇,则 pA/pB 为相交结点。
想弄清楚为什么这样可行, 可以考虑以下两个链表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于结点 9。 由于 B.length (=4) < A.length (=6),pB 比 pA 少经过 22 个结点,会先到达尾部。将 pB 重定向到 A 的头结点,pA 重定向到 B 的头结点后,pB 要比 pA 多走 2 个结点。因此,它们会同时到达交点。
如果两个链表存在相交,它们末尾的结点必然相同。因此当 pA/pB 到达链表结尾时,记录下链表 A/B 对应的元素。若最后元素不相同,则两个链表不相交。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
/**
定义两个指针, 第一轮让两个到达末尾的节点指向另一个链表的头部, 最后如果相遇则为交点(在第一轮移动中恰好抹除了长度差)
两个指针等于移动了相同的距离, 有交点就返回, 无交点就是各走了两条指针的长度
**/
if(headA == null || headB == null) return null;
ListNode l1=headA,l2=headB;
// 在这里第一轮体现在pA和pB第一次到达尾部会移向另一链表的表头, 而第二轮体现在如果pA或pB相交就返回交点, 不相交最后就是null==null
while (l1!=l2){
l1=(l1==null)?headB:l1.next;
l2=(l2==null)?headA:l2.next;
}
return l1;
}
}
如果只是判断是否存在交点,那么就是另一个问题,即 编程之美 3.6 的问题。有两种解法:
1.把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;
2.或者直接比较两个链表的最后一个节点是否相同。
206.反转链表(easy)
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
思路:
方法一:头插法:
建一个新链表newList,把链表head上的节点,一个个拿下来,头插到新链表newList里,head=next继续下一轮循环。主要执行如下图的圈1和圈2的操作:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode newlist=new ListNode(-1);
while (head!=null){
ListNode next =head.next;
head.next=newlist.next;
newlist.next=head;
head=next;
}
return newlist.next;
}
}
方法二:递归
class Solution {
public ListNode reverseList(ListNode head) {
//递归终止条件是当前为空,或者下一个节点为空
if(head==null || head.next==null) {
return head;
}
//这里的cur就是最后一个节点
ListNode cur = reverseList(head.next);
//这里请配合动画演示理解
//如果链表是 1->2->3->4->5,那么此时的cur就是5
//而head是4,head的下一个是5,下下一个是空
//所以head.next.next 就是5->4
head.next.next = head;
//防止链表循环,需要将head.next设置为空
head.next = null;
//每层递归函数都返回cur,也就是最后一个节点
return cur;
}
}
21.合并两个有序链表(easy)
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4