1. 反转链表(剑指offer-16)
题目:输入一个链表的头结点,反转该链表,并返回反转后链表的头结点。
解法一:从头到尾遍历原链表,每遍历一个结点,将其摘下放在新链表的最前端。注意链表为空和只有一个结点的情况。时间复杂度为O(n)。
//链表反转
public LNode ReverseList(LNode head){
// 如果链表为空或只有一个结点,无需反转,直接返回原链表头指针
if(head.next == null || head.next.next == null)
return head.next;
LNode pReversedHead = head;
LNode pCurrent = head.next;
pReversedHead.next=null;//先摘头节点,使得head.next=null
while(pCurrent != null)
{
LNode pTemp = pCurrent;
pCurrent = pCurrent.next;
pTemp.next = pReversedHead.next; // 将当前结点摘下,插入新链表的最前端
pReversedHead.next = pTemp;
}
return pReversedHead.next;
}
解法二:考虑到一般情况,我们翻转指针,每次利用指针p和p->next也就是p1来改变p1元素的位置
public LNode ReverseList(LNode head) {
// 如果链表为空或只有一个结点,无需反转,直接返回原链表头指针
if (head.next == null || head.next.next == null)
return head.next;
LNode p = head.next;
LNode p1 = p.next;
while(p.next != null){
p.next = p1.next;
p1.next = head.next;
head.next = p1;
p1 = p.next;
}
return head.next;
}
2. 合并两个排序的链表(剑指offer-17)
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。
解法一:递归实现(两个链表无头节点)
//合并两个排序链表
public static LNode Merge(LNode head1,LNode head2){
if(head1 == null)
return head2;
else if(head2 == null)
return head1;
LNode pMergedHead = null;
if(head1.data < head2.data)
{
pMergedHead = head1;
pMergedHead.next = Merge(head1.next, head2);
}
else
{
pMergedHead = head2;
pMergedHead.next = Merge(head1, head2.next);
}
return pMergedHead;
}
解法二:非递归实现(带头节点)
public static LNode Merge1(LNode head1, LNode head2) {
if (head1.next == null)
return head2;
else if (head2.next == null)
return head1;
LNode pMergedHead = head1;
LNode index1 = head1.next;
LNode index2 = head2.next;
while (index1 != null && index2 != null) {
if (index1.data < index2.data) {
pMergedHead.next = index1;
pMergedHead = index1;
index1 = index1.next;
}else{
pMergedHead.next = index2;
pMergedHead = index2;
index2 = index2.next;
}
}
if(index1!=null){
pMergedHead.next = index1;
}
if(index2!=null){
pMergedHead.next = index2;
}
return head1;
}
3.两个链表中的第一个公共节点(剑指offer-37)
题目:输入两个链表,找出它们的第一个公共节点。(注:输入链表为单链表非循环链表)
解析:首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个节点。在第二次遍历的时候,先在较长的节点上走若干步,接着同时在两个链表上遍历,找到的第一个相同的节点就是它们的公共的节点。
java代码:
// 两个链表中的第一个公共节点
public static LNode FindFirstCommonNode(LNode head1, LNode head2) {
// 得到两个链表的长度
int nLength1 = getLength(head1);
int nLength2 = getLength(head2);
int nLengthDif;
LNode pHeadLong;
LNode pHeadShort;
if (nLength2 > nLength1) {
pHeadLong = head2;
pHeadShort = head1;
nLengthDif = nLength2 - nLength1;
} else {
pHeadLong = head1;
pHeadShort = head2;
nLengthDif = nLength1 - nLength2;
}
// 先在长链表上走几步,再同时在两个链表上遍历。
for (int i = 0; i < nLengthDif; i++)
pHeadLong = pHeadLong.next;
while ((pHeadLong != null) && (pHeadShort != null)
&& (pHeadLong.data != pHeadShort.data)) {
pHeadLong = pHeadLong.next;
pHeadShort = pHeadShort.next;
}
// 得到第一个公共节点
LNode pFirstCommonNode = pHeadLong;
return pFirstCommonNode;
}
// 获取单链表长度
public static int getLength(LNode head) {
int length = 0;
LNode p = head;
while (p.next != null) {
length++;
p = p.next;
}
return length;
}
相关题目:
1.判断一个单链表中是否有环
这里也是用到两个指针。如果一个链表中有环,也就是说用一个指针去遍历,是永远走不到头的。因此,我们可以用两个指针去遍历,一个指针一次走两步,一个指针一次走一步,如果有环,两个指针肯定会在环中相遇。时间复杂度为O(n)。
2.判断两个单链表是否相交:
如果两个链表相交于某一节点,那么在这个相交节点之后的所有节点都是两个链表所共有的。也就是说,如果两个链表相交,那么最后一个节点肯定是共有的。先遍历第一个链表,记住最后一个节点,然后遍历第二个链表,到最后一个节点时和第一个链表的最后一个节点做比较,如果相同,则相交,否则不相交。时间复杂度为O(len1+len2),因为只需要一个额外指针保存最后一个节点地址,空间复杂度为O(1)。
3.已知一个单链表中存在环,求进入环中的第一个节点:
第一步:使用快、慢指针来判断链表是否存在环,若不存在结束。
第二步:若链表中存在环,我们从链表头、与两个指针的相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇的第一个点为环的入口点。
分析:当pfast若与pslow相遇时,pslow肯定没有走遍历完链表,而pfast已经在环内循环了n圈(1<=n)。
假设pslow走了s步,则pfast走了2s步(pfast步数还等于s 加上在环上多转的n圈),设环长为r,则:2s = s + nr s= nr
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a。 a + x = nr 则 a + x = (n – 1)r +r = (n-1)r + L - a a = (n-1)r + (L – a – x)
(L – a – x)为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点,于是我们从链表头、与相遇点分别设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。
参考来源: