1.打印有序链表的公共值
问题描述:
现有两个升序链表,且链表中均无重复元素。请设计一个高效的算法,打印两个链表的公共值部分。
给定两个链表的头指针headA和headB,请返回一个vector,元素为两个链表的公共部分。请保证返回数组的升序。两个链表的元素个数均小于等于500。保证一定有公共 值测试样例:
{1,2,3,4,5,6,7},{2,4,6,8,10}
返回:[2.4.6]
方法:两个头指针分别指向表头,哪个结点元素小,哪个指针前移,如果相同,则都前移,任意一个链表走完,函数停止。
public int[] findCommonParts(ListNode headA, ListNode headB) { // write code here List<Integer> arr=new ArrayList<Integer>(); if(headA==null||headB==null) return null; while(headA!=null&&headB!=null){ if(headA.val==headB.val) {arr.add(headA.val); headA=headA.next; headB=headB.next; }else if(headA.val<headB.val) headA=headA.next; else headB=headB.next; } int[] array=new int[arr.size()]; for(int i=0;i<arr.size();i++) array[i]=arr.get(i); return array; }
2.用栈完成链表的K逆序
问题:
有一个单链表,请设计一个算法,使得每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值,返回逆序后的链表的头指针。
方法:利用栈的特性,对k个元素进行反转
public ListNode inverse(ListNode head, int k) { // write code here ListNode[] stk=new ListNode[k]; int top=-1; ListNode current=head; ListNode lastGroupHead=new ListNode(-1); ListNode newHead=lastGroupHead; while(current!=null){ if(top==k-1){ while(top!=-1){ lastGroupHead=lastGroupHead.next=stk[top--]; } }else{ stk[++top]=current; current=current.next; } } if(top==k-1){ while(top!=-1) lastGroupHead=lastGroupHead.next=stk[top--]; }else{ for(int i=0;i<=top;i++) lastGroupHead=lastGroupHead.next=stk[i]; }lastGroupHead.next=null; return newHead.next; }
3.判断一个链表是否为回文结构:
方法1:T=O(n),S=O(n), 利用栈把链表每个元素入栈,然后依次出栈和链表元素对比。
方法2:T=O(n/2),S=O(n),设置两个指针,一个快指针(一次前进两步),一个慢指针,目的是找到链表的中间的位置。
分析:当链表元素个数为奇数时,快指针.next=null时(一定会走到.next=null),则此时移动后的慢指针就是中间位置。
当偶数时,快指针=null时(必然会走到null),移动前的慢指针就是中间两个元素的左边位置。
当得到中间位置的指针时,只需将一半的链表入栈,进行对比。
方法3:T=O(n),S=O(1),将后半部分指针翻转,如 1->2->3->2->1 变换成1->2->3<-2<-1,用右半部分和左半部分判断。
public static boolean isPalindrome(ListNode pHead) { ListNode slow=pHead; ListNode fast=pHead; while(fast!=null&&fast.next!=null) { fast=fast.next.next; slow=slow.next; } ListNode right;
//从slow开始翻转(如果是偶数,如1,2,3,3,2,1,奇数时如1,2,3,2,1 右半部分都是1,2,3
//无论是奇是偶,只需把右半部分比较完即可 right=reverse(slow); ListNode current=pHead;
while(right!=null) { if(current.val!=right.val) return false; current=current.next; right=right.next; } return true; }
//翻转右半部分 public static ListNode reverse(ListNode head) { ListNode n1=head; ListNode current=head.next; n1.next=null; ListNode n2=current; while(current!=null) { n2=current; ListNode tmp=current.next; n2.next=n1; n1=current; current=tmp; } return n2; }
4.复杂链表的复制
问题描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回一个链表的拷贝。
方法图解:
代码:
public RandomListNode Clone(RandomListNode pHead) { RandomListNode cur=pHead; while(cur!=null){ RandomListNode tmp=cur.next; RandomListNode cp=new RandomListNode(cur.label); cp.next=cur.next; cur.next=cp; cur=tmp; } cur=pHead; while(cur!=null){ if(cur.random==null) cur.next.random=null; else cur.next.random=cur.random.next; cur=cur.next.next; } RandomListNode newHead=new RandomListNode(-1); RandomListNode newcur=newHead; cur=pHead; while(cur!=null){ newcur.next=cur.next; newcur=newcur.next; cur.next=cur.next.next; cur=cur.next; } return newHead.next; }
5.链表判断否是有环
问题:如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回-1。
如果链表的长度为N,请做到时间复杂度O(N),额外空间复杂度O(1)。
分析:如果不做空间上的限制,可以使用哈希表来辅助,当一个节点重复出现时,即是环的入口。
当限制空间时,采用两个指针,快和慢,当快指针和慢指针相交时,即表示有环,此时让快指针回头开始位置,两个指针每次走一步,
当相交时,即进入环(此方法的数学证明略)
代码:
public int chkLoop(ListNode head, int adjust) { // write code here ListNode slow=head; ListNode fast=head; while(fast!=null&&fast.next!=null){ slow=slow.next; fast=fast.next.next; if(slow==fast){ fast=head; while(slow!=fast){ fast=fast.next; slow=slow.next; } return fast.val; } } return -1; }
6.无环链表判断相交
问题:
现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交
分析:
由于链表相交,则必然最后会有公共部分,故可以直接判断最后一个节点是否相同。
若要找到第一个交点,则需让较长的链表先走n2-n1步,然后两链表同步走,直到相交
代码:
public boolean chkIntersect(ListNode headA, ListNode headB) { // write code here ListNode lastA=headA; while(lastA.next!=null) lastA=lastA.next; ListNode lastB=headB; while(lastB.next!=null) lastB=lastB.next; return lastA==lastB; }
7.有环链表判断相交
问题:
如何判断两个有环单链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。如果两个链表长度分别为N和M,请做到时间复杂度O(N+M),额外空间复杂度O(1)。
分析:
首先取得带环链表的环入口,如果两入口相等,则有1,2两种情况,否则有3,4两种情况。
代码:
public boolean chkInter(ListNode head1, ListNode head2, int adjust0, int adjust1) { // write code here ListNode entry1=chkLoop(head1,0); ListNode entry2=chkLoop(head2,0); if(entry1==null||entry2==null) return false; if(entry1==entry2){ return true; }else{ ListNode tmp=entry2.next; while(tmp!=entry2){ if(tmp==entry1) return true; tmp=tmp.next; } return false; } }
8.链表判断相交
综合已上几种情况,分为链表都有环和都无环进行考虑,其他情况则不可能相交