链表类算法
1.关于java设计链表(不要小看,不看代码你设计一个试试)
get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。 addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。 addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。 addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。 deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
public class ListNode{ ListNode next; int val; public ListNode(int x) { this.val=x; } }; class MyLinkedList{ int size; ListNode head; public MyLinkedList(){ head=new ListNode(0); size=0; } public void addAtIndex(int index, int val) { if(index>size) { return; } if(index<0) { index=0; } ListNode pre=head; size++; for(int i=0;i<index;i++) { pre=pre.next; } ListNode temp=new ListNode(val); ListNode h=pre.next; pre.next=temp; temp.next=h; } public void addAtHead(int val){ addAtIndex(0,val); } public void addAtTail(int val){ addAtIndex(size,val); } public int get(int index){ if(index<0||index>=size) { return -1; } ListNode pre = head; for(int i=0;i<index+1;i++) { pre=pre.next; } return pre.val; } public void deleteAtIndex(int index) { if(index<0||index>=size) { return; } ListNode pre=head; size--; for(int i=0;i<index;i++) { pre=pre.next; } pre.next=pre.next.next; } }
2.双指针的经典问题(环形链表和反转链表即所在位置)
1.环形链表问题
if(head==null) { return null; } ListNode fast=head; ListNode slow=head; while(fast!=null) { if(fast.next==null) { return null; } fast=fast.next.next; slow=slow.next; if(slow==fast)//当慢指针和快指针相等时此时相遇 { ListNode pre=head; while(pre!=slow) { pre=pre.next; slow=slow.next; } return slow;//这时的位置表示相遇点的位置(关于为什么是这个位置可以看下图的解释) } } return null;//快指针比慢指针每次多走俩格后面总会在某一个地方相遇
2.反转链表问题()
public ListNode reserve(ListNode head) { if(head=null||head.next=null) { return head; } ListNode cur=head;//记录当前节点 ListNode pre=null;//记录前一个节点 ListNode next=null;//记录下一个节点 while(cur!=null) { next=cur.next;//提前记录下一个节点 cur.next=pre;//让当前节点的next域指向上一个节点 pre=cur;//将前一个节点向后推移 cur=next;//将当前节点向后推移 } return pre;//返回前一个节点。 } //这是基本的链表反转问题
链表反转问题可以联想到k个一组反转,奇偶链表
奇偶链表问题
public ListNode bianhua(ListNode head) { if(head==null||head.next==null) { retur head; } ListNode h=head.next;//偶数节点的起始位置 ListNode odd=head;//奇数节点的起始位置 ListNode even=h;//偶数节点的起始位置 while(even!=null&&even.next!=null) { odd.next=even.next; odd=odd.next; even.next=odd.next; even=even.next; } odd.next=h; return head; }
3.关于链表的拷贝问题
我在许多题中看到了关于深拷贝的问题,不仅仅是链表,就发现只要有深拷贝,就一定会用到hash表
复制带随机指针的链表
/* // Definition for a Node. class ListNode { int val; Node next; Node random; public Node(int val) { this.val = val; this.next = null; this.random = null; } } */ public ListNode copyRandomList(ListNode head){ if(head==null) { return null; } ListNode pre=head; Map<ListNode,ListNode> map=new HashMap<>(); while(pre!=null) { ListNode cur=new ListNode(pre.val); map.put(pre,cur); pre=pre.next; } pre=head; ListNode node=map.get(pre); ListNode temp=node; while(pre!=null) { node.next=map.get(pre.next); node.random=map.get(pre.random); pre=pre.next; node=node.next; } return temp; }
扁平化多级双向链表
输入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12] 输出:[1,2,3,7,8,11,12,9,10,4,5,6] 解释:
输入的多级列表如下图所示:
扁平化后的链表如下图:
/* // Definition for a Node. class Node { public int val; public Node prev; public Node next; public Node child; }; */ class Solution { private List<Node> nodes; public Node flatten(Node head) { if (head == null) { return null; } nodes = new ArrayList<>(); dfs(head); Node res = nodes.get(0); res.child = null; Node it = res; for (int i = 1; i < nodes.size(); i++) { Node node = nodes.get(i); node.child = null; it.next = node; node.prev = it; it = node; } return res; } private void dfs(Node node) { if (node == null) { return; } nodes.add(node); if (node.child == null) { dfs(node.next); } else { dfs(node.child); dfs(node.next); } } }