leetcode学习及刷题记录(持续跟新)

刷题目录参考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

栈和队列

哈希表

字符串

数组与矩阵

位运算

二、我的刷题记录

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值