赞
踩
//输入链表头结点,奇数长度返回中点,偶数长度返回上中点 public static <T> Node<T> midOrUpMidNode(Node<T> head) { //如果当前结点为空 或者 结点的下一个结点为空 或者结点的下一个结点的下一个结点为空 if(head == null || head.next == null || head.next.next == null) { return head; } //此时链表至少3个结点 Node<T> slow = head.next; Node<T> fast = head.next.next; /* 如果快指针后面还有两个结点 慢指针一次走一步 快指针一次走两步 */ while(fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }
//输入链表头结点,奇数长度返回中点,偶数长度返回下中点 public static <T> Node<T> midOrDownMidNode(Node<T> head) { // 如果当前结点为空 或 结点的下一个结点为空 if(head == null || head.next == null) { return head; } //此时链表至少2个结点 Node<T> slow = head.next; Node<T> fast = head.next; /* 如果快指针后面还有两个结点 慢指针一次走一步 快指针一次走两步 */ while(fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }
//输入链表头节点,奇数长度返回中点前一个,偶数长度返回上中点前一个 public static <T> Node<T> midOrUpMidPreNode(Node<T> head) { //如果当前结点为空 或者 结点的下一个结点为空 或者结点的下一个结点的下一个结点为空 if(head == null || head.next == null || head.next.next == null) { return null; } //此时链表至少3个结点 Node<T> slow = head; Node<T> fast = head.next.next; /* 如果快指针后面还有两个结点 慢指针一次走一步 快指针一次走两步 */ while(fast.next != null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }
//输入链表头节点,奇数长度返回中点前一个,偶数长度返回下中点前一个 public static <T> Node<T> midOrDownMidPreNode(Node<T> head){ // 如果当前结点为空 或 结点的下一个结点为空 if(head == null || head.next == null) { return null; } //如果结点的下一个结点的下一个结点为空 if(head.next.next == null) { return head; } //此时链表至少3个结点 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 Node<T> reverseLinkList(Node<T> head){
Node<T> pre = null;
Node<T> next = null;
while(head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
public Node<T> reverseDoubleLinked(Node<T> head){
head = head.next; //将head指向第一个结点
Node<T> pre = null;
Node<T> next = null;
while(head != null) {
next = head.next; //next存放当前结点的下一个
head.next = pre; //当前结点的下一个指向前面
head.previous = next; //当前结点的前一个指向后面
pre = head; //当前结点变成前结点
head = next; //结点后移:从头到尾 next充当临时变量
}
return pre;
}
//从单链表中删除指定元素 public Node<T> deleteTargetNode(Node<T> head,T target){ //首先 先找出第一个不是目标的结点作为head while(head.getData() != null) { if(head.getData() == target) { head = head.next; }else { break; } } Node<T> pre = head; Node<T> cur = head; //此时head 肯定不是target while(cur != null) { if(cur.getData() == target) { pre.next = cur.next;//跳过 }else { pre = cur; } cur = cur.next; //最后再将cur后移 } return head; }
//从双向链表中删除指定元素 public Node<T> deleteTargetNode(Node<T> head,T target){ //找到第一个不是target的元素作为head while(head!=null) { if(head.getData()!=target) { break; } head = head.next; } head.previous = null; Node<T> pre = head; Node<T> cur = head; while(cur!=null) { if(cur.getData().equals(target)) { pre.next = cur.next; //判断是否为最后一个元素 if(cur.next!=null) { cur.next.previous = pre; } }else { pre = cur; } cur = cur.next; } return head; }
例子: 1 → 2 → 1 返回true 1 → 2 → 2 → 1 返回true 1 → 2 → 3 返回false
如果链表长度为N,时间复杂度为O(N),额外空间复杂度O(1)
//利用栈 空间O(N) public static <T> boolean checkByStack(Node<T> head) { //判断链表是否为空 或 长度为1 if(head == null || head.next == null) { return true; } Stack<Node<T>> stack = new Stack<>(); Node<T> p = head; Node<T> q = head; while(p!=null) { stack.add(p); p = p.next; } while(!stack.isEmpty()) { if(!stack.pop().getData().equals(q.getData())) { return false; } q = q.next; } return true; }
//利用栈 空间O(N/2) public static <T> boolean checkByStackPlus(Node<T> head) { //判断链表是否为空 或 长度为1 if(head == null || head.next == null) { return true; } //快慢指针优化 只存一半的内容进栈 Node<T> fast = head; // 一次走两步 Node<T> slow = head; // 一次走一步 Node<T> q = head; while(fast.next!=null && fast.next.next != null) { slow = slow.next; fast = fast.next.next; } Stack<Node<T>> stack = new Stack<>(); while(slow.next != null) { stack.add(slow.next); slow = slow.next; } while(!stack.isEmpty()) { if(!stack.pop().getData().equals(q.getData())) { return false; } q = q.next; } return true; }
/*利用有限几个变量 空间O(1)*/ public static <T> boolean checkByVar(Node<T> head) { //判断链表是否为空 或 长度为1 if(head == null || head.next == null) { return true; } //快慢指针优化 Node<T> fast = head; // 一次走两步 Node<T> slow = head; // 一次走一步 while(fast.next != null && fast.next.next != null) {//找中点 slow = slow.next; // mid fast = fast.next.next; // end } fast = slow.next; //当前slow是中点 将fast变成是右半部分的第一个结点 slow.next = null; //将慢指针(当前指向mid)指向null 前半段结束 Node<T> rightPartFirstNode = null; //开始将后半段逆序 连到中点上 while(fast != null) { rightPartFirstNode = fast.next; //保存 下一个结点 fast.next = slow; //修改fast的下一个结点 slow = fast; //slow移动 fast = rightPartFirstNode; //fast移动 } //当前只有slow指针指在链表最后一个元素的位置 rightPartFirstNode = slow; //用结点保存 最后结点 //然后slow跟fast指针才能往mid走 fast = head; //让fast成为头结点 boolean res = true; while(slow != null && fast != null) { //检查 判断条件 if(slow.getData() != fast.getData()) { res = false; //这里不着急return 是因为还得将链表变为原样 break; } slow = slow.next; //slow 移动至 mid fast = fast.next; //fast 移动至 mid } //退出此while循环时,fast指向null,slow指向mid //下面开始复原链表 slow = rightPartFirstNode.next; rightPartFirstNode.next = null; while(slow != null) { //当slow到达mid的时候 此时他的next就为null fast = slow.next; slow.next = rightPartFirstNode; rightPartFirstNode = slow; slow = fast; } return res; }
//1、把链表放入数组里面,在数组上做partition(笔试用) public static <T> Node<T> listPartition(Node<T> head, int target){ if(head == null) { return head; } Node<T> cur = head;//cur设为头结点 //求链表元素个数 为了生成数组 int i = 0; while(cur != null) { i++; cur = cur.next; } Node<T>[] nodeArr = new Node[i]; i = 0; cur = head; //遍历往数组中存值 for(i = 0; i != nodeArr.length; i++) { nodeArr[i] = cur; cur = cur.next; } //做partition arrPartition(nodeArr,target); //此时数组已经按预期排好了 下面开始连接结点 for(i = 1; i != nodeArr.length; i++) { nodeArr[i -1].next = nodeArr[i]; } //最后一个结点的next指针 指向null nodeArr[i -1].next = null; //返回头结点 return nodeArr[0]; } public static <T> void arrPartition(Node<T>[] nodeArr,int target) { int small = -1; int big = nodeArr.length; int index = 0 ; while(index != big) { if((int)nodeArr[index].data < target) { swap(nodeArr,++small,index++); //自己和自己交换 }else if((int)nodeArr[index].data == target) { index++; //index++ 直接后移 }else { swap(nodeArr,--big,index); //当前数和最后的数做交换 } } } public static <T> void swap(Node<T>[] arr,int x,int y) { Node<T> node = arr[x]; arr[x] = arr[y]; arr[y] = node; }
public static <T> Node<T> linkPartition(Node<T> head, int target){ Node<T> sH = null; //小于部分的头指针 Node<T> sT = null; //小于部分的尾指针 Node<T> eH = null; //等于部分的头指针 Node<T> eT = null; //等于部分的尾指针 Node<T> mH = null; //大于部分的头指针 Node<T> mT = null; //大于部分的尾指针 Node<T> next = null; //额外变量 while(head != null) { next = head.next; if((int)head.data < target) { if(sH == null) { //如果当前小于部分的头指针为空 sH = head; //小于部分的头尾指针都指向此结点 sT = head; }else { //如果当前小于部分的头指针不为空 sT.next = head; //小于部分的头尾指针的next指向此结点 sT = head; //尾指针后移 } }else if((int)head.data == target) { if(eH == null) { //如果当前等于部分的头指针为空 eH = head; //等于部分的头尾指针都指向此结点 eT = head; }else { //如果当前等于部分的头指针不为空 eT.next = head; //等于部分的头尾指针的next指向此结点 eT = head; //尾指针后移 } }else { if(mH == null) { //如果当前大于部分的头指针为空 mH = head; //大于部分的头尾指针都指向此结点 mT = head; }else { //如果当前大于部分的头指针不为空 mT.next = head; //大于部分的头尾指针的next指向此结点 mT = head; //尾指针后移 } } head = next; } //开始连接各部分 if(sT != null) { //如果有小于区域 sT.next = eH; eT = eT == null ? sT : eT; // 下一步,谁去连大于区域的头,谁就变成eT } if(eT != null) { //如果有等于区域 eT.next = mH; } //最后返回所以链表串起来的最左结点 return sH != null ? sH : (eH != null ? eH : mH); }
public static class Node{
int value;
Node next;
Node rand;
public Node(int val){
value = val;
}
}
rand指针是单链表结点结构中新增的指针,rand 可能指向链表中的任意一个结点,也可能指向null
给定一个由Node结点类型组成的无环单链表的头结点head
请实现一个函数完成这个链表的复制,并返回复制的新链表的头结点
要求:空间复杂度O(N)、额外空间复杂度O(1)
//1、使用HashMap存储 public static Node copyListWithRandByHashMap(Node head) { Map<Node,Node> map = new HashMap<>(); Node cur = head; //第一次循环 存入新Node while(cur != null) { map.put(cur, new Node(cur.value)); cur = cur.next; } cur = head; //第二次循环 为新Node串指针 while(cur != null) { // cur 是老结点 map.get(cur)为新结点 map.get(cur).next = map.get(cur.next); //新结点的next指针 map.get(cur).rand = map.get(cur.rand); //新结点的rand指针 cur = cur.next; } //返回 head` 结点 return map.get(head); }
//2、直接在每个结点后面新增该结点 用位置来解决指针指向问题 public static Node copyListWithRand(Node head) { if(head == null) { return null; } Node cur = head; Node next = null; //第一次循环 对链表中每个结点进行复制 1 -> 2 ==> 1 -> 1` -> 2 -> 2` while(cur != null) { next = cur.next; //2 cur.next = new Node(cur.value); //1 -> 1` cur.next.next = next; //1 -> 1` -> 2 cur = next; //cur = 2 } cur = head; Node curCopy = null; //第二次循环 设置复制结点的rand 1 -> 1` -> 2 -> 2` while(cur != null) { next = cur.next.next; // 2 curCopy = cur.next; // 1` curCopy.rand = cur.rand != null? cur.rand.next : null; cur = next; } //开始分离node`结点 Node resHead = head.next; //此时就是head`结点 cur = head; while(cur != null) { //1 -> 1` -> 2 -> 2` next = cur.next.next; //2 curCopy = cur.next; //1` cur.next = next; //1.next = 2 curCopy.next = next!=null? next.next : null; cur = next; } return resHead; }
给定两个可能有环也可能无环的单链表,头结点head1 和 head2。
请实现一个函数,如果两个链表相交,请返回相交的 第一个结点。如果不相交,返回null
要求:如果两个链表长度之和为N,时间复杂度语法到O(N),额外空间复杂度,请达到O(1)
1、情况一:两个链表都无环,如果相交,返回第一个相交结点,如果不相交,返回null
2、情况二:两个链表 如果一个有环 一个没有 这是不可能相交的 return null
3、情况三:两个链表都有环
找到链表的第一个入环结点,如果无环,返回null
public static Node getLoopNode(Node head) { if(head == null || head.next == null || head.next.next == null) { return null; } //一开始让慢指针先走一步 快指针先走两步 Node slow = head.next; Node fast = head.next.next; //下次相遇时肯定已经走过入环结点了 while(slow != fast) { //如果fast指针走到null 证明链表是一条单线 if(fast.next == null || fast.next.next == null) { return null; } slow = slow.next; fast = fast.next.next; } //此时两个指针已经过了入环结点 让快指针回到head fast = head; //让慢指针停留在原地 快指针回到head //让他们每次走一步 再相遇则是入环点 while(slow != fast) { slow = slow.next; fast = fast.next; } return slow; }
1、两个链表都无环
2、两个链表 一个有环 一个无环 这种情况 两个链表是不可能相交的
3、两个链表都有环
//主函数 如果两个链表相交,返回相交的 第一个结点 public static Node getInterNode(Node head1,Node head2) { if(head1 == null || head2 == null) { return null; } Node loop1 = getLoopNode(head1);//找到链表head1 的第一个入环结点 Node loop2 = getLoopNode(head2);//找到链表head2 的第一个入环结点 //如果两个链表各自的入环结点都为空 证明 情况一 if(loop1 == null && loop2 == null) { return noLoop(head1,head2); } //如果两个链表各自的入环结点都不为空 证明 情况三 if(loop1 != null && loop2 != null) { return bothLoop(head1,loop1,head2,loop2); } //情况二:两个链表 如果一个有环 一个没有 这是不可能相交的 return null return null; } //找到链表的第一个入环结点,如果无环,返回null public static Node getLoopNode(Node head) { if(head == null || head.next == null || head.next.next == null) { return null; } //一开始让慢指针先走一步 快指针先走两步 Node slow = head.next; Node fast = head.next.next; //下次相遇时肯定已经走过入环结点了 while(slow != fast) { //如果fast指针走到null 证明链表是一条单线 if(fast.next == null || fast.next.next == null) { return null; } slow = slow.next; fast = fast.next.next; } //此时两个指针已经过了入环结点 让快指针回到head fast = head; //让慢指针停留在原地 快指针回到head //让他们每次走一步 再相遇则是入环点 while(slow != fast) { slow = slow.next; fast = fast.next; } return slow; } //情况一:如果两个链表都无环,如果相交,返回第一个相交结点,如果不相交,返回null public static Node noLoop(Node head1,Node head2) { if(head1 == null || head2 == null) { return null; } Node cur1 = head1; Node cur2 = head2; int len = 0; //记录两个链表的差值 //注意 cur1是走到最后一个结点 而不是 null while( cur1.next != null) { len ++; cur1 = cur1.next; } //注意 cur2是走到最后一个结点 而不是 null while(cur2.next != null) { len --; cur2 = cur2.next; } //此时len 就是 链表1和链表2 之间的差值 //如果此时 链表1的最后一个结点的内存地址 不等于 链表2的最后一个结点的话 证明 俩链表不相交 if(cur1 != cur2) { return null; } cur1 = len > 0 ? head1 : head2; //如果len大于0 证明head1长度大于head2 谁长谁cur1 cur2 = (cur1 == head1) ? head2 : head1; //谁短,谁cur2 len = Math.abs(len); //len取绝对值 //长链表 先走len个长度 while(len != 0) { len --; cur1 = cur1.next; } //长链表和短链表一起走 再次相遇时 第一个相交结点就找到了 while(cur1 != cur2) { cur1 = cur1.next; cur2 = cur2.next; } return cur1; } //情况二:两个链表 如果一个有环 一个没有 这是不可能相交的 //情况三:如果两个链表都有环,如果相交,他们肯定是共用环的 返回第一个相交结点,如果不相交,返回null public static Node bothLoop(Node head1,Node loop1,Node head2,Node loop2) { Node cur1 = null; Node cur2 = null; if(loop1 == loop2) { //如果两个入环结点相同 证明是情况3-2:如果相交,他们肯定是共用环的 cur1 = head1; cur2 = head2; int len = 0; //两个链表的长度差值 //开始计算链表1的长度 注意此时条件是不等于 loop1 因为可以转换为无环链表相交问题 while(cur1 != loop1) { len ++; cur1 = cur1.next; } while(cur2 != loop2) { len --; cur2 = cur2.next; } cur1 = len > 0 ? head1 : head2; //谁长谁cur1 cur2 = (cur1 == head1) ? head2 : head1;//谁短谁cur2 len = Math.abs(len); //长的链表先走len步 while(len != 0) { len --; cur1 = cur1.next; } //两个链表一起走 while(cur1 != cur2) { cur1 = cur1.next; cur2 = cur2.next; } //相遇则相交 return cur1; }else { //证明是情况3-1 或 情况3-3 cur1 = loop1.next; //往下走 如果遇到loop2证明3-3 相交 如果没遇到证明两个链表有环但没相交3-1 while(cur1 != loop1) { if(cur1 == loop2) { return loop1;//两个loop都算是相交的第一个结点 } cur1 = cur1.next; } return null; //情况3-1 俩链表有环没相交 } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。