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刷题之旅相关推荐

  1. Leetcode刷题之旅1

    Leetcode刷题之旅1 先从剑指offer66题开始刷 链表可创建dummy哑节点指向头指针,目的是为了对头节点进行操作 例子:删除链表中重复节点 确定有限状态自动机 例子:剑指offer20 表 ...

  2. LeetCode 刷题之旅(2020.05.22)——105. 从前序与中序遍历序列构造二叉树(中)

    LeetCode 刷题之旅(2020.05.22)--105. 从前序与中序遍历序列构造二叉树(中) 题目: 根据一棵树的前序遍历与中序遍历构造二叉树. 注意: 你可以假设树中没有重复的元素. 例如, ...

  3. LeetCode刷题之旅正式开始

    开学三个多月了,但没写过多少代码,感觉再这样下去就废了.所以今天开始LeetCode刷题.这篇文章算是一个刷题的开端,主要写写为什么选择leetcode刷题,怎样刷题等问题. 1.为什么刷题? 我觉得 ...

  4. leetcode刷题之旅-58. 最后一个单词的长度

    给定一个仅包含大小写字母和空格 ' ' 的字符串 s,返回其最后一个单词的长度.如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词. 如果不存在最后一个单词,请返回 0 . 说明:一个单词 ...

  5. leetcode刷题之旅(5) Longest Palindromic Substring

    题目描述 Given a string s, find the longest palindromic substring in s. You may assume that the maximum ...

  6. leetcode刷题之堆

    今天终于开启的第二个专题的刷题之旅堆,不过第一个专题栈还有一个小问题没解决就是利用递减栈去解决接雨水的问题,虽然那道题我用动态规划的问题解决出来了,我记得看到过一道面试题,问栈和堆有什么区别.通过搜索 ...

  7. 【LeetCode刷题日记】常用算法基础和理解及运用

    在我们LeetCode刷题过程中,如果我们只是了解数据结构(数组,链表,数)的使用方法,那我们在面对复杂的题目时,是很难很好的解决问题的,因此我们要了解一些常用算法来帮助我们更好的解题. 递归和迭代 ...

  8. LeetCode刷题系列

    LeetCode 我们工作面试和提高自身数据结构和算法能力的时候往往需要刷刷题,我选择LeetCode是通过一个留学论坛了解的.专业,覆盖语种全面. 提前说说刷题的心得: 尽量手写代码,少使用IDE的 ...

  9. 我的LeetCode刷题初体验

    背景 2019年08月03很幸运的参加了第一期 <极客大学前端训练营>,成为了winner大神的座下弟子,老师的第一堂课,就让我们自己写出一个黑白棋项目,第一堂课就被虐的很惨,因为黑白棋的 ...

最新文章

  1. 最新 MSDN Library for Visual Studio 2008 SP1
  2. wxWidgets:wxGridUpdateLocker类用法
  3. [Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能
  4. SAP ECC EHP7 RFC 发布成WebService
  5. php与JAVA的RSA加密互通
  6. 使用zerorpc踩的第一个坑:
  7. 解释python脚本程序的name变量及其作用_一些概念总结
  8. 传感器 倾斜角 android,android – 如何使用sensor / s获得手机的角度/度数?
  9. URL带中文参数的解决方法FR.cjkEncode()
  10. 会赚钱的教师才是好教师
  11. Docker下载与安装(2020)
  12. 计算机主板不认硬盘怎么回事,主板sata接口不认硬盘怎么办
  13. WiFi关联拒绝log分析以及代码流程 ASSOC_REJECT
  14. 【C语言作业】一个数如果恰好等于它的因子之和,这个数就称为完整数。例如6的因子为1、2、3,而6=1+2+3,因此6是完数,编程找出1000之内的所有完整数
  15. osm数据下载 python_Python环境下使用OpenStreetMap下载的.osm数据
  16. 跨境电商一件代发和专线小包是什么意思?有什么区别?
  17. Lucas 与 ExLucas
  18. 金蝶BOS开发代码调用过程
  19. 我国超级计算机型号,中国成功研制千万亿次超级计算机天河一号
  20. 2021 神经网络压缩 (李宏毅

热门文章

  1. IPSEC ×××实验六:ASA SSL ×××
  2. Struts1.x系列教程(6):Bean标签库
  3. 商用机型和家用机型区别
  4. apache基于ip如何配置虚拟主机
  5. apache http server指的是什么
  6. PostgreSQL的QT驱动编译
  7. 问题 B: 分组统计
  8. 动态规划最常见的习题 (最长公共子串、最长公共子序列、最短编辑距离)
  9. 百度前端学院---斌斌学院---任务demo---1
  10. centos下添加的端口不能访问(防火墙关闭)