# 二、单链表

// Definition for singly-linked list.
public class SinglyListNode {
int val;
SinglyListNode next;
SinglyListNode(int x) { val = x; }
}


### 1.设计单链表

707. 设计链表

class MyLinkedList {
//头结点
//链表长度
private int N;

//结点类
private class Node {
int val;
Node next;

public Node(int val, Node next) {
this.val = val;
this.next = next;
}
}

/**
* Initialize your data structure here.
*/
// 初始化头结点
// head = new Node(0, null);
// 初始化结点个数
N = 0;
}

/**
* Get the value of the index-th node in the linked list. If the index is invalid, return -1.
*/
public int get(int index) {
if (index < 0 || index >= N) {
return -1;
}
//通过循环，从头结点开始往后找，依次找index次，就可以找到对应元素
for (int i = 0; i < index; i++) {
node = node.next;
}
return node.val;
}

/**
* Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
*/
Node newNode = new Node(val, head);
N++;
}

/**
* Append a node of value val to the last element of the linked list.
*/
if (N == 0) {
}
//找到当前最后一个结点
while (node.next != null) {
node = node.next;
}
//创建新结点，保存元素val
Node newNode = new Node(val, null);
node.next = newNode;
N++;
}

/**
* Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.
*/
public void addAtIndex(int index, int val) {
if (index <= 0 || N == 0) {
} else if (index <= N) {
// 找到index位置前一个结点
for (int i = 0; i < index - 1; i++) {
pre = pre.next;
}
// 找到index位置的结点
Node cur = pre.next;
//创建新结点，并且新结点需要指向原来i位置的结点
Node newNode = new Node(val, cur);
//原来i位置的前一个结点指向新结点即可
pre.next = newNode;
//元素个数+1
N++;
}
}

/**
* Delete the index-th node in the linked list, if the index is valid.
*/
public void deleteAtIndex(int index) {
if (index == 0) {
} else if (index < 0 || index >= N) {
return;
} else {
//找到i位置的前一个结点
for (int i = 0; i < index - 1; i++) {
pre = pre.next;
}
//要找到i位置的结点
Node cur = pre.next;
//要找到i位置的下一个结点
Node nextNode = cur.next;
//前一个结点指向后一个结点
pre.next = nextNode;
}
//元素个数-1
N--;
}
}

/**
* int param_1 = obj.get(index);
* obj.deleteAtIndex(index);
*/


# 三、双指针技巧

## 1. 环形链表

141. 环形链表

    public boolean hasCycle(ListNode head) {
return false;
}
while (slowNode != fastNode) {
if (slowNode.next != null && fastNode.next != null && fastNode.next.next != null) {
slowNode = slowNode.next;
fastNode = fastNode.next.next;
} else {
return false;
}
}
return true;
}


public boolean hasCycle(ListNode head) {
return false;
}
while (l1 != null && l2 != null && l2.next != null) {
if (l1 == l2) {
return true;
}
l1 = l1.next;
l2 = l2.next.next;
}
return false;
}


public class Solution {
Set<ListNode> seen = new HashSet<ListNode>();
return true;
}
}
return false;
}
}


## 2.环形链表Ⅱ

142. 环形链表 II

        Set<ListNode> seen = new HashSet<>();
}
}
return null;
}


    public ListNode detectCycle(ListNode head) {
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
while (n != slow) {
slow = slow.next;
n = n.next;
}
return n;
}
}
return null;
}


## 3.相交链表

160. 相交链表

    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
HashSet<ListNode> seen = new HashSet<>();
break;
}
}
}
}
return null;
}


        ListNode temp = headB;
}
}
}
return null;


    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
while (A != B) {
A = A != null ? A.next : headB;
B = B != null ? B.next : headA;
}
return A;
}


## 4.删除链表的倒数第N个节点

19. 删除链表的倒数第N个节点
fast指针先行n步，然后两指针以相同速度共同前进，在fast指针刚好走到最后一个元素时一起停下脚步，此刻，slow站着的地方就是被删除元素的前一个元素，然后进行删除操作。

        // 双指针
for (int i = 0; i < n; i++) {
if (fast != null) {
fast = fast.next;
}
}
if (fast == null) {
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;


    public ListNode removeNthFromEnd(ListNode head, int n) {
List<ListNode> list = new ArrayList<>();
while (node != null) {
node = node.next;
}
int len = list.size();
if (len == n) {
}
ListNode pre = list.get(len - n - 1);
pre.next = list.get(len - n).next;// list.get(len - n + 1)会索引越界
}


## 5.小结

// Initialize slow & fast pointers
/**
* Change this condition to fit specific problem.
* Attention: remember to avoid null-pointer error
**/
while (slow != null && fast != null && fast.next != null) {
slow = slow.next;           // move slow pointer one step each time
fast = fast.next.next;      // move fast pointer two steps each time
if (slow == fast) {         // change this condition to fit specific problem
return true;
}
}
return false;   // change return value to fit specific problem


1. 任何一个结点每次调用next字段之前，检查结点是否为空
获取空节点的下一个节点将导致空指针错误。例如，在我们运行 fast = fast.next.next 之前，需要检查 fast 和 fast.next 不为空。
2. 仔细定义循环的结束条件。
运行几个示例，以确保你的结束条件不会导致无限循环。在定义结束条件时，你必须考虑我们的第一点提示。

1. 如果没有循环，快指针需要 N/2 次才能到达链表的末尾，其中 N 是链表的长度。
2. 如果存在循环，则快指针需要 M 次才能赶上慢指针，其中 M 是列表中循环的长度。

# 四、经典问题

## 1.反转链表

206. 反转链表

    public ListNode reverseList(ListNode head) {
ListNode pre = null;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}


    public ListNode reverseList(ListNode head) {
}
return cur;
}


## 2.移除链表元素

203. 移除链表元素

    public ListNode removeElements(ListNode head, int val) {
// 删除开头可能存在的所有结点，要用while循环
}
while (pre != null && pre.next != null) {
if (pre.next.val == val) {
pre.next = pre.next.next;
} else {// 考虑到可能需要删除连续的结点，所以这里的else不能省
pre = pre.next;
}

}
}


        if(head==null)
return null;
}else{
}


## 3.奇偶链表

328. 奇偶链表

    public ListNode oddEvenList(ListNode head) {
return null;
}
while (even != null && even.next != null) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
}


## 4.回文链表

234. 回文链表

    public boolean isPalindrome(ListNode head) {
List<ListNode> list = new ArrayList<>();
while (node != null) {
node = node.next;
}
int l = 0, r = list.size() - 1;
while (l < r) {
if (list.get(l++).val != list.get(r--).val){
return false;
}
}
return true;
}


## 5.小结

1. 通过一些测试用例可以节省时间
• 因为链表不容易调试，因此，编写代码之前，自己尝试几个不同的示例来验证算法是很有用的。
1. 可以同时使用多个指针。
• 有时在设计链表问题算法时，需要同时跟踪多个结点。得记住需要跟踪哪些结点，并且可以自由地使用几个不同的结点指针来同时追踪这些结点。
• 如果你使用多个指针，最好为它们指定适当的名称。
1. 在许多情况下，需要跟踪当前结点的前一个结点。
• 无法追溯单链表的前一个结点。因此，不仅要存储当前结点，还要存储前一个结点。

# 五、双链表

1. 链接 cur 与 prev 和 next，其中 next 是 prev 原始的下一个节点；
2. 用 cur 重新链接 prev 和 next。

707. 设计链表

# 六、小结

1. 它们都无法在常量时间内随机访问数据。
2. 它们都能够在 O(1) 时间内在给定结点之后或列表开头添加一个新结点。
3. 它们都能够在 O(1) 时间内删除第一个结点。

• 在单链表中，它无法获取给定结点的前一个结点，因此在删除给定结点之前我们必须花费 O(N) 时间来找出前一结点。
• 在双链表中，这会更容易，因为我们可以使用“prev”引用字段获取前一个结点。因此我们可以在 O(1) 时间内删除给定结点。

## 1.合并两个有序链表

21. 合并两个有序链表

        ListNode node1 = l1, node2 = l2;
ListNode merged = null;
while (node1 != null && node2 != null) {
if (node1.val <= node2.val) {
merged = node1;
node1 = node1.next;
} else {
merged = node2;
node2 = node2.next;
}
merged = merged.next;
}


        ListNode l1 = new ListNode(1);
ListNode l2 = new ListNode(2);
ListNode l3 = new ListNode(4);
l1.next = l2;
l2.next = l3;
l3.next = null;
ListNode l4 = new ListNode(1);
ListNode l5 = new ListNode(3);
ListNode l6 = new ListNode(4);
l4.next = l5;
l5.next = l6;
l6.next = null;

    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
cur.next = l1;
cur = cur.next;
l1 = l1.next;
} else {
cur.next = l2;
cur = cur.next;
l2 = l2.next;
}
}
if (l1 == null) {
cur.next = l2;
} else {
cur.next = l1;
}
}


## 2.两数相加

2. 两数相加

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode cur = res;
int carry = 0;
while (l1 != null || l2 != null) {
int a = l1 == null ? 0 : l1.val;
int b = l2 == null ? 0 : l2.val;
int sum = a + b + carry;
carry = sum / 10;
cur.next = new ListNode(sum % 10);
cur = cur.next;
if (l1 != null)
l1 = l1.next;
if (l2 != null)
l2 = l2.next;
}
if (carry > 0) {
cur.next = new ListNode(carry);
}
return res.next;
}


## 3.扁平化多级双向链表

430. 扁平化多级双向链表

  public Node flatten(Node head) {

Deque<Node> stack = new ArrayDeque<>();

while (!stack.isEmpty()) {
curr = stack.pop();
prev.next = curr;
curr.prev = prev;

if (curr.next != null) stack.push(curr.next);
if (curr.child != null) {
stack.push(curr.child);
// don't forget to remove all child pointers.
curr.child = null;
}
prev = curr;
}
// detach the pseudo node from the result
}



## 4.复制带随机指针的链表

138. 复制带随机指针的链表

    public Node copyRandomList(Node head) {
// 1.每个原结点后面复制一个结点
if (head == null) return null;
while (l1 != null) {
Node newNode = new Node(l1.val);
newNode.next = l1.next;
l1.next = newNode;
l1 = l1.next.next;
}
// 2.设置新结点的random引用
while (l1 != null && l1.next != null) {
if (l1.random != null)//别漏掉这句，因为random可能为null
l1.next.random = l1.random.next;
l1 = l1.next.next;
}
// 3.把新链表提取出来
Node res = new Node(0);
Node cur = res;
while (l1 != null && cur != null) {
cur.next = l1.next;
cur = cur.next;
l1.next = cur.next;//我一开始漏掉了这句，会出现错误，所以要把原链表还原回来
// Next pointer of node with label 7 from the original list was modified.
l1 = l1.next;
}
return res.next;
}


• map.get(原节点)，得到的就是对应的新节点
• map.get(原节点.next)，得到的就是对应的新节点.next
• map.get(原节点.random)，得到的就是对应的新节点.random

map.get(原节点.random) 这样新链表的next和random都被串联起来了

    public Node copyRandomList1(Node head) {
// 1.新建一个Map容器存储新旧结点
Map<Node, Node> map = new HashMap<>();
while (ptr != null) {
map.put(ptr, new Node(ptr.val));
ptr = ptr.next;
}
// 2.设置旧结点的next和random属性
while (ptr != null) {
map.get(ptr).next = map.get(ptr.next);
map.get(ptr).random = map.get(ptr.random);
ptr = ptr.next;
}
// 3.提取新链表，而且发现并不需要像上面的方法那样还原原链表
Node resNode = new Node(0);
Node cur = resNode;
while (ptr != null && cur != null) {
cur.next = map.get(ptr);
cur = cur.next;
ptr = ptr.next;
}
return resNode.next;*/
}


## 5.旋转链表

61. 旋转链表

（吐槽：这道中等题想了几分钟就想出来了，前面两道想了好久。。）

1. 非null验证
2. 创建链表指针l1，用来找到链表尾，并用count记录链表长度。
    public ListNode rotateRight(ListNode head, int k) {
if (head == null) return null;
int count = 1;
while (l1.next != null) {
l1 = l1.next;
count++;
}
for (int i = 0; i < count - k % count; i++) {
l1 = l1.next;
}
l1.next = null;
}


# 七、闲话

http://music.163.com/song?id=546504279&userid=88476473

04-20 270

04-04 352
01-28 197
04-03 137
04-23 283
10-20 11
05-11 116
01-04 111
01-07 31