【第一题】删除链表的节点
分析:双指针。
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:37.9 MB, 在所有 Java 提交中击败了70.57% 的用户
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head == null){
return head;
}
ListNode cur = head;
ListNode pre = null;
if(cur.val == val) return head.next;
while(cur.val != val){
pre = cur;
cur = cur.next;
}
pre.next = pre.next.next;
return head;
}
}
【第二题】链表的中间结点
分析:快慢指针。从第一个结点开始出发,快指针每次走两步,慢指针每次走一步,当快指针走到终点,慢指针走到中点。
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:35.5 MB, 在所有 Java 提交中击败了81.76% 的用户
class Solution {
public ListNode middleNode(ListNode head) {
if(head == null) return null;
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
【第三题】移除链表元素
分析:缝缝补补终于过了!淦我好菜!
//执行用时:1 ms, 在所有 Java 提交中击败了99.49% 的用户
//内存消耗:39.2 MB, 在所有 Java 提交中击败了85.31% 的用户
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head == null){return null;}
if(head.val == val && head.next== null){return null;}
if(head.val != val && head.next == null){return head;}
//假如当前指向的结点值为val,那么直接换头节点
while(head.next!=null && head.val == val){
head = head.next;
}
if(head.val == val && head.next== null){return null;}
if(head.val != val && head.next == null){return head;}
ListNode cur = head;
while(cur.next !=null && cur.next.next !=null){
if(cur.next.val == val){
if(cur.next.next == null){
cur.next = null;
}else{
cur.next = cur.next.next;
}
}else{
cur = cur.next;
}
}
if(cur.next!=null && cur.next.val == val){
cur.next = null;;
}
return head;
}
}
//双指针
//执行用时:1 ms, 在所有 Java 提交中击败了99.49% 的用户
//内存消耗:39.7 MB, 在所有 Java 提交中击败了26.63% 的用户
class Solution{
public ListNode removeElements(ListNode head,int val){
ListNode header = new ListNode(-1);
header.next = head;
ListNode cur = header;
//这里在头节点前设置结点,避免了判断头节点为删除结点
while(cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
}else{
cur = cur.next;
}
}
return header.next;
}
}
【第四题】两个链表的第一个公共结点
分析:
方法一:先遍历两个列表,得出列表的长度,然后求出两者长度之差n,让指向长列表的指针先走n步,然后两个指针一起走,当他们指向相桶的结点时,返回该节点即可。
方法二:交集链表;
方法三:集合。将结点加入集合,在加入的过程中碰到第一个已存在的结点时,返回该节点。
//执行用时:1 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:41.2 MB, 在所有 Java 提交中击败了75.31% 的用户
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//两者都为空
if(headA == null || headB == null){return null;}
//两者都只有一个结点
if(headA.next == null && headB.next == null && headA == headB){
return headA;
}
//其中一个链表只有一个结点
if(headA.next == null){
if(headB.next== headA){return headA;}
}
if(headB.next == null){
if(headA.next == headB){return headB;}
}
//两个链表都不止一个结点
//遍历一遍得到长度
ListNode i = headA;
ListNode j = headB;
int lenA = 0,lenB = 0;
while(i.next!=null){
i = i.next;
lenA++;
}
while(j.next!=null){
j = j.next;
lenB++;
}
//System.out.println(lenA+" "+lenB);
int n = Math.abs(lenB-lenA);
i = headA;
j = headB;
//int n = Math.abs(skipA - skipB);
while(n!=0){
if(lenA > lenB){
i = i.next;
}else{
j = j.next;
}
n--;
}
// System.out.println(j.val);
while(i.next!=null && j.next!=null){
if(i == j){
return i;
}
i = i.next;
j = j.next;
}
return i == j?i:null;
}
}
//执行用时:1 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:40.8 MB, 在所有 Java 提交中击败了98.80% 的用户
//当其中一个走到尽头,回到起点,继续与未走到尽头的一起向前。
public ListNode getIntersectionNode(ListNode headA,ListNode headB){
ListNode h1 = headA,h2 = headB;
while(h1 !=h2){
h1 = h1 == null?headB:h1.next;
h2 = h2 == null?headA:h2.next;
}
return h1;
}
//用集合好慢
//执行用时:8 ms, 在所有 Java 提交中击败了16.26% 的用户
//内存消耗:42.3 MB, 在所有 Java 提交中击败了5.59% 的用户
public ListNode getIntersectionNode(ListNode headA,ListNode headB){
Set<ListNode> set = new HashSet<>();
while(headA!=null){
set.add(headA);
headA = headA.next;
}
while(headB !=null){
if(set.contains(headB)){
return headB;
}
headB = headB.next;
}
return null;
}
【第五题】反转链表
分析:看见反转——>立即推,栈,双指针,reverse,递归。
链表的反转——>头插法。
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:38.1 MB, 在所有 Java 提交中击败了83.24% 的用户
class Solution{
public ListNode reverseList(ListNode head){
if(head == null || head.next == null){return head;}
ListNode i = new ListNode(0);
i.next = head;
ListNode cur = head.next;
head.next =null;
while(cur!=null){
ListNode temp = cur.next;
cur.next = head;
i.next = cur;
head = i.next;
//cur.next = null;
cur = temp;
}
return i.next;
}
}
【第六题】链表相交
分析:判断链表是否相交
//套用交集那个题,但是好慢
//执行用时:391 ms, 在所有 Java 提交中击败了6.07% 的用户
//内存消耗:41 MB, 在所有 Java 提交中击败了86.98% 的用户
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode h1 = headA;
ListNode h2 = headB;
while(h1 !=h2){
h1 = h1 == null?headA:h1.next;
h2 = h2 == null?headB:h2.next;
}
if(h1 == null && h1 == h2){
return null;
}
return h1;
}
}
//风水轮流转,这个写法居然比上边那个效率要高了
//执行用时:1 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:41.2 MB, 在所有 Java 提交中击败了67.90% 的用户
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
//两者都为空
if(headA == null || headB == null){return null;}
//两者都只有一个结点
if(headA.next == null && headB.next == null && headA == headB){
return headA;
}
//其中一个链表只有一个结点
if(headA.next == null){
if(headB.next== headA){return headA;}
}
if(headB.next == null){
if(headA.next == headB){return headB;}
}
//两个链表都不止一个结点
//遍历一遍得到长度
ListNode i = headA;
ListNode j = headB;
int lenA = 0,lenB = 0;
while(i.next!=null){
i = i.next;
lenA++;
}
while(j.next!=null){
j = j.next;
lenB++;
}
//System.out.println(lenA+" "+lenB);
int n = Math.abs(lenB-lenA);
i = headA;
j = headB;
//int n = Math.abs(skipA - skipB);
while(n!=0){
if(lenA > lenB){
i = i.next;
}else{
j = j.next;
}
n--;
}
// System.out.println(j.val);
while(i.next!=null && j.next!=null){
if(i == j){
return i;
}
i = i.next;
j = j.next;
}
return i == j?i:null;
}
}
【第七题】相交链表(和前两题一样)
【第八题】回文链表
分析:
方法一:栈
方法二:将链表的值都填入数组,然后用双指针
//执行用时:1 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:41 MB, 在所有 Java 提交中击败了77.43% 的用户
class Solution {
public boolean isPalindrome(ListNode head) {
//if(head)
int len = 0;
ListNode i = head;
while(i!=null){
i = i.next;
len++;
}
int[] arr = new int[len];
for(int j = 0;j < len;j++){
arr[j] = head.val;
head = head.next;
}
int k = 0,j = len-1;
while(k < j){
if(arr[k] !=arr[j]){
return false;
}
k++;
j--;
}
return true;
}
}
【第九题】删除排序链表中重复的元素
方法一:集合去重
方法二:双指针前后比较
//执行用时:5 ms, 在所有 Java 提交中击败了91.51% 的用户
//内存消耗:39.9 MB, 在所有 Java 提交中击败了39.95% 的用户
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null){return null;}
HashSet<Integer> set = new HashSet<>();
ListNode cur = head;
ListNode pre = new ListNode(-1);
pre.next = cur;
while(cur!=null){
if(!set.contains(cur.val)){
set.add(cur.val);
pre = pre.next;
cur = cur.next;
}else{
pre.next = cur.next;
cur = cur.next;
}
}
//System.out.println(set);
return head;
}
}
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:37.6 MB, 在所有 Java 提交中击败了90.91% 的用户
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null){return null;}
if(head.next == null){return head;}
ListNode cur = head.next;
ListNode pre = head;
while(cur.next!=null){
if(pre.val == cur.val){
pre.next = cur.next;
cur = cur.next;
}else{
pre = pre.next;
cur = cur.next;
}
}
if(pre.val == cur.val){
pre.next = null;
}
return head;
}
}
【第十题】环形链表
分析:判断链表是否有环。
方法一:快慢指针,有环的话终将相遇。
方法二:集合判重。(好慢好慢好慢)
//执行用时:4 ms, 在所有 Java 提交中击败了19.84% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了40.89% 的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){return false;}
ListNode i = head;
HashSet<ListNode> set = new HashSet<>();
while(i!= null){
if(!set.add(i)){
return true;
}
if(i.next ==null){
return false;
}
i = i.next;
}
return true;
}
}
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:38.4 MB, 在所有 Java 提交中击败了88.82% 的用户
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){
if(fast == null || fast.next == null){
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
【第十一题】从尾到头打印链表
分析:
方法一:将结点的值存入栈中,然后将栈中元素一个一个弹到数组中。(这么慢的速度是真实的吗)
方法二:倒过来存到数组中,省略了栈的空间。
//执行用时:2 ms, 在所有 Java 提交中击败了36.94% 的用户
//内存消耗:38.9 MB, 在所有 Java 提交中击败了81.41% 的用户
class Solution {
public int[] reversePrint(ListNode head) {
int len = 0;
Stack<Integer> s = new Stack<>();
while(head!=null){
s.push(new Integer(head.val));
head = head.next;
len++;
}
//System.out.println("stack: " + s);
int[] arr = new int[len];
for(int i = 0;i < len;i++){
arr[i] = (Integer)s.pop();
}
return arr;
}
}
//执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
//内存消耗:39 MB, 在所有 Java 提交中击败了79.15% 的用户
class Solution {
public int[] reversePrint(ListNode head) {
int len = 0;
ListNode cur = head;
while(cur!=null){
len++;
cur = cur.next;
}
//System.out.println("stack: " + s);
int[] arr = new int[len];
for(int i = len-1;i >=0;i--){
arr[i] = head.val;
head = head.next;
}
return arr;
}
}