欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

LeetCode刷题之旅

发布时间:2025/4/16 75 豆豆
生活随笔 收集整理的这篇文章主要介绍了 LeetCode刷题之旅 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

LeetCode刷题之旅

一、链表

1.链表逆序(leetcode 206.Reverse Linked List)esay

题目描述:已知链表头节点指针head,将链表逆序。

思路:从链表的头节点依次遍历,每遍历一个节点就进行逆序, 使用头插法进行逆序。

代码实现

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode reverseList(ListNode head) {if (head == null || head.next == null) return head;ListNode newHead = null;while(head != null) {ListNode nextTemp = head.next;head.next = newHead;newHead = head;head = nextTemp; }return newHead;} }

2.链表部分逆序(leedcode92.Reverse Linked List2)medium

题目描述:已知链表头节点指针head,将链表从位置m到n逆序。

思路:需要完成m到n位置的链表逆序,其中逆序算法已经清楚,只需要确定开始逆序的位置(从起始节点后移m个位置),以及需要逆序的节点个数(m-n+1)。同时,需要记录开始逆序节点的前驱(pre_start),以便逆序完可以直接链接上。逆置段尾结点的后继也需要记录。需要注意一点,讨论m是否为1,如果从第一个节点开始逆置,则逆置完的链表段头节点即为起始节点。

代码实现

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode reverseBetween(ListNode head, int m, int n) {if (head == null || head.next == null) return head;ListNode pre_start, new_start, new_end;pre_start = new_start = new_end = null;ListNode list = head; //记录listint length = n-m+1; //长度int steps = m-1;while(head!=null && steps!=0) {pre_start = head;head = head.next;steps--; //head指针移动到m对应位置}new_end = head;ListNode next_head = null;while (head!=null && length!=0) {next_head = head.next;head.next = new_start;new_start = head;head = next_head;length--; //逆序length个节点}new_end.next = head;if (m == 1) {list = new_start;} else {pre_start.next = new_start; //将pre_start链接上逆置后的链表段}return list;

3.求两个链表的交点(leetcode160.Intersection of Two Linked Lists)easy

题目描述:已知链表A的头节点指针headA,链表B的头结点指针headB,两个链表相交,求两个链表相交的交点

思路:1.使用set集合。遍历链表A,将A中节点对应的指针(地址)存入set中。遍历链表B,将B中节点对应的指针(地址),在set中查找,发现在set中的第一个节点地址,即是两个链表的起始交点。

​ 2.若链表A与链表B相交,则只是相交节点的前面链表段不相同。分别计算链表A与B的长度,将较长的链表起点移动到与短链表起点对齐的地方,这样一同出发,相交点即是指针相同的交点。

代码实现

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA == null || headB == null) return null;ListNode listA = headA;ListNode listB = headB;int lengthA = 0;int lengthB = 0;while(headA != null) {lengthA++;headA = headA.next;} // 计算链表A的长度while(headB != null) {lengthB++;headB = headB.next;} // 计算链表B的长度int dis = 0;if (lengthB > lengthA) {dis = lengthB - lengthA;while(dis > 0){listB = listB.next;dis--;}} else {dis = lengthA - lengthB;while(dis > 0){listA = listA.next;dis--;}} //将较长链表的起点移动到与短链表起点对齐的地方while (listA!=listB && listA!=null && listB!=null) {listA = listA.next;listB = listB.next;}return listA; } }

4.链表求环(leetcode 141.Linked List Cycle)easy

问题描述:已知链表可能有环,若有环则返回true,否则返回false

思路:因为只需要判断是否有环,不用确定起始节点,所以方法较多,列举常见的两种。

1.利用set集合,依次将节点指针插入set中,当在set中查找,第一个在set中发现的节点地址,即是链表环的起点

2.从起点开始遍历,依次判断该节点的next是否为本身,如果是,则返回true;如果不是,则将该节点的next指针指向自己,并递归遍历其后面的链表。

代码实现:方法1:

/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public class Solution {public boolean hasCycle(ListNode head) {if (head == null) return false;Set <ListNode> nodeSet = new HashSet<ListNode>(); while(head != null) {if (nodeSet.contains(head)) {return true;} else {nodeSet.add(head);head = head.next;}}return false;} }

方法2:

/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public class Solution {public boolean hasCycle(ListNode head) {if (null == head) return false;if (head == head.next) return true;ListNode n = head.next; head.next = head; // 将指针指向自己return hasCycle(n);} }

5.链表求环2(leetcode 142.Linked List Cycle2)medium

问题描述:已知链表可能有环,若有环则返回环起始节点,否则返回NULL

思路:1.简单的方法是,利用set集合实现。依次将节点指针插入set中,当在set中查找,第一个在set中发现的节点地址,即是链表环的起点。

​ 2.相对巧妙的想法,利用快慢指针实现。见下面,fast与slow指针同时从起点X出发,其中fast是slow速度的两倍,当fast指针追到slow指针时,此时相遇于Z点。对于slow指针,走了a+b的路程。那么fast指针的路程为2(a+b),同时也为 a+b+c+b,即a+2b+c=2(a+b),得a=c。这时,如果从它们的交点Z出发的话,同时有一指针head从起点X出发,必会相交于Y点,也即为环的起始点。

代码实现:方法1

/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public class Solution {public ListNode detectCycle(ListNode head) {// if (null == head) return null;Set<ListNode> nodeSet = new HashSet<ListNode>();while(head != null) {if (nodeSet.contains(head)) {return head;} else {nodeSet.add(head);}head = head.next;}return null;} }

方法2:

/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/ public class Solution {public ListNode detectCycle(ListNode head) { ListNode fast = head;ListNode slow = fast;while (fast!=null && fast.next!=null && fast.next.next!=null) { fast = fast.next.next;slow = slow.next;if (fast == slow) { // fast与slow指针相交时while (head != slow) {head = head.next; // head指针从起始点出发slow = slow.next;}return head;} }return null;} }

6.链表划分(leetcode86.Partition List)medium

问题描述:已知链表头指针head与数值x,将所有小于x的节点放在大于等于x的节点前,且保存这些节点的原来的相对位置。如,划分前:1->4->3->2->5->2,x=3,划分后:1->2->2->4->3->5

思路:遍历链表,将小于x的节点插入到新链表less_head中,将大于等于x的节点插入到新链表more_head中,然后将more_head.next插入在less_head的末尾,返回less_head即可。

或者直接将原链表中大于x的节点移出并链接为新链表mhead,然后将mhead链接至原链表的结尾即可。

代码实现:方法1

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode partition(ListNode head, int x) {ListNode lhead = new ListNode(0); // 使用两个链表less_head和more_head来分别记录ListNode less_head = lhead; ListNode mhead = new ListNode(0);ListNode more_head = mhead;while(head!=null) {if (head.val < x) {lhead.next = head;lhead = lhead.next;} else {mhead.next = head;mhead = mhead.next;}head = head.next;}mhead.next = null;lhead.next = more_head.next;return less_head.next;} }

方法2:

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode partition(ListNode head, int x) { ListNode list = new ListNode(0); // 将原链表中大于x的值连接为一个链表mheadlist.next = head;ListNode new_head = list;ListNode more_head = new ListNode(0);ListNode mhead = more_head;while(list.next!=null) {if (list.next.val < x) {list = list.next;} else {more_head.next = list.next;more_head = more_head.next;list.next = list.next.next; }}list.next = mhead.next;more_head.next = null;return new_head.next;} }

7.链表的深度拷贝(leetcode138.Copy List With Random Pointer)medium

问题描述:已知一个复杂的链表,链表节点除了有一个指向下一个节点的next指针,还有一个random指针,指向本链表的任意某个节点(也可能指向自己),求这个链表的深度拷贝(构造生成一个完全新的链表,即使原链表毁坏,新链表也可独立使用)

思路:本问题中,主要是一个random指针也需要拷贝下来。利用map来实现,其中map的key记录原链表的各个节点,value来保存新建的节点(val值为key中节点的值),对于新建的节点的next和random指向的节点位置,由对应的key获得。即,map.get(head).next = map.get(head.next)、map.get(head).random = map.get(head.random)。

代码实现

/* // Definition for a Node. class Node {public int val;public Node next;public Node random;public Node() {}public Node(int _val,Node _next,Node _random) {val = _val;next = _next;random = _random;} }; */ class Solution {public Node copyRandomList(Node head) {if (head == null) return null;Map<Node, Node> map = new HashMap<>();Node iter = head;while(iter != null) {map.put(iter, new Node(iter.val));iter = iter.next;} // 将链表的各个节点指针存为map的key,value为该节点的拷贝。iter = head;while(iter != null) {map.get(iter).next = map.get(iter.next);map.get(iter).random = map.get(iter.random);iter = iter.next;}return map.get(head);} }

8.排序链表的合并(2个)(leetcode21.Merge Two Sorted Lists)easy

问题描述:已知两个已排序链表头结点l1和l2,将这两个链表合并,合并之后仍是有序的,返回合并后链表头节点。

思路:本题相对简单,依次比较l1和l2的头结点,谁小就将谁插入新链表l中。最后返回l链表即可。

代码实现:方法1

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode l = new ListNode(0);ListNode head = l;while(l1!=null && l2!=null) {if (l1.val < l2.val) {head.next = l1;head = head.next; l1 = l1.next;} else {head.next = l2;head = head.next;l2 = l2.next;}}if (l1 != null) head.next = l1; // 如果l1还有剩余节点,直接链接到l后面else head.next = l2; // l2还有剩余return l.next;} }

方法2(递归实现):

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) { if(l1 == null) return l2;if(l2 == null) return l1;if(l1.val < l2.val){l1.next = mergeTwoLists(l1.next, l2);return l1;} else{l2.next = mergeTwoLists(l1, l2.next);return l2;}} }

9.排序链表的合并(多个)(leetcode23.Merge K Sorted Lists)hard

问题描述:已知k个已排序链表头结点l1和l2,将这k个链表合并,合并之后仍是有序的,返回合并后链表头节点。

思路:本题相当于上一题难度变大,主要是如何分配这k个链表,让其分别两两合并,最后合并为一个整体。如果采用暴力破解,从第一个开始,合并第二个,然后再合并第三个。。。,共k个链表,平均每个链表有n个节点,则时间复杂度为:(n+n)+(2n+n)+(3n+n)+…+((k-1)n+n) = (1+2+3+…+k-1)n+(k-1)n = (k^2+k-1)/2*n = O(k^2 *n).

另一个思路,采用分治法,即两两进行合并。第一轮,进行k/2次,每次处理2n个;第二轮,进行k/4次,每次处理4n个,…,最后一次,进行k/(2logk)次,每次处理2logk*n个值。时间复杂度为:k/2 *2n+k/4 *4n+…+k/(2^logk) *(2^logk *n) = nk+nk+…+nk = O(klogk * n)。
代码实现

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode mergeKLists(ListNode[] lists) {return mergeKLists(lists, 0, lists.length-1);}public ListNode mergeKLists(ListNode[] lists, int first, int end) {if (first == end) return lists[first];if (first > end) return null;if (end-first == 1) return mergeTwoLists(lists[first], lists[end]);int mid = (first + end)/2;ListNode l1 = mergeKLists(lists, first, mid); // 递归合并前半部分ListNode l2 = mergeKLists(lists, mid+1, end); // 递归合并后半部分return mergeTwoLists(l1, l2);}public ListNode mergeTwoLists(ListNode l1, ListNode l2) { // 合并两个链表if (l1 == null) return l2;if (l2 == null) return l1;if (l1.val < l2.val) {l1.next = mergeTwoLists(l1.next, l2);return l1;} else {l2.next = mergeTwoLists(l1, l2.next);return l2;}} }

利用数组实现:

/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val = x; }* }*/ class Solution {public ListNode mergeKLists(ListNode[] lists) {if (lists.length == 0) return null;if (lists.length == 1) return lists[0];if (lists.length == 2) return mergeTwoLists(lists[0], lists[1]);int mid = lists.length/2;ListNode[] lists1 = new ListNode[mid];for (int i=0; i<mid; i++) { // 将lists前半部分存入数组list1[]中lists1[i] = lists[i];}ListNode[] lists2 = new ListNode[lists.length-mid];for (int i=mid; i<lists.length; i++) { // 将lists后半部分存入数组list2[]中lists2[i-mid] = lists[i];} ListNode sub_list1 = mergeKLists(lists1); // 将list1[]中链表合并ListNode sub_list2 = mergeKLists(lists2); // 将list2[]中链表合并return mergeTwoLists(sub_list1, sub_list2);}public ListNode mergeTwoLists(ListNode l1, ListNode l2) {if (l1 == null) return l2;if (l2 == null) return l1;if (l1.val < l2.val) {l1.next = mergeTwoLists(l1.next, l2);return l1;} else {l2.next = mergeTwoLists(l1, l2.next);return l2;}} }

总结

以上是生活随笔为你收集整理的LeetCode刷题之旅的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。