链表
160.相交链表
思考:只要p1和p2不相等就一直在循环里,因为就算都为null也会走到相等
记录:需要二刷
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA,p2 = headB;
while(p1 != p2){
if(p1 == null){
p1 = headB;
}else{
p1 = p1.next;
}
if(p2 == null){
p2 = headA;
}else{
p2 = p2.next;
}
}
return p1;
}
}
206.反转链表
记录:需要二刷
思考:迭代写法,定义三个指针pre、cur、nxt,pre和cur负责将指针反转,然后依次向后移动,nxt负责进行下个要反转节点的移动
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode pre = null,cur = head,nxt = head.next;
while(cur != null){
cur.next = pre;
pre = cur;
cur = nxt;
if(nxt != null){
nxt = nxt.next;
}
}
return pre;
}
}
思考:递归写法,把头节点拎出来,直接把后面的链表全部反转,然后考虑第一个节点怎么变化。后面的反转指向第一个,第一个指向空,想象一下链表结构,不要跳进递归
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode last = reverseList(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
234.回文链表
思考:链表不能整体反转,因为会破坏原来的链表结构,只能用快慢指针先找到后面一半再反转
记录:需要二刷
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null){
return true;
}
ListNode fast = head,slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
ListNode newHead = reverse(slow);
ListNode p1 = head,p2 = newHead;
while(p2 != null){
if(p1.val != p2.val){
return false;
}
p1 = p1.next;
p2 = p2.next;
}
return true;
}
ListNode reverse(ListNode head){
if(head == null || head.next == null){
return head;
}
ListNode last = reverse(head.next);
head.next.next = head;
head.next = null;
return last;
}
}
141.环形链表
思考:只要有环一定相交,并且fast不会指向null,因为一直在环里绕
记录:不需要二刷
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode fast = head,slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
}
142.环形链表2
思考:先套判断是否为环形链表的模板,然后当两个指针相遇,将slow置为head,然后slow++,fast++,再次相遇就是环起点。可以画图看一下,这是个公式,记住
记录:需要二刷
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head,slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
slow = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
21.合并两个有序链表
思考:哪个小后面就接谁,最后跟上没遍历完的链表,秒
记录:不需要二刷
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1);
ListNode p = dummy,p1 = list1,p2 = list2;
while(p1 != null && p2 != null){
if(p1.val <= p2.val){
p.next = p1;
p1 = p1.next;
}else{
p.next = p2;
p2 = p2.next;
}
p = p.next;
}
if(p1 != null){
p.next = p1;
}
if(p2 != null){
p.next = p2;
}
return dummy.next;
}
}
2.两数相加
思考:用一个count记录进位情况,只要链表1和2有数值,并且进位有数值,每次的val就依次相加。并且每次循环更新count和val值
记录:不需要二刷
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode p = dummy,p1 = l1,p2 = l2;
int count = 0;
while(p1 != null || p2 != null || count != 0){
int val = count;
if(p1 != null){
val = val + p1.val;
p1 = p1.next;
}
if(p2 != null){
val = val + p2.val;
p2 = p2.next;
}
count = val / 10;
val = val % 10;
p.next = new ListNode(val);
p = p.next;
}
return dummy.next;
}
}
19.删除链表的倒数第n个节点
思考:用虚拟头节点,这样能防止p2.next.next越界
记录:不需要二刷
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode p1 = dummy,p2 = dummy;
while(n >= 0){
p1 = p1.next;
n--;
}
while(p1!= null){
p1 = p1.next;
p2 = p2.next;
}
p2.next = p2.next.next;
return dummy.next;
}
}
24.两两交换链表中的节点
思考:用递归,两两交换,就抽出我们交换的两个节点,其他节点用others,然后进行交换操作
记录:需要二刷
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode first = head,second = head.next,others = head.next.next;
first.next = swapPairs(others);
second.next = first;
return second;
}
}
25.K个一组翻转链表
思考:先反转以head开头的k个元素,再将k+1个元素作为head递归调用
记录:需要二刷
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null || head.next == null){
return head;
}
ListNode a = head,b = head;
for(int i = 0;i < k;i++){
//不足k个不需要翻转
if(b == null){
return head;
}
b = b.next;
}
ListNode newHead = reverse(a,b);
a.next = reverseKGroup(b,k);
return newHead;
}
ListNode reverse(ListNode a,ListNode b){
ListNode pre = null,cur = a,nxt = a;
while(cur != b){
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
138.随机链表的复制
思考:深拷贝问题就是一个哈希表+两次遍历
- 哈希表:存原始节点 到 拷贝出的节点
- 第一次遍历:为每个原始节点,创建一个映射的拷贝出的新节点
- 第二次遍历:按照原始节点的链条规则,组装新节点
记录:需要二刷
class Solution {
public Node copyRandomList(Node head) {
HashMap<Node,Node> hm = new HashMap<>();
for(Node p = head;p != null;p = p.next){
if(!hm.containsKey(p)){
hm.put(p,new Node(p.val));
}
}
for(Node p = head;p != null;p = p.next){
if(p.next != null){
hm.get(p).next = hm.get(p.next);
}
if(p.random != null){
hm.get(p).random = hm.get(p.random);
}
}
return hm.get(head);
}
}
148.排序链表
思考:解析太负责我懒得看了,直接把他存到数组,用数组排序,然后再取出来
记录:不需要二刷
class Solution {
public ListNode sortList(ListNode head) {
ListNode p = head;
int size = 0;
while(p != null){
p = p.next;
size++;
}
p = head;
int[] num = new int[size];
for(int i = 0;i < size;i++){
num[i] = p.val;
p = p.next;
}
p = head;
Arrays.sort(num);
for(int i : num){
p.val = i;
p = p.next;
}
return head;
}
}
23.合并K个升序链表
思考:用优先级队列,把lists按照顺序装进去,然后再弹出
记录:不需要二刷
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
for(int i = 0;i < lists.length;i++){
ListNode head = lists[i];
ListNode p = head;
while(p != null){
pq.offer(p.val);
p = p.next;
}
}
ListNode dummy = new ListNode(-1);
ListNode newP = dummy;
while(!pq.isEmpty()){
newP.next = new ListNode(pq.poll());
newP = newP.next;
}
return dummy.next;
}
}
146.LRU缓存
思考:记住用的数据结构:LinkedHashMap,要设计一个makeRecently类
记录:需要二刷
class LRUCache {
LinkedHashMap<Integer,Integer> cache;
int cap = 0;
public LRUCache(int capacity) {
cache = new LinkedHashMap<>();
cap = capacity;
}
public int get(int key) {
if(cache.containsKey(key)){
makeRecently(key);
return cache.get(key);
}else{
return -1;
}
}
public void put(int key, int value) {
if(cache.containsKey(key)){
cache.put(key,value);
makeRecently(key);
}else{
cache.put(key,value);
}
if(cache.size() > cap){
int maxKey = cache.keySet().iterator().next();
cache.remove(maxKey);
}
}
public void makeRecently(int key){
if(cache.containsKey(key)){
int val = cache.get(key);
cache.remove(key);
cache.put(key,val);
}
}
}