标签:
本文是【常用算法思路分析系列】的第四篇,总结链表相关的高频题目和解题思路。本文分析如下几个问题:1、环形链表的差值问题;2、只能访问单个结点的删除问题;3、链表的分化;4、打印两个链表的公共部分;5、把链表的每k个结点逆序;6、删除链表中指定结点;7、判断链表是否为回文结构;8、复杂链表的复制;9、判断链表是否有环;10、判断两个无环链表是否相交;11、判断两个有环链表是否相交;12、判断两个链表(状态未定)是否相交。
本系列前三篇导航:
【常用算法思路分析系列】排序高频题集
【常用算法思路分析系列】字符串高频题集
有一个整数val,如何在节点值有序的环形链表中插入一个节点值为val的节点,并且保证这个环形单链表依然有序。
给定链表的信息,及元素的值A及对应的nxt指向的元素编号同时给定val,请构造出这个环形链表,并插入该值。
[1,3,4,5,7],[1,2,3,4,0],2
返回:{1,2,3,4,5,7}
public class InsertValue { /** * @param args */ public static void main(String[] args) { int[] a = {1,3,4,5,7}; int[] b = {1,2,3,4,0}; // insert(a,b,2); System.out.println("dd"); } public static class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public static ListNode insert(int[] A, int[] nxt, int val) { ListNode head = null; if(A == null || A.length == 0){ head = new ListNode(val); head.next = null; }else{ //构造环形链表 ListNode curNode = new ListNode(A[0]); head = curNode; for(int i = 0; i < nxt.length - 1; i++){ ListNode node = new ListNode(A[nxt[i]]); curNode.next = node; curNode = node; } curNode.next = head; ListNode pre = head; curNode = head.next; boolean flag = false; while(curNode != head){ if(val <= curNode.val){ ListNode node = new ListNode(val); node.next = curNode; pre.next = node; flag = true; break; }else{ pre = curNode; curNode = curNode.next; } } if(!flag){//表示没有找到合适的位置,需要插入到头结点的前面(可能是val大于所有值或小于所有节点值) ListNode node = new ListNode(val); node.next = head; pre.next = node; if(val <= head.val){ head = node; } } } return head; } }
实现一个算法,删除单向链表中间的某个结点,没有给定头结点,你只能访问该结点。
给定带删除的节点,请执行删除操作,若该节点为尾节点,返回false,否则返回true
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public boolean removeNode(ListNode pNode) { if(pNode == null || pNode.next == null) return false; ListNode nextNode = pNode.next; pNode.val = nextNode.val; pNode.next = nextNode.next; return true; }
对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。
给定一个链表的头结点head,同时给定阈值val,请返回一个链表,使小于等于它的结点在前,大于等于它的在后,保证结点值不重复。
{1,4,2,5},3
{1,2,4,5}思路:
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode listDivide(ListNode head, int val) { if(head == null) return null; ListNode minList = null,maxList = null; ListNode minListHead = null,maxListHead = null; ListNode next = null; while(head != null){ next = head.next; head.next = null; if(head.val <= val){//如果当前结点值<=val,将当前结点放入到小于阀值的链表 if(minList == null){ minList = head; minListHead = head; }else{ minList.next = head; minList = head; } }else{//如果当前结点值>val,将当前结点放入到大于阀值的链表 if(maxList == null){ maxList = head; maxListHead = head; }else{ maxList.next = head; maxList = head; } } head = next; } if(minList != null){ minList.next = maxListHead; } return minListHead != null ? minListHead : maxListHead; }
现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。
给定两个链表的头指针headA和headB,请返回一个vector,元素为两个链表的公共部分。请保证返回数组的升序。两个链表的元素个数均小于等于500。保证一定有公共值
{1,2,3,4,5,6,7},{2,4,6,8,10}
返回:[2.4.6]思路:
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public int[] findCommonParts(ListNode headA, ListNode headB) { if(headA == null || headB == null){ return null; } List<Integer> list = new ArrayList<Integer>(); while(headA != null && headB != null){ if(headA.val < headB.val){ headA = headA.next; }else if(headA.val > headB.val){ headB = headB.next; }else{//相等,打印值 list.add(headA.val); headA = headA.next; headB = headB.next; } } int[] res = new int[list.size()]; for(int i = 0; i < list.size(); i++){ res[i] = list.get(i); } return res; }
有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。例如链表1->2->3->4->5->6->7->8->null,K=3这个例子。调整后为,3->2->1->6->5->4->7->8->null。因为K==3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。
给定一个单链表的头指针head,同时给定K值,返回逆序后的链表的头指针。
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode inverse(ListNode head, int k) { if(head == null || head.next == null || k < 2){ return head; } Stack<ListNode> stack = new Stack<ListNode>(); ListNode next = null; ListNode resHead = null;//反转后的链表的头结点 ListNode header = null, tail = null;//记录每次反转过程中的头结点和尾结点 ListNode lastTail = null;//上一次反转过程中的尾结点 while(head != null){ next = head.next; head.next = null; stack.push(head); if(stack.size() == k){ //先退栈反转这k个元素,找到这k个元素的头和尾部 header = tail = stack.pop(); while(!stack.isEmpty()){ tail.next = stack.pop(); tail = tail.next; } if(resHead == null){//是否是第一次反转前k个元素 resHead = header; lastTail = tail; }else{ lastTail.next = header; lastTail = tail; } } head = next; } if(!stack.isEmpty()){//栈中还有元素,表示最后几个元素没有满足k个数量,保持原有顺序 header = stack.pop(); header.next = null; ListNode temp ; while(!stack.isEmpty()){//头插法,维持原来顺序 temp = stack.pop(); temp.next = header; header = temp; } lastTail.next = header; } return resHead; }
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode inverse(ListNode head, int k) { if(head == null || head.next == null || k < 2){ return head; } ListNode next = null; ListNode resHead = null;//反转后的链表的头结点 ListNode header = null, tail = null;//记录每次截取k个元素过程中的头结点和尾结点 ListNode lastTail = null;//上一次反转过程中的尾结点 boolean flag = true; while(flag){ header = null; tail = null; //每次拿出k个元素 for(int i = 0; i < k; i++){ if(head == null){//表示最后一组不满足k个元素 flag = false; break; }else{ next = head.next; head.next = null; if(i == 0){ header = head; tail = head; }else{ tail.next = head; tail = head; } head = next; } } if(flag){//反转这k个元素 ListNode tempListHeader = header; ListNode temp = tempListHeader; tempListHeader = tempListHeader.next; temp.next = null;//摘取出来 while(tempListHeader != null){ next = tempListHeader.next; tempListHeader.next = temp; temp = tempListHeader; tempListHeader = next; } if(resHead == null){ resHead = tail; lastTail = header; }else{ lastTail.next = tail; lastTail = header; } }else{//最后一组不满足k个元素,直接放到反转后的链表后面 lastTail.next = header; } } return resHead; }
现在有一个单链表。链表中每个节点保存一个整数,再给定一个值val,把所有等于val的节点删掉。
给定一个单链表的头结点head,同时给定一个值val,请返回清除后的链表的头结点,保证链表中有不等于该值的其它值。请保证其他元素的相对顺序。
{1,2,3,4,3,2,1},2
{1,3,4,3,1}思路一:
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public ListNode clear(ListNode head, int val) { if(head == null) return null; ListNode first = null,tail = null,next = null; while(head != null){ next = head.next; head.next = null; if(head.val != val){ if(first == null){ first = head; tail = head; }else{ tail.next = head; tail = head; } } head = next; } return first; }
public ListNode clear(ListNode head, intnum) { while(head != null) { if(head.val != num) { break; } head = head.next; } ListNode pre = head; ListNode cur = head; while(cur != null) { if(cur.val == num) { pre.next = cur.next; } else{ pre = cur; } cur = cur.next; } return head; }
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public boolean isPalindrome(ListNode pHead) { if(pHead == null){ return false; } Stack<ListNode> stack = new Stack<ListNode>(); ListNode first = pHead; while(first != null){//链表中全部结点先入栈 stack.push(first); first = first.next; } ListNode temp; while(pHead != null && !stack.isEmpty()){//再遍历一遍链表 temp = stack.pop(); if(temp.val != pHead.val){ return false; }else{ pHead = pHead.next; } } return true; }
public boolean isPalindrome(ListNode pHead) { if(pHead == null){ return false; } Stack<ListNode> stack = new Stack<ListNode>(); ListNode fast = pHead; ListNode slow = pHead; while(fast != null){ if(fast.next == null){//表示到快指针达尾部,说明链表结点个数为奇数,此时慢指针到达中部,且此时慢指针指向的结点不需要入栈 slow = slow.next;//链表结点数为奇数时,跳过中间结点 break; } stack.push(slow); slow = slow.next; fast = fast.next.next; } ListNode temp; while(slow != null && !stack.isEmpty()){//后面慢指针和栈顶结点值依次比较 temp = stack.pop(); if(temp.val != slow.val){ return false; }else{ slow = slow.next; } } return true; }
public boolean isPalindrome(ListNode pHead) { if(pHead == null){ return false; } ListNode fast = pHead; ListNode slow = pHead; ListNode lastHead = null; while(fast.next != null && fast.next.next != null){//寻找到慢指针指向的中点 slow = slow.next; fast = fast.next.next; } if(fast.next != null){//表示链表节点数量为偶数 lastHead = slow.next; }else{ lastHead = slow; } ListNode head = lastHead;//指向反转后的头结点 lastHead = lastHead.next; ListNode next = null; head.next = null; while(lastHead != null){//反转链表 next = lastHead.next; lastHead.next = head; head = lastHead; lastHead = next; } boolean res = true; ListNode tempList = head; while(tempList != null && pHead != null){ if(tempList.val != pHead.val){ res = false; break; } tempList = tempList.next; pHead = pHead.next; } //再把后面反转的链表反转回来 ListNode head2 = head; head = head.next; head2.next = null; while(head != null){ next = head.next; head.next = head2; head2 = head; head = next; } slow.next = head2; return res; }
public class RandomListNode { int label; RandomListNode next = null; RandomListNode random = null; RandomListNode(int label) { this.label = label; } } public RandomListNode Clone(RandomListNode pHead){ if(pHead == null){ return null; } RandomListNode list = pHead; //第一遍遍历结点,每遍历到一个结点,复制一个相同的结点,并加入到当前结点的下一个位置; while(list != null){ RandomListNode node = new RandomListNode(list.label); node.next = list.next; list.next = node; list = list.next.next; } //第二遍遍历结点,设置新结点的random指针 list = pHead; while(list != null){ if(list.random != null){ list.next.random = list.random.next; }else{ list.next.random = null; } list = list.next.next; } list = pHead; RandomListNode newList = null; RandomListNode temp = null; RandomListNode res = null; //第三遍遍历结点,还原原来链表,取出所有的新结点; while(list != null){ temp = list.next; list.next = list.next.next; temp.next = null; list = list.next; if(newList == null){ newList = temp; res = temp; }else{ newList.next = temp; newList = temp; } } return res; }
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public int chkLoop(ListNode head) { if(head == null){ return -1; } int res = -1; Map<ListNode,Integer> map = new HashMap<ListNode,Integer>(); while(head != null){ if(map.containsKey(head)){ return head.val; } map.put(head, 1); head = head.next; } return res; }
public int chkLoop(ListNode head) { if(head == null || head.next == null){//链表为空或者只有一个结点 return -1; } ListNode fast = head.next.next;//我的判断条件中是fast!=slow,因此这里不能设置fast=slow=head; ListNode slow = head.next; while(fast != null && fast.next != null && fast != slow){//如果无环,则fast或fast.next会指向null,如果有环,某一时刻fast==slow fast = fast.next.next; slow = slow.next; } if(fast == slow){//表示有环 fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast.val; } return -1; }
现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交。
给定两个链表的头结点headA和headB,请返回一个bool值,代表这两个链表是否相交。
思路一:public boolean chkIntersect(ListNode headA, ListNode headB) { // write code here ListNode alast=headA,blast=headB; while(alast.next!=null){ alast=alast.next; } while(blast.next!=null){ blast=blast.next; } if(alast==blast) returntrue; else returnfalse; }
public boolean chkIntersect(ListNode headA, ListNode headB) { if(headA == null || headB == null){ return false; } ListNode listA = headA; ListNode listB = headB; int m = 0;//链表A的长度 int n = 0;//链表B的长度 int dst = 0;//两个链表的长度差值 while(listA != null){ m++; listA = listA.next; } while(listB != null){ n++; listB = listB.next; } listA = headA; listB = headB; if(m >= n){ dst = m - n; for(int i = 0; i < dst; i++){ listA = listA.next; } }else{ dst = n - m; for(int i = 0; i < dst; i++){ listB = listB.next; } } while(listA != null && listB != null && listA != listB){ listA = listA.next; listB = listB.next; } if(listA != null && listB != null && listA == listB){ return true; } return false; }
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public boolean chkInter(ListNode head1, ListNode head2) { if(head1 == null || head2 == null){ return false; } ListNode loopNode1 = getLoopStartNode(head1);//拿到链表head1的入环结点 ListNode loopNode2 = getLoopStartNode(head2);//拿到链表head2的入环结点 if(loopNode1 == null || loopNode2 == null) return false; if(loopNode1 == loopNode2){//表示两个链表的入环结点相同,则两个链表肯定相交 return true; }else{ ListNode temp = loopNode1.next; while(temp != loopNode1){ if(temp == loopNode2){//表示环中有相交结点 return true; } temp = temp.next; } } return false; } public ListNode getLoopStartNode(ListNode head){ ListNode fast = head.next.next; ListNode slow = head.next; while(fast != null && fast.next != null && fast != slow){ fast = fast.next.next; slow = slow.next; } if(fast == slow){ fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; } return null; }
public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } public boolean chkInter(ListNode head1, ListNode head2, int adjust0, int adjust1) { if(head1 == null || head2 == null){ return false; } ListNode loopNode1 = getLoopStartNode(head1);//拿到链表head1的入环结点 ListNode loopNode2 = getLoopStartNode(head2);//拿到链表head2的入环结点 //如果一个链表有环,一个链表无环,那么这两个链表不可能相交 if((loopNode1 != null && loopNode2 == null) || (loopNode2 != null && loopNode1 == null)){ return false; } //如果两个链表都没有环,则问题回到两个无环链表是否相交的判断上 if(loopNode1 == null && loopNode2 == null){ return chkNoLoopList(head1,head2); } //如果两个入环结点都不为空,即问题回到两个有环链表是否相交的判断上 if(loopNode1 != null && loopNode2 != null){ return chkHasLoopList(loopNode1,loopNode2); } return false; } /** * 判断两个有环链表是否相交 * @param loopNode1 * @param loopNode2 * @return */ public boolean chkHasLoopList(ListNode loopNode1, ListNode loopNode2) { if(loopNode1 == loopNode2){//表示两个链表的入环结点相同,则两个链表肯定相交 return true; }else{ ListNode temp = loopNode1.next; while(temp != loopNode1){ if(temp == loopNode2){//表示环中有相交结点 return true; } temp = temp.next; } } return false; } /** * 判断两个无环链表是否相交 * @param headA * @param headB * @return */ public boolean chkNoLoopList(ListNode headA, ListNode headB) { if(headA == null || headB == null){ return false; } ListNode listA = headA; ListNode listB = headB; int m = 0;//链表A的长度 int n = 0;//链表B的长度 int dst = 0;//两个链表的长度差值 while(listA != null){ m++; listA = listA.next; } while(listB != null){ n++; listB = listB.next; } listA = headA; listB = headB; if(m >= n){ dst = m - n; for(int i = 0; i < dst; i++){ listA = listA.next; } }else{ dst = n - m; for(int i = 0; i < dst; i++){ listB = listB.next; } } while(listA != null && listB != null && listA != listB){ listA = listA.next; listB = listB.next; } if(listA != null && listB != null && listA == listB){ return true; } return false; } /** * 获取链表的入环结点 * @param head * @return */ public ListNode getLoopStartNode(ListNode head){ if(head == null || head.next == null){//链表为null或者只有一个结点,此时没有入环结点 return null; } ListNode fast = head.next.next; ListNode slow = head.next; while(fast != null && fast.next != null && fast != slow){ fast = fast.next.next; slow = slow.next; } if(fast == slow){ fast = head; while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; } return null; }
标签:
原文地址:http://blog.csdn.net/shakespeare001/article/details/51487553