链表
那什么是链表,链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由结点组成,每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
链表的分类
- 单链表
- 双链表
- 循环链表
链表特点
因为指针域的存在,链表的增添和删除都是O(1)的操作,也不会影响到其它节点,但查找的时候较复杂,需要从头节点开始查找,根据下一个节点的位置依次进行查找,因此链表的删除,查找操作是O(n)的操作
移除链表元素
听说用了虚拟头结点,再也不担心链表问题了
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode res = new ListNode(0); //设置虚拟头结点点
res.next=head;
ListNode cur = res;
if (head==null) return head;
while(cur.next!=null ){
if(cur.next.val==val){
cur.next=cur.next.next; //当下一个值与目标值相等时直接跳过下一个结点
}else{
cur=cur.next; 否则就继续
}
}
return res.next;
}
}
反转链表
一 迭代法
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
ListNode next =cur.next;
cur.next=pre;
pre=cur;
cur=next;
}
return pre;
}
}
二、递归
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(head,null);
}
public ListNode reverse(ListNode cur ,ListNode pre){
if(cur==null) return pre;
ListNode next = cur.next;
cur.next=pre;
return reverse(next,cur);
}
}
环形链表
本题注意返回的是结点 不是结点的索引
方法一、该方法借用哈希表进行求解
时间复杂度O(N)
空间复杂度O(N) N 均为链表结点的数量 需要保存 访问该链表的每一个结点
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode cur = head;
Set<ListNode> res =new HashSet<>();
while(cur!=null){
if(res.contains(cur)){
return cur;
}else{
res.add(cur);
}
cur=cur.next;
}
return null;
}
}
方法二、快慢指针
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null){
slow=slow.next;
if(fast.next!=null){
fast=fast.next.next;
}else{
return null;
}
if(fast==slow){
ListNode res = head;
while(slow!=res){
slow=slow.next;
res=res.next;
}
return res;
}
}
return null;
}
}