链表的定义和内存存储方式
链表使用非连续的内存空间来存储数据,通过next指针将内存块串联在一起
解题技巧
链表相关问题都会涉及遍历,核心是通过画图举例来确定遍历的三要素:
- 遍历的结束条件: p==null or p.next == null
- 指针的初始值:p=head or 。。。
- 遍历的核心逻辑: 需要根据题目要求来定
特殊情况需要处理:空链表、头结点、尾节点等;
引入虚拟节点:是否可以通过添加虚拟节点来简化编程(需要看下头结点是否需要特殊处理,来判断是否需要引入头结点)
头插尾插 链表反转用的头插,大部分情况用尾插;
Definition for singly-linked list.
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; }
}
// 虚拟节点
ListNode newHead = new ListNode();
newHead.next = head;
例题
203 移除链表元素
思考:这个题目只用到了一个指针去做,需要保留待删除前面的指针,故使用prev.next.val 去做判断;
/**
* Definition for singly-linked list.
* 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) {
//if( head == null ) return null;
ListNode newHead = new ListNode();
newHead.next = head;
ListNode p = newHead;
while(p.next != null){
if(p.next.val == val){
p.next = p.next.next;
}else{
p = p.next;
}
}
return newHead.next;
}
}
876 链表中间节点
思考:这个题目没有用到虚拟头结点 解题思路:分成奇数和偶数的情况去判断,最后合并
/**
* Definition for singly-linked list.
* 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 p1 = head;
ListNode p2 = head;
while(p1 != null && p1.next != null ){
p1 = p1.next.next;
p2 = p2.next;
}
return p2;
}
}
21合并两个有序链表
思路:新开一个头结点,用尾插的方式插入数据
/**
* Definition for singly-linked list.
* 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 mergeTwoLists(ListNode list1, ListNode list2) {
ListNode newHead = new ListNode();
ListNode tail = newHead;
tail.next = null;
ListNode p1 = list1;
ListNode p2 = list2;
while(p1 !=null && p2 !=null){
if(p1.val < p2.val){
tail.next = p1;
tail = p1;
p1 = p1.next;
}else{
tail.next = p2;
tail = p2;
p2 = p2.next;
}
}
if(p1 == null){
tail.next = p2;
}else{
tail.next = p1;
}
return newHead.next;
}
}
206反转链表
/**
* Definition for singly-linked list.
* 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) return null;
ListNode newhead = null;// newhead 为指针非节点
ListNode p = head;
ListNode cur;
while(p !=null){
cur = p.next;// 临时节点存储以防丢
p.next = newhead;// 头插核心
newhead = p;// 头插核心
p = cur;
}
return newhead;
}
}
234回文链表
思路:和反转链表类似,可以现将链表进行反转,然后在遍历;需要注意的是,链表反转过程中需要重新new ListNode
/**
* Definition for singly-linked list.
* 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 boolean isPalindrome(ListNode head) {
ListNode newHead = null;
ListNode p = head;
while (p != null){
ListNode cur = new ListNode(p.val);
cur.next = newHead;
newHead = cur;
p = p.next;
}
ListNode p1 = newHead,p2 = head;
while(p1!=null){
if(p1.val == p2.val){
p1 = p1.next;
p2 = p2.next;
}else{
return false;
}
}
return true;
}
}
328奇偶链表
思路:新建两个头结点,分别奇偶链入自己的结点后面,注意:新加结点后的next需要置为null,否则会出现环
/**
* Definition for singly-linked list.
* 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 oddEvenList(ListNode head) {
if(head == null) return head;
ListNode newHead1 = new ListNode();
ListNode newHead2 = new ListNode();
ListNode p = head,p1 = newHead1,p2 = newHead2;
int num = 1;
while(p != null ){
ListNode tmp = p.next;
if(num%2==1){
p1.next = p;
p1 = p;
p1.next = null; // important
}else{
p2.next = p;
p2 = p;
p2.next = null; // important
}
p = tmp;
num++;
}
p1.next = newHead2.next;
return newHead1.next;
}
}
141环形链表
思路:快慢指针,如果出现环形的化,快慢指针指定相遇,需要注意特殊点: 1.链表是否为空。 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(head == null) return false;
ListNode fast = head.next,slow = head;
while(fast != null && fast.next !=null && fast !=slow){
slow = slow.next;
fast = fast.next.next;
}
if(fast == slow){
return true;
}else{
return false;
}
}
}
142环形链表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) {
if(head == null || head.next ==null) return null;
ListNode fast = head,slow = head;
boolean flag = false;
while(fast !=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
flag = true;
break;
}
}
if(flag){
slow = head;
while(slow != fast){
slow =slow.next;
fast = fast.next;
}
return slow;
}else{
return null;
}
}
}