链表
反转
反转链表
两种方法 迭代 递归(递归方法要记熟)
public ListNode reverseList(ListNode head) {
ListNode pre = null,cur = head,next = null;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
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;
}
从尾到头打印链表
1.反转链表 2.使用栈
public int[] reversePrint(ListNode head) {
ListNode pre = null, cur = head, next = null;
int length = 0;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
length++;
}
int[] arr = new int[length];
for(int i = 0;i< arr.length;i++){
arr[i]=pre.val;
pre = pre.next;
}
return arr;
}
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack=new Stack<Integer>();
while(listNode!=null){//遍历入栈
stack.push(listNode.val);
listNode=listNode.next;
}
ArrayList<Integer> list=new ArrayList<Integer>();
while(!stack.isEmpty()){//判断是否空 出栈
list.add(stack.pop());
}
return list;
}
反转链表 II
【m,n】内反转链表
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode curm = dummy,curn = dummy;
for(int i = 0; i < m - 1 ;i++){
curm = curm.next;
}
for(int i = 0;i <= n;i++){
curn = curn.next;
}
ListNode a = curm.next;
curm.next = reverse(a,curn);
a.next = curn;
return dummy.next;
}
private ListNode reverse(ListNode a,ListNode b){
ListNode pre = null,cur = a,next = a;
while(cur != b){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
K 个一组翻转链表
反转【a,b) 双指针
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null) return null;
ListNode a, b;
a = b = head;
for (int i = 0; i < k; i++) {
if (b == null) return head;
b = b.next;
}
ListNode newHead = reverse(a, b);
a.next = reverseKGroup(b, k);
return newHead;
}
private ListNode reverse(ListNode a, ListNode b) {
ListNode pre, cur, nxt;
pre = null; cur = a; nxt = a;
while (cur != b) {
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
删除
删除链表的节点
创建一个dummy结点 可以避免头结点不方便处理
public ListNode deleteNode(ListNode head, int val) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode cur = dummy;
while(cur != null && cur.next != null){
if(cur.next.val == val){
cur.next = cur.next.next;
break;
}
cur = cur.next;
}
return dummy.next;
}
删除有序链表中重复出现的元素
dummy + 三指针维护
public ListNode deleteDuplicates (ListNode head) {
// write code here
if(head == null || head.next == null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy,cur = head,next = head;
int count = 0;
while(true){
if(next == null){
pre.next = (count == 1) ? cur : null;
break;
}
if(next.val == cur.val){
count ++;
}else{
if(count == 1){
pre.next = cur;
pre = pre.next;
cur = next;
}else{
cur = next;
count = 1;
}
}
next = next.next;
}
return dummy.next;
}
删除有序链表中重复的元素
双指针维护 重复元素保留一个
public ListNode deleteDuplicates(ListNode head) {
if(head == null) return null;
ListNode pre = head;
ListNode cur = head;
while(cur != null){//cur遍历一遍 pre跳着走
if(cur.val != pre.val){
pre.next = cur;
pre = cur;
}
cur = cur.next;
}
pre.next = null;
return head;
}
删除链表的倒数第n个节点
public ListNode removeNthFromEnd (ListNode head, int n) {
// write code here
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode fast = dummy,slow = dummy;
for(int i = 0;i < n;i++){
fast = fast.next;
}
while(fast.next != null){
fast = fast.next;
slow =slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
合并
合并两个排序的链表
1.使用递归 2.可使用迭代
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null || l2 == null) return (l1 == null) ? l2 : l1;
if(l1.val < l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
合并K个升序链表
合并两个有序链表 二分法 归并
public ListNode mergeKLists(ListNode[] lists) {
if(lists == null || lists.length == 0) return null;
return help(lists,0,lists.length-1);
}
private ListNode help(ListNode[] lists,int start,int end){
if(start == end) return lists[start];
int mid = start + (end - start)/2;
ListNode left = help(lists,start,mid);
ListNode right = help(lists,mid + 1,end);
return merge(left,right);
}
private ListNode merge(ListNode a,ListNode b){
if(a == null || b== null) return (a== null) ? b : a;
if(a.val < b.val) {
a.next = merge(a.next,b);
return a;
}else{
b.next = merge(a,b.next);
return b;
}
}
快慢指针
链表中倒数第k个节点
1.快慢指针 2.遍历
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head,slow = head;
for(int i = 0;i < k - 1;i++){
fast = fast.next;
}
while(fast.next != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
环形链表
快慢指针
public boolean hasCycle(ListNode head) {
if(head == null || head.next ==null) return false;
ListNode first = head,slow = head;
while(first != null && first.next != null){
first = first.next.next;
slow = slow.next;
if(first == slow) return true;
}
return false;
}
链表中环的入口结点
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null || pHead.next == null) return null;
ListNode fast = pHead,slow = pHead;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;
}
slow = pHead;//slow回到起点
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
链表的中间结点
快慢指针
public ListNode middleNode(ListNode head) {
if(head == null || head.next == null) return head;
ListNode fast = head,slow = head;
/*
while(fast.next != null && fast.next.next != null){停在左侧
slow = slow.next;
fast = fast.next.next;
}
*/
while(fast != null && fast.next != null){//当结点时偶数时 slow停在右侧
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
构建链表
二叉搜索树与双向链表
实质上就是一个中序遍历 和一个头结点构造双向链表
迭代
public Node treeToDoublyList(Node root) {
if(root == null) return null;
Stack<Node> stack = new Stack<>();
Node dummy = new Node(0),pre = dummy,cur = root;
while(!stack.isEmpty() || cur != null) {
if(cur != null) {
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
pre.right = cur;//
cur.left = pre;//
pre = pre.right;//
cur = cur.right;
}
}
pre.right = dummy.right;
dummy.right.left = pre;
return dummy.right;
}
递归
Node dummy = new Node(-1),pre = dummy;//
public Node treeToDoublyList(Node root) {
if(root == null) return null;
inOrder(root);
pre.right = dummy.right;//
dummy.right.left = pre;//
return dummy.right;
}
private void inOrder(Node node){
if(node == null) return;
inOrder(node.left);
pre.right = node;//
node.left = pre;//
pre = pre.right;//
inOrder(node.right);
}
特定深度节点链表
层序遍历 + 构建链表
public ListNode[] listOfDepth(TreeNode tree) {
if(tree == null) return new ListNode[]{};
List<ListNode> list = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(tree);
while(!queue.isEmpty()){
int len = queue.size();
ListNode dummy = new ListNode(-1),pre = dummy;
for(int i = 0;i < len;i++){
TreeNode node = queue.poll();
pre.next = new ListNode(node.val);
pre = pre.next;
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
list.add(dummy.next);
}
ListNode[] res = list.toArray(new ListNode[list.size()]);
return res;
}
链表求和
构建链表
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode curA = l1,curB = l2;
int carry = 0,sum = 0;
ListNode dummy = new ListNode(0),cur = dummy;//
while(curA != null || curB != null){
int x = (curA == null) ? 0 : curA.val;
int y = (curB == null) ? 0 : curB.val;
sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
cur.next= new ListNode(sum);//
cur = cur.next;//
if(curA != null) curA = curA.next;
if(curB != null) curB = curB.next;
}
if(carry == 1) cur.next = new ListNode(carry);
return dummy.next;
}
奇偶链表
构建奇偶链表 然后连接两链表
public ListNode oddEvenList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode curO = head,curE = head.next;//head是奇链表的头结点
ListNode evenStart = curE;//偶链表 保留头结点
while(curE != null && curE.next != null){//和快慢指针中的快指针一样 先走先判断
curO.next = curE.next;//造链表
curO = curO.next;//
curE.next = curE.next.next;//造链表
curE = curE.next;
}
curO.next = evenStart;//相连
return head;
}
分隔链表
1.双指针 2.创建两个链表 分类后相连
public ListNode partition(ListNode head, int x) {//典型的造链表
ListNode dummyA = new ListNode(0),curA = dummyA;//
ListNode dummyB = new ListNode(0),curB = dummyB;//
ListNode cur = head;
while(cur != null){
if(cur.val < x){
curA.next = new ListNode(cur.val);//
curA = curA.next;//
}else{
curB.next = new ListNode(cur.val);//
curB = curB.next;//
}
cur = cur.next;
}
curA.next = dummyB.next;
dummyB.next = null;
return dummyA.next;
}
综合
分隔链表
遍历分块
public ListNode[] splitListToParts(ListNode root, int k) {
ListNode cur = root;
int len = 0;
while(cur != null){
len++;
cur = cur.next;
}
cur = root;
int width = len / k;
int rem = len % k;
ListNode[] res = new ListNode[k];
for(int i = 0;i < k;i++){//将链表分为k分
res[i] = cur ;
for(int j = 1;j<width+(i < rem ? 1 : 0);j++){
cur = cur.next;
}
if(cur != null){
ListNode next = cur.next;//保存cur的下一个结点
cur.next = null;//断开
cur = next;//cur 指向 next
}
}
return res;
}
重排链表
找中点 反转后链表 合并两链表
public void reorderList(ListNode head) {
if(head == null) return ;
ListNode fast = head,slow = head;//快慢指针找中点
while(fast !=null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
fast = slow.next;
slow.next = null;//断开链表
ListNode headB = reverse(fast);//反转尾链表
ListNode headA = head;
ListNode newHead = merge(headA,headB);//合并两链表
}
private ListNode merge(ListNode A,ListNode B){//合并链表
if(A == null || B == null) return (A == null) ? B : A;
A.next = merge(B,A.next);
return A;
}
private ListNode reverse(ListNode head){
ListNode pre = null,cur = head,next = null;
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
旋转链表
形成环形链表 断开链表
public ListNode rotateRight(ListNode head, int k) {
if(head == null || k== 0) return head;
int len = 1;
ListNode cur = head;
while(cur.next != null){//cur指向尾结点
cur = cur.next;
len++;
}
cur.next = head;//形成环形链表
int step = len - (k % len);
for(int i = 0;i < step;i++){
cur = cur.next;
}
head = cur.next;
cur.next = null;//断开环形链表
return head;
}
排序链表
归并排序 (合并两个有序链表 + 中点)
public ListNode sortList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode midNode = getMidNode(head);
ListNode rightHead = midNode.next;
midNode.next = null;
ListNode left = sortList(head);
ListNode right = sortList(rightHead);
return merge(left,right);
}
//找中点
public ListNode getMidNode(ListNode head) {
if(head == null || head.next == null) return head;
ListNode fast = head.next;//这里需要先让fast先走1、2步 否则无法正常拆分链表
//例如1 2 的情况下 fast slow同时走 时slow指向2 导致 链表无法正常拆分
ListNode slow = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
//合并两个有序链表
private ListNode merge(ListNode a,ListNode b){
if(a == null || b == null) return (a==null)?b:a;
if(a.val < b.val){
a.next = merge(a.next,b);
return a;
}else{
b.next = merge(a,b.next);
return b;
}
}
回文链表
快慢指针 反转后链表 比较
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) return true;
ListNode fast = head,slow = head;//找中间结点
while(fast.next != null && fast.next.next != null){//这里必须这么写 偶数结点时需要指向中间的左侧
fast = fast.next.next;
slow = slow.next;
}
fast = slow.next ;
ListNode pre = null,cur = fast,next = null;//反转
while(cur != null){
next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
fast = pre;//首尾开始一一比较
slow = head;
while(fast != null && slow != null){
if(fast.val != slow.val) return false;
fast = fast.next;
slow = slow.next;
}
return true;
}
二进制链表转整数
public int getDecimalValue(ListNode head) {
if(head == null) return 0;
ListNode cur = head;
int num = 0;
while(cur != null){
num = num*2 + cur.val;
cur = cur.next;
}
return num;
}
两个链表的第一个公共节点
1.求两链表长度 使两者同步
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA,curB = headB;
int lenA = 0,lenB = 0;
while(curA != null){
lenA++;
curA = curA.next;
}
while(curB != null){
lenB++;
curB = curB.next;
}
curA = headA;
curB = headB;
if(lenA > lenB){
for(int i = 0;i < lenA - lenB;i++){
curA = curA.next;
}
}else{
for(int i = 0;i < lenB - lenA;i++){
curB = curB.next;
}
}
while(curA != null&&curA != curB){
curA = curA.next;
curB = curB.next;
}
return curA;
}
//时间负责度 O(M+N) 空间复杂度O(1)
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {//走别人的路
ListNode curA = headA,curB = headB;
while(curA != curB){
curA = (curA != null) ? curA.next : headB;//注意这里curB.next
curB = (curB != null) ? curB.next : headA;//向一个8字一样追赶
}
return curA;
}