链表的基础知识是什么?
1.翻转链表
2.双指针合并链表
3.找环
4.删除node
5.结构转换
链表分为单链表,双链表,循环链表(有环)
链表的核心操作集有三种:插入,删除,查找(遍历)
链表是由一组不必相连(可以连续也可以不连续)的内存结构(节点),按特定的顺序链接在一起的抽象数据类型
单链表
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
}
}
双向链表
class ListNode {
int val;
ListNode next;
ListNode prev;
ListNode(int val) {
this.val = val;
}
}
反转链表
public ListNode reverse(ListNode head) {
ListNode prev = null;
while (head != null) {
ListNode next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
}
快慢指针找中点
private ListNode endOfFirstHalf(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
链表和array比较有什么性质?
1.即便是排好序的数组,你用二分查找,时间复杂度也是O(logn)。所以正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度是O(1)
2.如果你的代码对内存的使用非常苛刻,那数组就更适合你。因为链表中的每个节点都需要消耗额外的存储空间去存储一份指向下一个节点的指针
数组
优点:随机访问性强
查找速度快
缺点:插入和删除效率低
可能浪费内存
内存空间要求高,必须有足够的连续内存空间
数组大小固定,不能动态拓展
链表
优点:插入删除速度快
内存利用率高,不会浪费内存
大小没有固定,拓展很灵活
缺点:不能随机查找,必须从第一个开始遍历,查找效率低
public ListNode reverseList(ListNode head) {
ListNode newhead = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = newHead;
newHead = cur;
cur = next;
}
return newHead;
}
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
fast = head;
while (slow != fast) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
总结
LinkedList作为最简单的数据结构,leetcode所收录的题目只有43道,hard只有2道。作为用来入门的第一个数据结构十分适合,但是它的难点也在于Corner case的互相衔接,很多时候point的链接是十分费时且容易出错的。简易画图举例子验证自己的代码
1.常用reverse linkedlist的method,recursive or iterative
2.链表找环的基本方式
3.删除单个node可以直接在前一个node(prev)上将想删除的下一个node跳过
4.删除头结点的Corner case需要哨兵节点sentinel node或者叫dummy node
5.双指针可以有效解决距离终点多少距离的问题
6.prefix sum和two sum,找sublist sum = 0是一个很经典的技巧