1.相交链表No.160
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if(headA==null||headB==null) return null;ListNode pA=headA;ListNode pB=headB;while(pA!=pB){pA=(pA==null)?headB:pA.next;pB=(pB==null)?headA:pB.next;}return pA; }
2.反转链表No.206
public ListNode reverseList(ListNode head) {ListNode curr = head;//当前节点,从头节点开始ListNode prev = null;//前一个节点,初始为nullwhile(curr!=null){ListNode nextTemp = curr.next;curr.next = prev;prev = curr;curr = nextTemp;}return prev;}
3.回文链表No.234
- 方法一
思路:顺序存放一个列表,倒序存放一个列表,比较两个列表是否相同。
public boolean isPalindrome(ListNode head) {ListNode p = head;List<Integer> list1 = new ArrayList<>();List<Integer> list2 = new ArrayList<>();while(p!=null){list1.add(p.val);p=p.next;}//将列表倒序ListNode curr = head;ListNode prev = null;while(curr!=null){ListNode nextTemp = curr.next;curr.next = prev;prev=curr;curr=nextTemp;}while(prev!=null){list2.add(prev.val);prev=prev.next;}return list1.equals(list2);}
- 方法二
列表+双指针
public boolean isPalindrome(ListNode head) {//使用列表+双指针List<Integer> list = new ArrayList<>();//定义链表节点ListNode curr = head;while(curr!=null){list.add(curr.val);curr=curr.next;}//定义指针int l = 0, r = list.size()-1;while(l<r){if(!list.get(l).equals(list.get(r))){return false;}l++;r--;}return true;}
4.环形链表
- 方法一:哈希表法
- 思路:
- 遍历链表,用一个哈希表记录访问过的节点。
- 如果在遍历过程中发现当前节点已经存在于哈希表中,说明链表有环。
- 如果遍历结束仍未发现重复节点,则链表无环。
注意: set中存放的是节点,而不是节点中的值
public boolean hasCycle(ListNode head) {Set<ListNode> set = new HashSet<>();while(head!=null){if(set.contains(head)){return true;}set.add(head);head=head.next;}return false;}
-
方法二:快慢指针法
-
思路:
- 使用两个指针,慢指针每次走一步,快指针每次走两步
- 如果链表中有环,快指针会在环内追上慢指针
- 如果快指针或其next为null,说明链表无环
public boolean hasCycle(ListNode head) {if(head==null||head.next==null){return false;}//定义快慢指针ListNode slow = head;ListNode quick = head;while(quick!=null&&quick.next!=null){slow=slow.next;quick = quick.next.next;if(slow==quick){return true;}}return false;}
5.环形链表IINo.142
- 方法一:哈希表法
public ListNode detectCycle(ListNode head) {//创建HashsetHashSet<ListNode> set = new HashSet<>();while(head!=null){if(set.contains(head)){return head;}set.add(head);head = head.next;}return null;}
- 方法二:快慢指针
public ListNode detectCycle(ListNode head) {ListNode slow = head,fast = head;while(fast!=null&&fast.next!=null){slow=slow.next;fast=fast.next.next;if(slow==fast){//证明有环break;}}if(fast==null||fast.next==null){return null;}//令其中一个环从头结点开始slow = head;while(slow!=fast){slow = slow.next;fast = fast.next;}return slow;}
6.合并两个有序链表No.21
- 方法一:列表+数组
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {if(list1==null&&list2==null){return null;}List<Integer> list = new ArrayList<>();//将链表放入列表list中while(list1!=null){list.add(list1.val);list1 = list1.next;}while(list2!=null){list.add(list2.val);list2 = list2.next;}Integer[] arr = list.toArray(new Integer[list.size()]);Arrays.sort(arr);//将数组转为链表ListNode head = new ListNode(arr[0]);ListNode curr =head;for(int i = 1;i<arr.length;i++){ListNode nextTem = new ListNode(arr[i]);curr.next = nextTem;curr=nextTem; }return head;}
- 方法二:指针
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {//创建一个新链表,首先创建一个虚拟头指针ListNode head = new ListNode(0);ListNode curr = head;//创建两个指针,分别遍历链表1、链表2;ListNode p1=list1,p2=list2;while(p1!=null&&p2!=null){if(p1.val<p2.val){curr.next = p1;p1 = p1.next;//p1指针向后移动}else{curr.next = p2;p2 = p2.next; //p2指针向后移动}curr = curr.next;//当前指针向后移动}//如果一个链表遍历完成,将另一个链表的剩余元素之间添加在后面;if(p1!=null){curr.next=p1;}else if(p2!=null){curr.next=p2;}return head.next;}
7.两数相加
- 逐位相加
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//创建虚拟节点,从而建立新的链表ListNode head = new ListNode(0);ListNode curr= head;//记录和int sum = 0;while(l1!=null||l2!=null||sum>0){if(l1!=null){sum+=l1.val;l1=l1.next;}if(l2!=null){sum+=l2.val;l2=l2.next;}curr.next = new ListNode(sum%10);curr=curr.next;sum/=10;}return head.next;}
8.删除链表的倒数第N个结点No.19
public ListNode removeNthFromEnd(ListNode head, int n) {//创建一个虚拟节点ListNode dummy = new ListNode(0);dummy.next = head;//定义双指针,创建快慢指针,先让快指针先走n步,当快指针走到链表末尾时,慢指针恰好到达需要删除节点的位置ListNode fast = dummy;ListNode slow = dummy;//利用for循环,让快指针先走n步for(int i = 0;i<n;i++){fast = fast.next;}//快慢指针同时走while(fast.next!=null){slow = slow.next;fast = fast.next;}//删除slow.next = slow.next.next;return dummy.next;}
9.两两交换链表中的节点No.24
- 方法一:迭代
public ListNode swapPairs(ListNode head) {//创建虚拟节点ListNode dummy = new ListNode(0);dummy.next = head;ListNode curr = dummy;while(curr.next!=null&&curr.next.next!=null){ListNode first = curr.next;ListNode second = curr.next.next;//交换节点first.next = second.next;second.next = first;curr.next = second;curr = first;}return dummy.next;}
- 方法二:递归
public ListNode swapPairs(ListNode head) {//递归if(head==null||head.next==null){return head;}//创建新的头节点ListNode newHead = head.next;head.next=swapPairs(newHead.next);newHead.next = head;return newHead;}
10.随机链表的复制No.138
- 思路
- 复制节点并插入到原链表中
- 设置新节点的random指针
- 拆分链表
public Node copyRandomList(Node head) {if(head==null){return null;}//1.复制节点并插入到原链表中Node curr = head;while(curr!=null){//创建新的节点Node newHead = new Node(curr.val);newHead.next = curr.next;curr.next = newHead;curr= curr.next.next; //移动到下一个原节点}//2.设置新节点的random指针curr = head;//将curr指向原来的头节点while(curr!=null){//判断random指针是否为空if(curr.random!=null){curr.next.random=curr.random.next;}curr= curr.next.next;}//3.拆分链表curr = head;Node newHead = head.next; // 新链表的头节点while(curr!=null){Node temp = curr.next;curr.next = temp.next; // 恢复原链表的 next 指针if(temp.next!=null){temp.next = temp.next.next; // 连接新链表的 next 指针}curr = curr.next; // 移动到下一个原节点}return newHead;}
11.排序链表No.148
public ListNode sortList(ListNode head) {// 1.如果链表中少于等于1个元素则不排序if (head == null || head.next == null) {return head;}// 2.将链表分为两部分ListNode mid = spilt(head);ListNode nextHead = mid.next;mid.next = null;// 3.递归排序链表的左右两部分ListNode l = sortList(head);ListNode r = sortList(nextHead);// 4.合并排序后的链表return merge(l, r);}// 归并排序public ListNode merge(ListNode l, ListNode r) {// 创建一个虚拟节点ListNode dummy = new ListNode(0);ListNode curr = dummy;while (l != null && r != null) {if (l.val <= r.val) {curr.next = l;l = l.next;} else {curr.next = r;r = r.next;}curr = curr.next;}// 左半部分剩余部分if (l != null) {curr.next = l;}else{ // 右半部分剩余部分curr.next = r;}return dummy.next;}// 找到链表的中间节点public ListNode spilt(ListNode head) {// 使用快慢指针进行拆分ListNode fast = head.next;ListNode slow = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}return slow;}
12.LRU缓存NO.146
class LRUCache {//定义双向链表class DListNode{DListNode prev;DListNode next;int key,value;public DListNode(){}public DListNode(int key,int value){this.key = key;this.value = value;}}//定义哈希表HashMap<Integer,DListNode> map = new HashMap<>();//定义缓存的长度int size = 0;//定义容量int capacity;//定义头尾节点,双向链表DListNode head,tail;public LRUCache(int capacity) {this.size=0;this.capacity=capacity;head = new DListNode();tail = new DListNode();head.next = tail;tail.prev = head;}public int get(int key) {DListNode node = map.get(key);int res = -1;//如果不存在返回-1if(node!=null){res = node.value;/*更新链表,key被访问过,放到链表的头部1.先删除2.放入头部*///删除链表节点node.prev.next = node.next;node.next.prev = node.prev;//放入头部DListNode temp = head.next;head.next = node;node.next = temp;temp.prev = node;node.prev = head;}return res;}public void put(int key, int value) {DListNode node = map.get(key);if(node!=null){node.value = value;//访问完,更新链表//删除链表节点node.prev.next = node.next;node.next.prev = node.prev;//放入头部DListNode temp = head.next;head.next = node;node.next = temp;temp.prev = node;node.prev = head;}else{DListNode newNode = new DListNode(key,value);map.put(key,newNode);//如果不存在,放入头部//放入头部DListNode temp = head.next;head.next = newNode;newNode.next = temp;temp.prev = newNode;newNode.prev = head;size++;//存入节点,个数+1;if(size > capacity){//如果个数大于容量,删除尾部节点DListNode removedNode = tail.prev;//删除节点removedNode.prev.next = removedNode.next;removedNode.next.prev = removedNode.prev;//哈希表中也删除map.remove(removedNode.key);size--;}}}
}
- 将重复的代码抽取成方法
//手动创建一双向链表
class DListNode{DListNode prev;DListNode next;int key,val;public DListNode(){}public DListNode(int key,int val){this.key = key;this.val = val;}
}
class LRUCache {//定义双向链表,创建头尾节点DListNode head;DListNode tail;//定义个数int size = 0;//定义容量int capacity;//定义一个hashHashMap<Integer,DListNode> map = new HashMap<>();public LRUCache(int capacity) {this.size = 0;this.capacity=capacity;head = new DListNode();tail = new DListNode();head.next = tail;tail.prev = head;}public int get(int key) {DListNode node = map.get(key);int res = -1;if(node!=null){res=node.val;//因为访问过当前node,根据LRU规则,需要将此节点放入到头部,先删除,在插入removeNode(node);insertNode(node);}return res;}public void put(int key, int val) {DListNode node = map.get(key);if(node!=null){//更新valuenode.val = val;//因为访问过当前node,根据LRU规则,需要将此节点放入到头部,先删除,在插入removeNode(node);insertNode(node);}else{//创建新节点DListNode newNode = new DListNode(key,val);//放入mapmap.put(key,newNode);//插入头部insertNode(newNode);size++;if(size>capacity){//删除末尾节点DListNode removeNode = tail.prev;removeNode(removeNode);//同时删除map中元素map.remove(removeNode.key);size--;}}}public void removeNode(DListNode node){node.prev.next = node.next;node.next.prev = node.prev;}public void insertNode(DListNode node){DListNode temp = head.next;head.next = node;node.next = temp;temp.prev = node;node.prev = head;}
}