目录
单链表反转:
public static Node reverse(Node head) { if(head == null) return null; //第一个节点 Node node = head.next; head.next = null; while(node != null) { Node temp = node.next; node.next = head.next; head.next = node; node = temp; } return head; }
判断回文:
判断链表是否是回文结构: 1、遍历链表将节点压栈,再遍历链表进行逐一比对 2、利用快慢指针。快指针走两步,慢指针走一步,快指针不能时慢指针来到中点位置,再压栈可省二分之一空间。 快指针条件为while(fast.next != null && fast.next.next != null) 让慢指针少走一步 3、不用额外空间:利用快慢指针使慢指针走到中间位置,反转后面的链表,从两头逐一比对,返回前反转回来
O(N)空间
//整个放入栈 第二遍遍历依次比对 public static boolean is1(Node head) { Stack<Node> stack = new Stack<>(); Node node = head; while(node != null) { stack.push(node); node = node.next; } node = head; while(node != null) { if(node.data != stack.pop().data) { return false; } node = node.next; } return true; }
使用一半空间
//利用快慢指针将后一半放入栈 比较一半 public static boolean is2(Node head) { Node p1 = head; Node p2 = head; while(p2 != null && p2.next != null) { p1 = p1.next; p2 = p2.next.next; } Stack<Node> stack = new Stack<>(); while(p1 != null) { stack.push(p1); p1 = p1.next; } while(!stack.isEmpty()) { if(head.data != stack.pop().data) { return false; } head = head.next; } return true; }
不使用额外空间
//利用快慢指针找到中点位置 将后半逆序再比对 比对完后复原 public static boolean is3(Node head) { if(head == null) { return true; } Node p1 = head; Node p2 = head; while(p2 != null && p2.next != null) { p1 = p1.next; p2 = p2.next.next; } Node next = p1.next; Node temp; p1.next = null; while(next != null) { temp = next.next; next.next = p1; p1 = next; next = temp; } Node record = p1; p2 = head; boolean b = true; while(p1 != null && p2 != null) { if(p1.data != p2.data) { b = false; break; } p1 = p1.next; p2 = p2.next; } p1 = record; next = p1.next; p1.next = null; while(next != null) { temp = next.next; next.next = p1; p1 = next; next = temp; } return b; }
将一个链表划分为小于等于大于区域
将链表划分为小于等于大于某个数的格式: 1、将链表存入节点数组,利用快排方式分区(不保证稳定性),最后遍历数组串起来 2、不使用额外空间:定义六个变量分别为小于等于大于的头尾,遍历链表分别加入各自区域,在最后返回前串起来
package com.wtp.链表; public class 将一个链表划分小于等于大于区域 { public static void main(String[] args) { } public static Node listPartition(Node head,int pivot) { Node sH = null;//小于区域头 Node sT = null;//小于区域尾 Node eH = null;//等于区域头 Node eT = null;//等于区域尾 Node mH = null;//大于区域头 Node mT = null;//大于区域尾 Node next = null; while(head != null) { next = head.next; head.next = null;//将当前节点摘下来 if(head.data < pivot) { if(sH == null) { sH = head; sT = head; }else { sT.next = head; sT = head; } }else if(head.data == pivot) { if(eH == null) { eH = head; eT = head; }else { eT.next = head; eT = head; } }else { if(mH == null) { mH = head; mT = head; }else { mT.next = head; mT = head; } } head = next; } if(sT != null) {//如果有小于区域 sT.next = eH; eT = eT == null ? sT : eT; } if(eT != null) {//如果小于区域和等于区域不是都没有 eT.next = mH; } return sH != null ? sH : (eT == null ? mH : eH); } }
复制链表
给定一个链表,链表节点有一个指向任意节点的指针(Node rand),写一个方法复制这个链表并返回头节点
用额外空间:准备一张哈希表(key-旧节点,value-新节点),先遍历一遍链表设置旧节点对应新节点,再遍历一遍链表通过旧链表节点取出新链表节点设置新节点的rand
不用额外空间:将链表加工成旧-新-旧-新...的形式,新节点的rand指向为旧节点rand指向的下一个节点,最后分离新老链表
package com.wtp.链表; import java.util.HashMap; import java.util.Map; public class 复制链表 { public static void main(String[] args) { } //不用额外空间 public static Node copy2(Node head) { if(head == null) { return null; } Node cur = head; Node next ; while(cur != null) { next = cur.next; Node node = new Node(cur.data); node.next = next; cur.next = node; cur = next; } cur = head; while(cur != null) { next = cur.next.next; cur.next.rand = cur.rand != null ? cur.rand.next : null; cur = next; } cur = head; head = head.next; while(cur != null) { next = cur.next; cur.next = next != null ? next.next : null; cur = next; } return head; } //用额外空间 public static Node copy1(Node head) { Map<Node,Node> map = new HashMap<>(); Node cur = head; while(cur != null) { Node node = new Node(cur.data); map.put(cur,node); cur = cur.next; } cur = head; while(cur != null) { Node node = map.get(cur); node.setRand(map.get(cur.getRand())); node.setNext(map.get(cur.getNext())); cur = cur.next; } return map.get(head); } }
返回两个链表相交的节点
两个链表可能有环,给定两个链表的头返回两个相交的节点
判断链表是否有环: 1、遍历链表时判断哈希表中是否有该节点,若有则该节点为环的起点 2、利用快慢指针,快指针停下时重新赋值为head,快慢指针各自走一步,相遇时该节点为环的起点
判断两条无环链表是否相交: 先判断最后节点是否相等,不相等不相交,先移动指向长链表的指针,到相同位置后两链表逐个比对节点,相等就返回
一个有环一个无环的链表不可能相交
package com.wtp.链表; public class 返回两个链表相交的节点 { public static void main(String[] args) { } public static Node process(Node head1,Node head2) { if(head1 == null || head2 == null) { return null; } Node loop1 = getLoopNode(head1); Node loop2 = getLoopNode(head2); //两条链表都无环时平行或相交 if(loop1 == null && loop2 == null) { return noLoop(head1,head2); }else if(loop1 != null && loop2 != null){//两条链表都有环 return bothLoop(head1,loop1, head2,loop2); } return null; } //两条链表都有环时 判断 public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2) { Node p1 = head1; Node p2 = head2; //情况1 两链表不相交 //情况2 两条链表的第一个相交的节点在入环节点外面 有相同的入环节点 if(loop1 == loop2) { int n = 0; while(p1 != loop1) { p1 = p1.next; n++; } while(p2 != loop2) { p2 = p2.next; n--; } p1 = n < 0 ? head2 : head1; p1 = p1 == head1 ? head2 : head1; n = Math.abs(n); while(n-- > 0) { p1 = p1.next; } while(p1 != p2) { p1 = p1.next; p2 = p2.next; } return p1; }else { //情况3 两条链表的第一个相交的节点在环上 (相交节点为head1或head2的入环节点) p1 = loop1.next; while(p1 != loop1) { //如果在一条链的环中遇到了另一条链的入环节点则相交 if(p1 == loop2) { return p1; } } //没有遇到入环节点 不相交 return null; } } //两条链表都无环时 判断 public static Node noLoop(Node head1,Node head2) { if(head1 == null || head2 == null) { return null; } int n = 0; Node p1 = head1; Node p2 = head2; while(p1.next != null) { n++; p1 = p1.next; } while(p2.next != null) { n--; p2 = p2.next; } //如果两条链表最后的节点不同则不相交 if(p1 != p2) { return null; } //p1为长链 p2为短链 p1 = n < 0 ? head2 : head1; p2 = p1 == head1 ? head2 : head1; n = Math.abs(n); while(n-- > 0) { p1 = p1.next; } while(p1 != p2) { p1 = p1.next; p2 = p2.next; } return p1; } //链表可能有环也可能无环 返回入环节点 public static Node getLoopNode(Node head) { if(head == null || head.next == null || head.next.next == null) { return null; } Node p1 = head.next; Node p2 = head.next.next; while(p1 != p2) { if(p2.next == null || p2.next.next == null) { return null; } p1 = p1.next; p2 = p2.next.next; } p2 = head; while(p1 != p2) { p1 = p1.next; p2 = p2.next; } return p1; } }