leetcodehot100刷题记录

hash

1.hash映射

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

wp:

 class Solution {
     public int[] twoSum(int[] nums, int target) {
         Map 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};
             }
             map.put(nums[i], i);
         }
         throw new IllegalArgumentException("No two sum solution");
     }
 }
 ​

这里主要是用了map映射,

遍历数组 nums,i 为当前下标,每个值都判断map中是否存在 target-nums[i] 的 key 值 如果存在则找到了两个值,如果不存在则将当前的 (nums[i],i) 存入 map 中,继续遍历直到找到为止

2.字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。

wp:

字母相同,但排列不同的字符串,排序后都一定是相同的。因为每种字母的个数都是相同的,那么排序后的字符串就一定是相同的。

所以我们直接进行排序

使用stream流来做

 class Solution2 {
 public List> getAnagrams(String[] strs) {
     return new ArrayList<>(Arrays.stream(strs).collect(Collectors.groupingBy(str -> Stream.of(str.split("")).sorted().collect(Collectors.joining()))).values());
 }
 }

str -> split -> stream -> sort -> join

3.128. 最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

wp:

首先本题是不能排序来做的,因为排序的时间复杂度为O(nlogn)

不符合题目的要求

对于 nums 中的元素 x,以 x 为起点,不断查找下一个数 x+1,x+2,⋯ 是否在 nums 中,并统计序列的长度。

 class Solution3{
     public int longestConsecutive(int[] nums){
         int ans = 0;
         Set st = new HashSet<>();
         for(int num:nums){
             st.add(num);
         }
         for(int x:st){
             if(st.contains(x-1)){
                 continue;
             }
             int y = x+1;
             while(st.contains(y)){
                 y++;
             }
             ans = Math.max(ans,y-x);
         }
         return ans;
     }
 }

主要使用了hashset来存储nums这个数组

主要的核心就是for循环

 for(int x:st){
             if(st.contains(x-1)){
                 continue;
             }
             int y = x+1;
             while(st.contains(y)){
                 y++;
             }
             ans = Math.max(ans,y-x);
         }

就是去试x周围的数据,x-1和x+1。最后取值y-x获得最大序列的长度

双指针

4.283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

wp:

这里我们参考快速排序的方法,以0为基准元素

我们使用两个指针 ij,只要 nums[i]!=0,我们就交换 nums[i]nums[j]

这样我们很快就能完成排序

时间复杂度:O(n)

 class Solution4 {
     public void moveZeroes(int[] nums){
         if (nums == null){
             return;
         }
         int j =0;
         for (int i=0;i 
  

5.11. 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0)(i, height[i])

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

用一句话概括双指针解法的要点:指针每一次移动,都意味着排除掉了一个柱子

如果固定左边的柱子,移动右边的柱子,那么水的高度一定不会增加,且宽度一定减少,所以水的面积一定减少。这个时候,左边的柱子和任意一个其他柱子的组合,其实都可以排除了。也就是我们可以排除掉左边的柱子了。

 class Solution5{
     public int maxArea(int[] height){
         int res = 0;
         int i = 0;
         int j = height.length-1;
         while (i 
  

6.三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

首先对数组进行排序

固定一个数i

L是i后面一个,R是len的最后一个

如果nums[i]大于0的话,sum必然大于0

如果num[i]==nums[i-1]的话,说明数字重复了,需要跳过

sum==0的时候,nums[L]==nums[L+1]重复舍去

nums[R] = nums[R-1]重负舍去

其中L是++的,R是——的

然后开始编码

当sum>0的时候说明R太大了,R--

Sum<0的时候说明L太小了,L++

wp:

 class Solution6{
     public static List> threeSum(int[] nums){
         List> ans = new ArrayList<>();
         int len = nums.length;
         if (nums==null||len<3) return ans;
         Arrays.sort(nums);
         for(int i =0;i0) break;
             if (i>0&&nums[i]==nums[i-1]) continue;
             int L = i+1;
             int R = len - 1;
             while (L0) R--;
             }
         }
         return ans;
     }
 }

滑动窗口

6.无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。

wp:

hashmap dic统计:指针j遍历字符s,哈希表统计s[j]最后一次出现的索引

根据上轮i与dis[s[j]]更新左边界i,保证[i+1,j]内无重复字符且最大

i= max(dic[s[j]],i)

更新res就是[i+1,j]的len即j-1的最大值

 class Solution7{
     public int lenOfLongesSubstring(String s){
         Map dic  = new HashMap<>();
         int i=-1,res = 0,len =s.length();
         for(int j=0;i 
  

链表

7.相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

 class Solution8 {
     public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         if (headA == null || headB == null) return null;
 ​
         ListNode A = headA, B = headB;
 ​
         while (A != B) {
             A = (A != null) ? A.next : headB;
             B = (B != null) ? B.next : headA;
         }
 ​
         return A;
     }
 }
 ​
 class ListNode {
     int val;
     ListNode next;
 ​
     ListNode(int x) {
         val = x;
         next = null;
     }
 }
 ​

先处理一下A与B不为空的情况

最重要的是

while (A != B) { A = (A != null) ? A.next : headB; B = (B != null) ? B.next : headA; }

两个具有相同结尾的链表拼接,无论哪一个在前,哪一个在后,这两种拼接方式,他们总能保持最后的一段相同的节点是不变的。

8.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

直接通过双指针反转

 class Solution {
     public ListNode reverseList(ListNode head) {
         ListNode cur = head, pre = null;
         while(cur != null) {
             ListNode tmp = cur.next; // 暂存后继节点 cur.next
             cur.next = pre;          // 修改 next 引用指向
             pre = cur;               // pre 暂存 cur
             cur = tmp;               // cur 访问下一节点
         }
         return pre;
     }
 }

9.回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为。如果是,返回 true ;否则,返回 false

这个题是在上面的基础上进行更改

 class Solution9{
     public ListNode reverList(ListNode head){
         ListNode cur = head,pre  = null;
         while (cur!=null){
             ListNode tmp = cur.next;
             cur.next = pre;
             pre = cur;
             cur = tmp;
 ​
         }
         return pre;
     }
     public boolean isPalindrome(ListNode head){
         ListNode mid =middleNode(head);
         ListNode head2  = reverList(mid);
         while (head2!=null){
             if (head.val!=head2.val){
                 return false;
             }
             head = head.next;
             head2 = head2.next;
         }
         return true;
     }
     private ListNode middleNode(ListNode head){
         ListNode slow = head;
         ListNode fast = head;
         while (fast!=null&&fast.next!=null){
             slow = slow.next;
             fast = fast.next.next;
        }return slow;
     }
 }

先找到中间的节点,也就是middleNode函数

slow比慢等到fast.next到终点的时候.slow就是中间的节点

然后反正mid那一部分,如果mid与反转后的相同的话,那么就是回文串

返回ture反之则false

9.环形链表1

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

如果一个链表存在环,那么快慢指针必然会相遇。实现代码如下

所以我们直接编写代码

 class Solution10{
         public boolean hasCycle(ListNode head){
             ListNode slow = head,fast = head;
             while (fast!=null&&fast.next!=null){
                 slow = slow.next;
                 fast = fast.next.next;
                 if (fast==slow){
                     return true;
                 }
             }
             return false;
         }
     }

10.环形链表2

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

根据分析,有环的标识是fast==slow

然后fast和slow第二次相遇的node就是环的节点

所以很简单的就可以分析出

 class Solution11{
         public ListNode detectCycle(ListNode head){
             ListNode fast = head,slwo = head;
             while (fast!=null&&fast.next!=null){
                 fast = fast.next.next;
                 slwo = slwo.next;
                 if (fast == slwo) {
                     fast = head;
                     while (slwo!=fast){
                         slwo = slwo.next;
                         fast = fast.next;
                     }
                     return fast;
                 }
             }
             return null;
         }
     }

直接使用两次相遇,相遇的fast就是环的起点;

11.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

我们使用递归算法

当两个链表都为空的时候,说明已经合并完毕了

l1和l2哪个同头节点更小,较小节点的Next指针就指向其余节点的合并结果

 class Solution12{
         public ListNode mergeTowLists(ListNode l1,ListNode l2){
             if (l1==null){
                 return l2;
             }
             else if (l2==null){
                 return l1;
             }
             else if (l1.val 
  

12.两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。‘

计算每一位的时候要考虑上一位的进位问题,计算结束后同样要跟更新进位

如果两个链表全部遍历完毕后,进位值为 1,则在新链表最前方添加节点 1

 class Solution13 {
         public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
             ListNode pre = new ListNode(0);
             ListNode cur = pre;
             int carry = 0;
             while (l1 != null || l2 != null) {
                 int x = l1 == null ? 0 : l1.val;
                 int y = l2 == null ? 0 : l2.val;
                 int sum = x + y + carry;
 ​
                 carry = sum / 10;
                 sum = sum % 10;
                 cur.next = new ListNode(sum);
 ​
                 cur = cur.next;
                 if (l1 != null) {
                     l1 = l1.next;
                 }
                 if (l2 != null) {
                     l2 = l2.next;
                 }
             }
             if (carry == 1) {
                 cur.next = new ListNode(carry);
             }
             return pre.next;
         }
     }

13.删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

设预先指针 pre 的下一个节点指向 head,设前指针为 start,后指针为 end,二者都等于 pre start 先向前移动n步 之后 start 和 end 共同向前移动,此时二者的距离为 n,当 start 到尾部时,end 的位置恰好为倒数第 n 个节点

因为要删除该节点,所以要移动到该节点的前一个才能删除,所以循环结束条件为 start.next != null

删除后返回 pre.next,为什么不直接返回 head 呢,因为 head 有可能是被删掉的点

 class Solution14 {
             public ListNode removeNthFromEnd(ListNode head, int n) {
                 ListNode pre = new ListNode(0);
                 pre.next = head;
                 ListNode start = pre, end = pre;
                 while (n != 0) {
                     start = start.next;
                     n--;
                 }//start先提前移动
                 while (start.next != null) {
                     start = start.next;
                     end = end.next;
                 }//一块移动
                 end.next = end.next.next;//删除某节点
                 return pre.next;
             }
         }

14.两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

调用单元:设需要交换的两个点为 head 和 next,head 连接后面交换完成的子链表,next 连接 head,完成交换 终止条件:head 为空指针或者 next 为空指针,也就是当前无节点或者只有一个节点,无法进行交换

假设链表为

 tmp -> (A) -> (B) -> (C) -> (D) -> null
 start -> (B)
 end -> (C)
 ​

使tmp.next = end;

链表现在变成了这样

 (A) -> (C) -> (D) -> null
 (B) (断开)
 ​

再start.next = end.next;

(B)就指向了(D)

end.next = start;

然后(C)就指向了(B)

所以链表就变成了

 (A) -> (C) -> (B) -> (D) -> null

最后tmp = start;

再从B开始继续迭代循环

最终完成了链表节点的交换

完整代码:

 class Solution15 {
     public ListNode swapPairs1(ListNode head) {
         if (head == null || head.next == null) {
             return head;
         }//没有节点,或者只剩一个的时候
         ListNode next = head.next;
         head.next = swapPairs1(next.next);//
         next.next = head;//后节点等于头节点
         return next;
     }
     public ListNode swapPairs2(ListNode head){
         ListNode pre = new ListNode(0);
         pre.next = head;
         ListNode tmp = pre;
         while (tmp.next!=null&&tmp.next.next!=null){
             ListNode start = tmp.next;
             ListNode end = tmp.next.next;
             tmp.next = end;//head
             start.next = end.next;
             end.next = start;
             tmp = start;
         }
         return pre.next;
     }
 ​
 }

15. 随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。

  • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

你的代码 接受原链表的头节点 head 作为传入参数。

 class Solution16{
     public Node copyRandomList(Node head){
         if (head==null) return null;
         Node cur =head;
         Map map = new HashMap<>();
         while (cur!=null){
             map.put(cur,new Node(cur.val));
             cur = cur.next;
         }
         cur = head;
         while (cur!=null){
             map.get(cur).next = map.get(cur.next);
             map.get(cur).random = map.get(cur.random);
             cur = cur.next;
         }
         return map.get(head);
     }
 }

while (cur!=null){ map.put(cur,new Node(cur.val)); cur = cur.next; }

先把链表复制一份

构建新节点的 nextrandom 引用指向。都是随机的

然后迭代下一个节点

最后返回head节点

16.排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

我们使用新建链表的方式

把val值排序后再组成一个新链表

 class Solution17{
     public ListNode sortList(ListNode head){
         if (head==null){
             return null;
         }
         ListNode cur = head;
         int n = 0;
         while (cur!=null){
             n++;
             cur = cur.next;
         }
         int[] arr = new int [n];
         cur = head;
         for(int i=0;i 
  

17.LRU 缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。

实现 LRUCache 类:

  • LRUCache(int capacity)正整数 作为容量 capacity 初始化 LRU 缓存

  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1

  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 getput 必须以 O(1) 的平均时间复杂度运行。

基于LinkedHashMap来实现

LinkedHashMap有序哈希表,可以按插入顺序访问顺序存储键值对。

super(capacity, 0.75F, true);

capacity: 初始容量

0.75F: 负载因子(默认 0.75

true: 启用 访问顺序,即 最近访问的元素会被移到链表尾部,最久未使用的元素会在链表头部。

size() > capacity 时,返回 trueLinkedHashMap 会自动删除 链表头部的最老元素(即最近最少使用的元素)。

 class LRUCache extends LinkedHashMap {
     private int capacity;
     public LRUCache(int capacity) {
         super(capacity,0.75F,true);
         this.capacity = capacity;
     }
     public int get(int key) {
         return super.getOrDefault(key,-1);
     }
     public void put(int key, int value) {
         super.put(key,value);
     }
     @Override
     protected boolean removeEldestEntry(Map.Entry eldest){
         return size() > capacity;
     }
 }

数组

18.最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

 class Solution18{
     public int maxSubArray(int[] nums){
         int ans = nums[0];
         int sum = 0;
         for (int num:nums){
             if (sum>0){
                 sum+=num;
             }
             else {
                 sum = num;
             }
             ans=Math.max(ans,sum);
         }
         return ans;
     }
 }

直接使用循环来完成这个目的

你可能感兴趣的:(leetcode,算法,数据结构,哈希算法,java)