题目全部出自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;
}
}