链表题目题解

题目全部出自leetcode,题目详细介绍可去官网查询

1、删除链表中的结点(237)

有一个单链表的 head,我们想删除它其中的一个节点 node。

给你一个需要删除的节点 node 。你将 无法访问 第一个节点  head。

链表的所有值都是 唯一的,并且保证给定的节点 node 不是链表中的最后一个节点。

删除给定的节点。

思路:结点包括元素和指针两个部分,要删除一个结点,就是让它后面结点的全部内容替代它,即元素变为它的元素,指针变为它的指针

public class ListNode {   //链表
     int val;   //元素
     ListNode next;   //指针
     ListNode(int x) { val = x; }
 }

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;   //元素内容变为下一个结点的内容
        node.next = node.next.next;  //next指针变成下一个结点的指针
    }
}

2、反转链表(206)

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

思路1:利用递归

反转链表,顾名思义,我们要设立一个新的头指针指向尾结点,然后连接每一个结点的指针都转向

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode newHead = reverseList(head.next);  
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}

newHead通过对reverseList递归,成功建立了一个从尾结点到头结点的下一个结点的反转链表,最后剩下头结点,我们就使用两步:令头结点的下一个结点变成头结点的前一个结点(建立一个新的方向相反的指针),头结点成为尾结点,它要指向null

思路2:

不使用递归,将原链表的反转存在newHead中,首先肯定是一个循环的过程;newHead事先存放了null,保证头结点被newHead指向后,它连接的下一个为空,即他本身变成了尾结点

让头结点由head指向变成newHead指向:先让这个结点的next指针指向newHead原有的结点,再令newHead指向这个结点,最后head指向这个结点原来的下一个结点

但是这里出现一个问题,就是head想要通过head.next找到原来的下一个结点是不行的,因为原头结点已经断开,接到newHead上了,所以引用了tmp中间指针,每次断结点时先把它接到下一个结点上,等head要连它时,直接连tmp即可

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}

class Solution {
    public ListNode reverseList(ListNode head) {
		if (head == null || head.next == null) return head;
		ListNode newHead = null;
		while (head != null) {
			ListNode tmp = head.next;
			head.next = newHead;
			newHead = head;
			head = tmp;
		}
		return newHead;
    }
}

3、环形链表(141)

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

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

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

思路:快慢指针,顾名思义就是一个指针快一点,一个指针慢一点,所做的处理就是快指针一次走两个结点,慢指针,一次走一个。如果有环,这两个指针一定会在某一时刻相遇。

如果没有环,快指针一定会先于慢指针指向null

本题为boolean型,寻找真假条件即可完成,最后返回true/false

class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
        next = null;
    }
}
public class Solution {
	public boolean hasCycle(ListNode head) {
		if (head == null || head.next == null) return false;
		
		ListNode slow = head;
		ListNode fast = head.next;
		while (fast != null && fast.next != null) {
			slow = slow.next;
			fast = fast.next.next;
			
			if (slow == fast) return true;
		}
		
		return false;
    }
}

4、删除排序链表中的重复元素(83)

给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。

思路:链表已经排序就比较方便了,只要将链表遍历一遍,看前后两个元素是否相等,相等就删除结点,不相等就继续遍历

引入了tmp指针从head开始依次遍历

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null){   //如果链表是空的,直接返回
            return null;
        }
        ListNode tmp = head;
        while(tmp.next != null){
            if(tmp.val == tmp.next.val){  //如果元素与下一节点元素相同,删除下一个节点
                tmp.next = tmp.next.next;
            }else{
                tmp = tmp.next;
            }    
        }
        return head;
    }
}

5、移除链表元素(203)

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

思路1:跟上面删除结点不同,要判断头结点会不会被删除,如果头结点删除就要让头指针指向下一位,这里不能用判断,而是用循环

比如说第一和第二个结点值都相同,且为val,那判断完第一个结点还要继续判断第二个结点。对于不知道判断次数的循环使用while

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head != null && head.val == val){
            head = head.next;
        }
        if(head == null){
            return head;
        }
        ListNode tmp = head;
        while(tmp.next != null){            
            if(tmp.next.val == val){
                tmp.next = tmp.next.next;
            }else{
                tmp = tmp.next;
            }
        }
        return head;
    }
}

思路2:利用递归,从头结点开始,不停判断现在结点的下一个结点值是否等于val,如果等于,就删除下一个结点,如果不等于,就继续下一个结点的递归

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        head.next = removeElements(head.next, val);
        return head.val == val ? head.next : head;
    }
}

6、链表的中间结点(876)

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

思路1:想要找到中间结点,就要知道链表的长度,先对链表进行一遍遍历,存放在len变量里;第二遍遍历直接找中间值即可

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode tmp = head;
        int len = 0;   //记录链表长度
        while(tmp.next != null){
            tmp = tmp.next;
            len += 1;
        }
        int mid = (len+1)/2;
        for(int i = 0; i < mid; i++){
            head = head.next;
        }
        return head;
    }
}

思路2:快慢指针的思想,快指针一次移动两步,慢指针一次移动一步。通过观察可以发现,快指针走到尾结点需要 len/2 次,这就是中间结点的位置,既然慢指针一次移动一步,那么等到快指针移动到尾结点,慢指针一定在中间结点位置

public class ListNode {
    int val;
    ListNode next;
    ListNode() {}
    ListNode(int val) { this.val = val; }
    ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值