目录
数据结构是数据的组织方式。
链表(线性表)基本概念
种类
链表节点:
// Definition for singly-linked list.
public class ListNode {
int val;
ListNode next;
public ListNode(int x) {
this.val = x;
}
}
线性表一般有单向链表、双向链表、循环链表、双向循环链表等
增删查操作
增加操作
s.next = p.next;
p.next = s;
删除操作
p.next = p.next.next;
查找操作
第一种情况是按照位置序号来查找。
第二种情况是按照具体的成绩来查找。
总结
链表在新增、删除数据都比较容易,可以在 O(1) 的时间复杂度内完成。但对于查找,不管是按照位置的查找还是按照数值条件的查找,都需要对全部数据进行遍历。这显然就是 O(n) 的时间复杂度。
虽然链表在新增和删除数据上有优势,但仔细思考就会发现,这个优势并不实用。这主要是因为,在新增数据时,通常会伴随一个查找的动作。
线性表真正的价值在于,它对数据的存储方式是按照顺序的存储。如果数据的元素个数不确定,且需要经常进行数据的新增和删除时,那么链表会比较合适。如果数据元素大小确定,删除插入的操作并不多,那么数组可能更适合些。
链表解题技巧
关于线性表,最高频的问题都会围绕数据顺序的处理。
链表翻转(三个指针)
范例:链表的翻转。给定一个链表,输出翻转后的链表。例如,输入1 ->2 -> 3 -> 4 ->5,输出 5 -> 4 -> 3 -> 2 -> 1。
使用三个指针:
特别注意初始状态:
ListNode prev = head ;
ListNode curr = head.next ;
ListNode next = null ;
下图中初始状态不对,后面过程对的
while(curr != null){
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
快慢指针(两个指针)
范例:给定一个的链表,查找出这个链表中间位置的结点的数值,,如果为偶数返回中间两个元素右一个的值。
初始化:
ListNode slow = head ;
ListNode fast = head ;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
范例:判断链表是否有环。如下图所示,这就是一个有环的链表。
LetCode题目
链表:
25. K 个一组翻转链表
206. 反转链表
用上文中的方法
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null){
return head ;
}
ListNode prev = head ;
ListNode curr = head.next ;
ListNode next = null ;
prev.next = null ; //尾节点指null
while(curr != null){
next = curr.next ;
curr.next = prev ;
prev = curr ;
curr = next ;
}
return prev ;
}
}
注意返回值;
876. 链表的中间结点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head ;
ListNode fast = head ;
while(fast!=null && fast.next!=null){
fast = fast.next.next ;
slow = slow.next ;
}
return slow ;
}
}
141. 环形链表
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode fast = head ;
ListNode slow = head ;
while(fast != null && fast.next != null ) {
fast = fast.next.next ;
if (fast == slow){ // 快指针追上慢指针,有环
return true ;
}
slow = slow.next ;
}
return false ;
}
}
注意fast指针追上slow指针,