网站建设衡水/百度关键词查询网站
一、链表解题的方法论
1)对于笔试,不用太在乎空间复杂度,一切为了时间复杂度
2)对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法
二、链表常用数据结构和技巧
1)使用容器(哈希表、数组等)
2)快慢指针
三、快慢指针
1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点
2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点
3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个
4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个
代码演示:
package class09;import java.util.ArrayList;public class LinkedListMid {public static class Node{public int value;public Node next;public Node(int value){this.value = value;}}/**链表求各种中点问题:* 1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点** 2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点** 3)输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个** 4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个*///1)输入链表头节点,奇数长度返回中点,偶数长度返回上中点 比如[2->3->5->6] 返回3节点public static Node midOrUpMidNode(Node head){//特殊情况:如果节点个数小于3个if(head == null || head.next == null || head.next.next == null){return head;}//节点3个及以上Node slow = head.next;Node fast = head.next.next;while(fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next;}return slow;}//2)输入链表头节点,奇数长度返回中点,偶数长度返回下中点 比如[2->3->5->6] 返回5节点public static Node midOrDownMidNode(Node head){//如果头节点空 或者只有头节点 就返回头节点if(head == null || head.next ==null){return head;}//快慢指针都同步来到头节点的下节点。这样进行指针前移,当前到边界时 slow就能确保在符合题意的位置Node slow = head.next;Node fast = head.next;while(fast.next != null && fast.next.next != null){slow =slow.next;fast = fast.next.next;}return slow;}//3)输入链表头节点,奇数长度返回中点前一个, 偶数长度返回上中点前一个 比如[2->3->5] 返回2节点 比如[2->3->5->6] 返回2节点public static Node midOrUpMidPreNode(Node head){//假如空节点,只有头节点 只有两个节点 这些情况下,都是返回溢出的节点,统一返回nullif(head == null || head.next == null || head.next.next ==null){return null;}//有三个及以上节点时Node slow = head;Node fast = head.next.next;while(fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next;}return slow;}//4)输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个 比如[2->3->5] 返回2节点 比如[2->3->5->6] 返回3节点public static Node midOrDownMidPreNode(Node head){//当空 只有头节点 那么就是返回溢出的节点 直接返回Nullif(head == null || head.next == null){return null;}Node slow = head;Node fast = head.next;while(fast.next != null && fast.next.next != null){slow = slow.next;fast = fast.next.next;}return slow;}public static Node right1(Node head) {if (head == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 1) / 2);}public static Node right2(Node head) {if (head == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get(arr.size() / 2);}public static Node right3(Node head) {if (head == null || head.next == null || head.next.next == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 3) / 2);}public static Node right4(Node head) {if (head == null || head.next == null) {return null;}Node cur = head;ArrayList<Node> arr = new ArrayList<>();while (cur != null) {arr.add(cur);cur = cur.next;}return arr.get((arr.size() - 2) / 2);}public static void main(String[] args) {Node test = null;test = new Node(0);test.next = new Node(1);test.next.next = new Node(2);test.next.next.next = new Node(3);test.next.next.next.next = new Node(4);test.next.next.next.next.next = new Node(5);test.next.next.next.next.next.next = new Node(6);test.next.next.next.next.next.next.next = new Node(7);test.next.next.next.next.next.next.next.next = new Node(8);Node ans1 = null;Node ans2 = null;ans1 = midOrUpMidNode(test);ans2 = right1(test);System.out.println(ans1 != null ? ans1.value : "无");System.out.println(ans2 != null ? ans2.value : "无");ans1 = midOrDownMidNode(test);ans2 = right2(test);System.out.println(ans1 != null ? ans1.value : "无");System.out.println(ans2 != null ? ans2.value : "无");ans1 = midOrUpMidPreNode(test);ans2 = right3(test);System.out.println(ans1 != null ? ans1.value : "无");System.out.println(ans2 != null ? ans2.value : "无");ans1 = midOrDownMidPreNode(test);ans2 = right4(test);System.out.println(ans1 != null ? ans1.value : "无");System.out.println(ans2 != null ? ans2.value : "无");}
}
四、给定一个单链表的头节点head,请判断该链表是否为回文结构。
题意:比如 12321 12344321从左往右读 跟从右往左读是一样的
核心思路:
1)栈方法特别简单(笔试用)
2)改原链表顺序的方法就需要注意边界了(面试用)
代码演示:
package class09;import java.util.Stack;/*** 给定一个单链表的头节点head,请判断该链表是否为回文结构。* 比如 12321 12344321从左往右读 跟从右往左读是一样的*/
public class IsPalindromeList {public static class Node {public int value;public Node next;public Node(int v) {value = v;}}//额外空间O(N) 用入栈出栈, 出栈顺序即为反序public static boolean isPalindrome1(Node head) {if (head == null || head.next == null) return true;//定义一个栈存链表元素Stack<Node> stack = new Stack<>();Node cur = head;while (cur != null) {stack.push(cur);cur = cur.next;}cur = head;while (cur != null) {if (stack.pop().value != cur.value) {return false;}cur = cur.next;}return true;}//额外空间O(N/2) 用入栈出栈, 入栈后一半元素,然后与前一半比较 出栈顺序即为反序public static boolean isPalindrome2(Node head) {if (head == null || head.next == null) return true;//使用快慢指针,让慢指针落到中点,如果是偶数个 那么就落到下中点,12344321 即落到第二个4 ,1111 第三个1, 11,第二个1//如果时奇数个,那么就落在中点下个节点 1234321 即落到第二个3//这两种不管怎么落,就从当前指针往后的元素入栈,最后依次出栈与头节点比较。 只要是回文结构都是相等的。Node slow = head.next;Node fast = head;while (fast.next != null && fast.next.next != null) {slow = slow.next;fast = fast.next.next;}//后半段元素入栈Stack<Node> stack = new Stack<>();while (slow != null) {stack.push(slow);slow = slow.next;}//利用快指针内存重定向头节点fast = head;while (!stack.isEmpty()) {if (stack.pop().value != fast.value) {return false;}fast = fast.next;}return true;}//额外空间O(1) 反转原链表后半段,与前半段对比,最后再把后半段链表还原,即再反转public static boolean isPalindrome3(Node head) {if (head == null || head.next == null) return true;Node n1 = head;Node n2 = head;//1.快慢指针找到中点,如果是偶数 那么中点就落在了中点上节点 12344321 落在第一个4while (n2.next != null && n2.next.next != null) {n1 = n1.next;n2 = n2.next.next;}//2. 定义n2重定向在n1中点下节点 12344321 落在第二个4 1234321 落在第二个3n2 = n1.next;//3.将上中点的next指针指向null, 这样做是为了与后半链表循环比较时有跳出循环的条件。n1.next = null;Node n3 = null;//辅助变量保存下个节点while (n2 != null) {n3 = n2.next;n2.next = n1;n1 = n2;n2 = n3;}//4.跳出循环时n1则落在最后一个节点 1->2->3->4 <- 4<-3<-2<-1 最后的1,将n2从定向在head节点 开始头尾比较相等n2 = head;//5.注意要保存n1节点在n3 最后还需要将其顺序反转回原链表顺序n3 = n1;//6.遍历n1 n2 头尾节点 如果是回文结构那么肯定相等,否则返回false 因为此时第一个4.next=null。判断到中点则会跳出循环while (n1 != null && n2 != null) {if (n1.value != n2.value) return false;n1 = n1.next;n2 = n2.next;}//7.此时如果符合,那么最后就是将后面 44321反转 在利用前面指针变量进行反转//可以提前先将下个指向保存,然后最后一个元素n3指向为空,形成4 <- 4<-3<-2 1n1 = n3.next; //curn3.next = null; //prewhile(n1 != null){n2 = n1.next; //利用n2保存下一个元素n1.next = n3;n3 = n1;n1 = n2;}return true;}public static void printLinkedList(Node node) {System.out.print("Linked List: ");while (node != null) {System.out.print(node.value + " ");node = node.next;}System.out.println();}public static void main(String[] args) {Node head = null;printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);head.next.next.next = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(2);head.next.next.next = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");head = new Node(1);head.next = new Node(2);head.next.next = new Node(3);head.next.next.next = new Node(2);head.next.next.next.next = new Node(1);printLinkedList(head);System.out.print(isPalindrome1(head) + " | ");System.out.print(isPalindrome2(head) + " | ");System.out.println(isPalindrome3(head) + " | ");printLinkedList(head);System.out.println("=========================");}}
五、将单向链表按某值划分成左边小、中间相等、右边大的形式
核心思路:
1)把链表放入数组里,在数组上做partition(笔试用)
2)分成小、中、大三部分,再把各个部分之间串起来(面试用)
package class09;import class08.TrieTree;/*** 将单向链表按某值划分成左边小、中间相等、右边大的形式** 1)把链表放入数组里,在数组上做partition** 2)分成小、中、大三部分,再把各个部分之间串起来*/
public class SmallerEqualBigger {public static class Node{public int value;public Node next;public Node(int v) {value = v;}}//1)把链表放入数组里,在数组上做partition 额外空间复杂度O(N)//数组做好分区操作,就遍历节点 依次重新指向下节点,注意最后一个节点要指向NULLpublic static Node listPartition1(Node head, int pivot){if(head == null) return head;//定义辅助变量遍历链表 判断链表大小Node cur = head;int size = 0;while(cur != null){size++;cur = cur.next;}cur = head; //重新指向头节点//定义数组存放节点Node[]nodeArr = new Node[size];for (int i = 0;i<nodeArr.length;i++){nodeArr[i] = cur;cur = cur.next;}//进行partition分区操作,按pivot值做划分partition(nodeArr,pivot);//节点重新定于指向,按照排好序的数组for(int i =1;i<nodeArr.length;i++){nodeArr[i-1].next = nodeArr[i];}//最后的节点的next指向注意要指向nullnodeArr[nodeArr.length-1].next = null;return nodeArr[0];}public static void partition(Node[] nodeArr, int pivot) {int less = -1;//注意大于区边界,是从最右元素+1的边界开始,因为这里划分值不是最后一个元素,需要一起判断int big = nodeArr.length;int index = 0;//开始分区交换,直到下标索引遇到右侧的大于区左边界big就停止while(index < big){if(nodeArr[index].value < pivot){swap(nodeArr,++less,index++);}else if(nodeArr[index].value == pivot){index++;}else {swap(nodeArr,--big,index);}}}public static void swap(Node[] arr,int i,int j){Node temp = arr[i];arr[i] = arr[j];arr[j] = temp;}//2)分成小、中、大三部分,再把各个部分之间串起来 额外复杂度三个区间有限变量 O(1)根据题意分成三个区间,定义小于区[sH,ST]、等于区[eH,eT]、大于区[mH,mT],每个区间都有头尾//最好分别把对应的值填入三个区间链表,再将小于区尾 -> 等于区头 -> 大于区头 指向起来。public static Node listPartition2(Node head, int pivot){if(head == null) return null;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; //清空指向节点 赋值为null,后面后依次进行重新排序指向if(head.value < pivot){//当前值小于划分值,就进入小于区 假如小于区没有头节点 说明是第一个节点 直接赋值头尾if(sH==null){sH = head;sT = head;}else{ //如果存在区间有值,那么就接在后面,保持原链表的稳定性 顺序次序不变sT.next = head;sT = head;}//等于就放入等于区间链表}else if(head.value == 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(sH != null){sT.next = eH;//接下来就是要连接大于区间,但是需要判断是小于区连接还是等于区,有可能等于区为空,那就是小于区连接大于区//如果等于区头不为空,那么eT还是eT,保持不变进行连接大于区的头,假如为空,那么就需要赋值成小于区的尾部,进行连接大于区的头eT = eT != null? eT : sT; //谁连接大于区头 谁就是eT}// 下一步,一定是需要用eT 去接 大于区域的头// 有等于区域,eT -> 等于区域的尾结点// 无等于区域,eT -> 小于区域的尾结点// eT 尽量不为空的尾巴节点if(eT != null){eT.next = mH; //非空则将eT指向大于区头}//连接完成,但是返回要判断头是否为空//如果小于区头非空 那么就返回sH,如果空,//就判断等于区头是否空,如果非空,那么就返回eH,如果空,//就返回大于区头mHreturn sH != null? sH:(eH!=null)?eH:mH;}public static void printLinkedList(Node node) {System.out.print("Linked List: ");while (node != null) {System.out.print(node.value + " ");node = node.next;}System.out.println();}public static void main(String[] args) {Node head1 = new Node(7);head1.next = new Node(9);head1.next.next = new Node(1);head1.next.next.next = new Node(8);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(2);head1.next.next.next.next.next.next = new Node(5);printLinkedList(head1);// head1 = listPartition1(head1, 4);head1 = listPartition2(head1, 5);printLinkedList(head1);Node head2 = new Node(7);head2.next = new Node(9);head2.next.next = new Node(1);head2.next.next.next = new Node(8);head2.next.next.next.next = new Node(5);head2.next.next.next.next.next = new Node(2);head2.next.next.next.next.next.next = new Node(5);printLinkedList(head2);head2 = listPartition1(head2, 5);printLinkedList(head2);}
}
六、138. 复制带随机指针的链表
138. 复制带随机指针的链表



核心思路:
1.通过哈希表保存节点,老节点为key,新节点为value 然后依次进行赋值next和random指针
这里特殊点在于random是随机指向,第一个可以指向最后一个,所以需要先获取出全部元素 再进行赋值
2.不使用哈希表的方式,减少空间复杂度,可以将复制的节点,依次放到对应节点的nex指针下 比如:
1->2->3->4 => 1-> copy1 -> 2 -> copy2 -> 3 -> copy3 -> 4 -> copy4,然后调整random指针,最后就将新老节点next指向分离出来 恢复原链表 返回复制链表头节点
package class09;import java.util.HashMap;/*** 一种特殊的单链表节点类描述如下* class Node {* int value;* Node next;* Node rand;* Node(int val) { value = val; }* }* rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null。* 给定一个由Node节点类型组成的无环单链表的头节点 head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点。* 【要求】* 时间复杂度O(N),额外空间复杂度O(1)*/
public class CopyListWithRandom {public static class Node{public int val;public Node next;public Node random;public Node(int v){val = v;next = null;random = null;}}//通过哈希表保存节点,老节点为key,新节点为value 然后依次进行赋值next和random指针//这里特殊点在于random是随机指向,第一个可以指向最后一个,所以需要先获取出全部元素 再进行赋值public static Node copyRandomList1(Node head){if(head == null)return null;//定义哈希表,保存节点,key对应的就是一个复制的节点HashMap<Node,Node> map = new HashMap<>();Node cur = head;while (cur != null){//每次进行遍历赋值新生成一个对应节点map.put(cur,new Node(cur.val));cur = cur.next;}//节点生成好之后就是赋值next random指针。//map.get(node).next 新节点 next=》 map.get(node.next) node.next表示老节点的next。 通过map获取得到新节点nextcur = head;while(cur != null){//新节点cur map.get(cur)的next指向 就是对应老节点cur的next指向,cur.next。 其指向对应的新节点就是map.get(cur.next)map.get(cur).next = map.get(cur.next);map.get(cur).random = map.get(cur.random);cur = cur.next;}return map.get(head);}//不使用哈希表的方式,减少空间复杂度,可以将复制的节点,依次放到对应节点的nex指针下 比如://1->2->3->4 => 1-> copy1 -> 2 -> copy2 -> 3 -> copy3 -> 4 -> copy4public static Node copyRandomList2(Node head){if(head == null) return null;Node cur = head;Node next = null;//将复制节点,都依次放到对应老节点的next 1-> copy1 -> 2 -> copy2 -> 3 -> copy3 -> 4 -> copy4while(cur != null){// 1-->2 把copy1放到两者中间 1 -> copy1 >2next = cur.next;cur.next = new Node(cur.val);cur.next.next = next;cur = next;}cur = head;//先将节点的random指针调整好while (cur != null){//1-> copy1 ->2 注意保存原节点的next 2next = cur.next.next;//cur.next.random新节点的random指向 就是cur.random老节点的指向一致,对应的是再其next,cur.random.nextcur.next.random = cur.random != null?cur.random.next:null;cur = next;}//最后分离出新老节点next指向cur = head;//新节点的头节点Node ans = head.next;//新节点遍历指针Node newNode = null;while(cur != null){//新节点的nextnext = cur.next.next;//当前对应的新节点newNode = cur.next;//分离新节点 旧节点next指向之前自己的nextcur.next = next;//新节点next 分离 指向自己的next 也就是对应老节点的nextnewNode.next = next!=null?next.next:null;cur = next;}return ans;}
}
七、给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null
【要求】
* 如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。
核心思路:
空间复杂度如果不限制,那么可以将两链表保存在数组,然后依次从头遍历如果有相等的就表示首个相交点
* 1.分类进行判断 两个链表都没有环,那么相交节点往后的节点肯定都是一样的。不可能后面还交叉,节点只有一个next
* 就可以判断如果相交了 那么最后两链表节点是肯定相等的 。确定相交后,再判断两个链表长度,较长的链表减去两链表长度差值
* 后开始与较短的链表同时遍历,一旦相等,就是相交第一个节点
* 2.两个链表一个有环,一个没环,那么肯定是没有相交的节点的 没有这种符合链表结构,因为一定会出现有节点存在两个next指针
* 3.两个链表有环,那相交节点 会存在两种形式。
* 两个链表环节点1即第一个相交点(这里也有可能相交节点也是入环首节点,判断逻辑都一样 ), 另一种就是 1 2 都可以认为是第一相交节点
* 第一种如两链表长度不同,那么就较长的链表减去两链表长度差值,后开始与较短的链表同时遍历,一旦相等,就是相交第一个节点
* 第二种就是以一个链表的入环节点1.next节点开始遍历,直到走回入环节点,过程遇到另外一个链表的入环节点2则表示相交。返回1或2都认为是相交首节点
* * * *
* * * *
1 1 * 2
* * *
* * * * *
* * *
代码演示:
package class10;/*** 给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null* 【要求】* 如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度 请达到O(1)。* <p>* 思路:(空间复杂度如果不限制,那么可以将两链表保存在数组,然后依次从头遍历如果有相等的就表示首个相交点)* 1.分类进行判断 两个链表都没有环,那么相交节点往后的节点肯定都是一样的。不可能后面还交叉,节点只有一个next* 就可以判断如果相交了 那么最后两链表节点是肯定相等的 。确定相交后,再判断两个链表长度,较长的链表减去两链表长度差值* 后开始与较短的链表同时遍历,一旦相等,就是相交第一个节点* 2.两个链表一个有环,一个没环,那么肯定是没有相交的节点的 没有这种符合链表结构,因为一定会出现有节点存在两个next指针* 3.两个链表有环,那相交节点 会存在两种形式。* 两个链表环节点1即第一个相交点(这里也有可能相交节点也是入环首节点,判断逻辑都一样 ), 另一种就是 1 2 都可以认为是第一相交节点* 第一种如两链表长度不同,那么就较长的链表减去两链表长度差值,后开始与较短的链表同时遍历,一旦相等,就是相交第一个节点* 第二种就是以一个链表的入环节点1.next节点开始遍历,直到走回入环节点,过程遇到另外一个链表的入环节点2则表示相交。返回1或2都认为是相交首节点* * * * ** * * * ** 1 1 * 2* * * ** * * * * ** * * **/
public class FindFirstIntersectNode {public static class Node {public int value;public Node next;public Node(int v) {value = v;}}//判断两链表是否相交,相交则返回第一个节点 不相交就返回nullpublic static Node getIntersectNode(Node head1, Node head2) {if (head1 == null || head2 == null) return null;//获取两链表的入环首节点 无环则返回nullNode loop1 = getLoopNode(head1);Node loop2 = getLoopNode(head2);//判断 两链表没有环 两链表都有环的两种情况if (loop1 == null && loop2 == null) {//没环的两个链表头节点入参进行获取相交首节点return noLoop(head1, head2);}if (loop1 != null && loop2 != null) {//有环的两个链表,需要将头节点 入环首节点都入参return bothLoop(head1, loop1, head2, loop2);}//如果是一个链表有环,一个链表无环,那么肯定没有相交节点 返回nullreturn null;}//获取两个有环链表相交首节点,没有相交则返回nullpublic static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {if(head1 == null || head2 == null) return null;//1.链表有环,那有两种情况//一个是两链表的入环首节点相等,那么将两链表等长位置开始往环首节点遍历过程如果有相等节点就表示相交首节点//另一个是入环首节点不相等,两个环节点都是相交首节点 返回哪个都可以if(loop1 == loop2){//如果入环首节点相交Node cur1 = head1;Node cur2 = head2;int n = 0;//定义较长链表与较短链表,这里注意 只需要遍历到loop 环节点,因为环内都是相同节点不需遍历while(cur1 != loop1){cur1 = cur1.next;n++;}while(cur2 != loop2){cur2 = cur2.next;n--;}cur1 = n > 0? head1:head2;cur2 = cur1 ==head1?head2:head1;//确定好长短链表 就将长链表cur1先走n步 再两链表同时走,相遇即为相交首节点 n要取绝对值n = Math.abs(n);while(n!=0){//递减差值直到0 较长链表cur1节点就来到和cur2同长节点n--;cur1 = cur1.next;}//遍历一定会存在一个相等节点 为相交首节点while(cur1 != cur2){cur1 = cur1.next;cur2 = cur2.next;}//相遇则返回其中一个节点即可return cur1;}else {//两链表入环首节点不相交 链表结构就是两个入环节点都是相交首节点//判断是否相交,就先遍历其中一个链表入环节点.next,绕一圈退出,假如有节点是与另外一个链表的入环节点一致 就表示相交 返回任意两链表一个入环节点Node cur = loop1.next;while(cur != loop1){//假如节点中有与head2链表的入环节点loop2相等 那么就表示有相交,返回相交节点就是两链表任意一个入环节点if(cur == loop2){return loop1;}cur = cur.next;}}//如果没相交 返回nullreturn null;}//获取两个无环链表相交首节点,没有相交则返回nullpublic static Node noLoop(Node head1, Node head2) {if (head1 == null || head2 == null) return null;Node cur1 = head1;Node cur2 = head2;int n = 0; //定义变量保存链表长度//1.先遍历head1while (cur1 != null) {n++;cur1 = cur1.next;}//2.再遍历head2 同时将head1的长度n 递减 看看最后n值 如果是大于0 那么表示head1链表较长while (cur2 != null) {n--;cur2 = cur2.next;}//3.两链表来到了最后节点,此时判断 如果两链表有相交节点,那么两链表的尾节点一定相等的,因为不可能相交后又交叉出去,链表节点只有一个nextif (cur1 != cur2) return null;//4.走到这里表示链表有相交,重定义cur1为较长的链表 cur2是较短的链表 假如n=0,两遍链表相等 等长不用交换cur1 = n > 0 ? head1 : head2;cur2 = cur1 == head1 ? head2 : head1;//4.将较长链表cur1先减去 差值n 需要取绝对值,假如n=0,那么就不用减,两链表直接从头节点开始遍历肯定会有相等的首节点n = Math.abs(n);while (n != 0) {cur1 = cur1.next;n--;}//5.执行完差值后 两链表cur1 cur2就开始往后等长,同时遍历 相遇时则是相交首节点while (cur1 != cur2) {cur1 = cur1.next;cur2 = cur2.next;}//6.最后返回相交首节点return cur1;}//获取链表入环第一个节点 如果没环返回nullpublic static Node getLoopNode(Node head) {if (head == null || head.next == null || head.next.next == null) return null;Node slow = head;Node fast = head;//先把快慢指针调整一次,下面的while条件才能执行起来 否则一开始都是头节点会存在相同情况slow = slow.next;fast = fast.next.next;//1.快慢指针遍历直到相遇while (slow != fast) {//如果快指针指向空 则表示链表没有环 那么就需要返回nullif (fast.next == null || fast.next.next == null) return null;slow = slow.next;fast = fast.next.next;}//2.快指针或慢指针其一重新指向头节点 然后再同步长进行指向,一旦快慢指针相等就表示来到入环的第一个节点fast = head;while (slow != fast) {slow = slow.next;fast = fast.next;}return slow;}public static void main(String[] args) {// 1->2->3->4->5->6->7->nullNode head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);// 0->9->8->6->7->nullNode head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getIntersectNode(head1, head2).value);// 1->2->3->4->5->6->7->4...head1 = new Node(1);head1.next = new Node(2);head1.next.next = new Node(3);head1.next.next.next = new Node(4);head1.next.next.next.next = new Node(5);head1.next.next.next.next.next = new Node(6);head1.next.next.next.next.next.next = new Node(7);head1.next.next.next.next.next.next = head1.next.next.next; // 7->4// 0->9->8->2...head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next; // 8->2System.out.println(getIntersectNode(head1, head2).value);// 0->9->8->6->4->5->6..head2 = new Node(0);head2.next = new Node(9);head2.next.next = new Node(8);head2.next.next.next = head1.next.next.next.next.next; // 8->6System.out.println(getIntersectNode(head1, head2).value);}
}
八、不给单链表的头节点,只给想要删除的节点,就能做到在链表上把这个点删掉
比如 1 -> 2 -> 3 -> 4 -> 5 ,链表 要删除指定节点,3, 但是不给链表头节点:
思路:
拿到的是指定的节点3位置, 可以将节点3.val = 节点3.next ,也就是3改成4数值。变成:
1 -> 2 -> 4 -> 4 -> 5
然后将节点3.next删除, 节点3.next = 节点3.next.next,变成:
1 -> 2 -> 4 ->5
相关文章:

【算法数据结构体系篇class09】:链表问题:快慢指针、回文结构、复制、中点,分区、相交
一、链表解题的方法论 1)对于笔试,不用太在乎空间复杂度,一切为了时间复杂度2)对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法二、链表常用数据结构和技巧1)使用容器(哈希表、数组等)2)快…...

实验室信息化管理行业方案
为适应新时代下的管理机制与应用场景,越来越多的检测实验室需对研发部门和实验部门进行全面的、现代化的、电子化的综合管理,帮助检测机构对实验室的规划与计划、项目立项与管理、项目成果、合同,以及基建等工作进行统一的管理,而…...

docker学习
docker 环境搭建 MySql # mysql5.7 docker run --name mysql10 -p 3306:3306 -v D:\MySql\conf:/etc/mysql/conf.d -v D:\MySql\data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD123456 -d mysql:5.7docker run --name mysql10 -p 3306:3306 -v /etc/mysql/conf.d:/etc/mysql/co…...

Linux 常用命令
重启 # 重启(root 用户操作) reboot# 强制重启 reboot -f关机 # 关机 # shutdown [OPTION] [TIME] [MESSAGE] shutdown-h 关机 -r 重启-c 取消上一个命令 第二个参数指的是多少分钟后执行操作,以分钟为单位,如果不加时间&am…...

数据结构-顺序表(2)
目录 1. 线性表 2. 顺序表 2.1 动态顺序表 3. 接口实现 前期工作 3.1 初始化、销毁与检查容量 3.1.1 初始化 3.1.2 销毁 3.1.3 检查容量 3.2 尾插 3.3 尾删 3.4 头插 3.5 头删 3.6 插入 3.7 删除 顺序表源码 SeqList.h SeqList.c test.c 写在最后ÿ…...

初学C/C++内存管理--new和delete的使用
一,内存分布 栈区: 一般的局部变量和函数的返回数据以及返回地址,函数的参数都在战栈区上开辟空间。栈区开空间一般由编译器自动管理,出了生命周期自动释放。也可以通过一些方式自己手动开辟栈区空间,不过一般用不到…...

【Java】volatile
一、volatile volatile是Java虚拟机提供的轻量级的同步机制,它有3个特性: 1)保证可见性 2)不保证原子性 3)禁止指令重排 当写一个volatile变量时,JMM会把该…...

混沌工程 Chaos Mesh 实践经验(持续更新)
使用 k8s JVM故障 Linux内核版本 Linux 系统内核必须为 4.1 及以上版本。 不然会一直失败,可以从Chaos Mesh dashboard前端看到。 对native方法注入故障无效 实测对Thread.sleep(Long) 注入故障无效,猜测是因为对native方法无效,大概因为…...

追梦之旅【数据结构篇】——详解C语言实现链栈
详解C语言实现链栈~😎前言🙌整体实现内容分析💞1.头文件编码实现🙌2.功能文件编码实现🙌3.测试函数功能代码🙌总结撒花💞😎博客昵称:博客小梦 😊最喜欢的座右…...

oracle数据库常用操作
1.连接登录切换用户su - oracle以管理员模式登录到sqlplus:sqlplus / as sysdba oracle登录身份有三种:1.1Normal 普通身份;1.2.sysdba 系统管理员身份;若以 ‘sysdba’ 方式认证,登录用户为 ‘SYS’,为 Or…...

一文教会你如何在Linux系统中使用Docker安装Redis 、以及如何使用可视化工具连接【详细过程+图解】
文章目录1、安装redis2、在外部创建配置文件3、创建redis4、启动测试redis5、数据持久化存储6、使用可视化工具连接redis前言在windows上安装过reids、在linux上也安装过redis,但是都没有docker上安装redis方便。这里给出docer安装redis的相关教程1、安装redis 默认…...

mysql 内存架构
1. 背景 从 innodb 的整体架构中可以知道 innodb 的内存架构中分为 buffer pool 缓存区, change pool 修改缓冲区, adaptive hash index 自适应哈希索引, 和 log buffer 日志缓冲区. 2. buffer pool buffer pool 是用于缓冲磁盘页的数据,mysql 的80%的内存会分配给…...

Helm安装Harbor
一、介绍 1.1 Harbor Harbor 是由 VMware 公司为企业用户设计的 Registry Server 开源项目,包括了权限管理 (RBAC)、LDAP、审计、管理界面、自我注册、HA 等企业必需的功能,同时针对中国用户的特点,设计镜像复制和中文支持等功能。目前该项…...

梯度下降优化器:SGD -> SGDM -> NAG ->AdaGrad -> AdaDelta -> Adam -> Nadam -> AdamW
目录 1 前言 2 梯度概念 3 一般梯度下降法 4 BGD 5 SGD 6 MBGD 7 Momentum 8 SGDM(SGD with momentum) 9 NAG(Nesterov Accelerated Gradient) 10 AdaGrad 11 RMSProp 12 Adadelta 13 Adam 13 Nadam 14 AdamW 15 Lion(EvoLve…...

Ubuntu下gcc多版本管理
Ubuntu下多gcc版本的管理 开发过程中,在编译一个开源项目时,由于代码使用的c版本过高,而系统内置的gcc版本过低时,这个时候我们就需要升级gcc版本,但是为了避免兼容性问题,安装多个版本的gcc,然…...

吃透8图1模板,人人可以做架构
前言 在40岁老架构师 尼恩的读者交流群(50)中,很多小伙伴问尼恩: 大佬,我们写架构方案, 需要从哪些方面展开 大佬,我们写总体设计方案需要一些技术亮点,可否发一些给我参考下 诸如此类,问法很多…...

骨传导耳机推荐哪款好,列举几款是市面上热销的骨传导耳机
骨传导耳机是一种新型的耳机类型,通过震动和声音将振动传到了耳道外,对耳道不会产生损伤,能够保护听力。相比于传统耳机的优势有很多,比如运动时佩戴更加稳固,也可以在听歌时与人交谈。但在市面上的骨传导耳机款式可…...

CFS三层内网渗透
目录 环境搭建 拿ubuntu主机 信息收集 thinkphp漏洞利用 上线msf 添加路由建立socks代理 bagecms漏洞利用 拿下centos主机 msf上线centos 添加路由,建立socks代理 拿下win7主机 环境搭建 设置三块虚拟网卡 开启虚拟机验证,确保所处网段正确&a…...

SQL server设置用户只能访问特定数据库、访问特定表或视图
在实际业务场景我们可能需要开放单独用户给第三方使用,并且不想让第三方看到与业务不相关的表或视图,我们需要在数据库中设置一切权限来实现此功能: 1.设置用户只能查看数据库中特定的视图或表 1.创建用户名 选择默认数据库 服务器角色默认…...

linux:http服务器搭建及实验案例
目录准备工作http服务器各个配置文件大概说明实验1:访问不同ip获得不同网页实验2:同一ip访问不同端口获得不同网页准备工作 1,安装http服务 2,将 /etc/selinux/config 文件下面的 SELINUX值改为 disabled 或者 permissive 。 3&a…...

【无标题】智能工业安全用电监测与智慧能源解决方案
工业互联网已成为全球制造业发展的新趋势。在新基建的推动下,5G、人工智能、云计算等技术与传统工业深度融合,为实现智能制造提供了技术支撑,将有力促进制造强国早日实现。 十四五规划在新基建的基础上进一步加快了制造业转型升级的步伐&…...

前端白屏的检测方案,让你知道自己的页面白了
前言 页面白屏,绝对是让前端开发者最为胆寒的事情,特别是随着 SPA 项目的盛行,前端白屏的情况变得更为复杂且棘手起来( 这里的白屏是指页面一直处于白屏状态 ) 要是能检测到页面白屏就太棒了,开发者谁都不…...

编译原理【文法设计】—每个a后面至少一个b、ab个数相等,ab个数不相等的所有串
编译原理【文法设计】—设计每个a后面至少一个b、ab个数相等,ab个数不相等的文法为字母表Σ{a,b}Σ\{a,b\}Σ{a,b}上的下列每个语言设计一个文法 (a) 每个a后面至少有一个b的所有串 首先,每个a后面至少有一个b的正规式怎么写呢?每个a都需要…...

【死磕数据库专栏启动】在CentOS7中安装 MySQL5.7版本实战
文章目录前言实验环境一. 安装MySQL1.1 配置yum源1.2 安装之前的环境检查1.3 下载MySQL的包1.4 开始使用yum安装1.5 启动并测试二. 设置新密码并重新启动2.1 设置新密码2.2 重新登录测试总结前言 学习MySQL是一件比较枯燥的事情,学习开始之前要先安装MySQL数据库&a…...

23.2.23 22湖北省赛 B
好久没打卡了, 随便找的个水题写 这题是简单难度的 ab1 所以可以找到固定规律, 通过手动模拟可以发现 假设两种水叫做a水和b水 先倒入a水 1:0 倒入b水 1:1 此时水杯为 倒出一半的混合物, 因为ab水互溶, 比例不变 再加入a水或者b水将容器填满 比例现在变为 3:1 混合之后再…...

ONLYOFFICE中的chatGPT 是如何编写毕业论文以及翻译多种语言的
前言 chatGPT这款软件曾被多个国家的大学禁用,我们也多次在网上看到chatGPT帮助应届毕业生编写毕业答辩论文,但是这款软件目前还没有在国内正式上线,ONLYOFFICE7.3版本更新后呢,就添加了chatGPT该功能,并且正常使用。 …...

QT入门Containers之QStackedWidget
目录 一、QStackedWidget界面相关 1、布局介绍 2、插入界面 3、插入类界面 二、Demo展示 此文为作者原创,创作不易,转载请标明出处! 一、QStackedWidget界面相关 1、布局介绍 QStackedWidget这个控件在界面布局时,使用还…...

Java学习-IO流-字节缓冲流
Java学习-IO流-字节缓冲流 IO流体系↙ ↘字节流 字符流↙ ↘ ↙ ↘InputStream OutputStream Reader Writer↓ ↓ ↓ ↓ FileInputStream FileOutputStream FileRe…...

C++这么难,为什么我们还要学习C++?
前言 C 可算是一种声名在外的编程语言了。这个名声有好有坏,从好的方面讲,C 性能非常好,哪个编程语言性能好的话,总忍不住要跟 C 来单挑一下;从坏的方面讲,它是臭名昭著的复杂、难学、难用。当然ÿ…...

C#底层库--业务单据号生成器(定义规则、自动编号、流水号)
系列文章 C#底层库–MySQL数据库访问操作辅助类(推荐阅读) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/126886379 C#底层库–JSON帮助类_详细(序列化、反序列化、list、datatable) 本文链接&…...