==============
2014-09-17
1、如何判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针)
思路:标志位如何解释?
方法一:使用p、q两个指针,p总是向前走,但q每次都从头开始走,对于每个结点,看p走的步数是否和q一样。如图,当p从6走到3时,用了6步,此时,若q从head出发,则只需两步到达3,因而步数不等,出现矛盾,存在环。
方法二:使用p、q两个指针,p每次向前走一步,q每次向前走两步,若在某个时候p == q,则存在环。
代码:
int HasLoop( node* head )
{
node* first = head;
node* second = head;
while( first != NULL && second != NULL && second->next != NULL)
{
p = p->next;
if( q->next != NULL )
q = q->next->next;
if( p == q )
return 1;
}
return 0;
}
延伸:
如何知道环的长度:记录下方法二中的碰撞点p,first从该店开始,再次碰撞所走过的步数即为环的长度。
2、链表奇偶交换,即头结点与第二个结点交换,第三个结点与第四个结点交换。
node* SwitchEvenAndOdd( node* head )
{
if( head == NULL || head->next = NULL )
return head;
node temp;
temp.next = head;
node* first = &temp;
node* second;
while( first-next != NULL && ( ( second = first->next->next ) != NULL )
{
first->next->next = second->next;
second->next = first->next;
first->next = second;
first = first->next->next;
}
return temp.next;
}
3、获取单链表倒数第n个结点值。
思路:建立两个指针,第一个先走n步,然后第2个指针也开始走,两个指针步伐(前进速度)一致。当第一个结点走到链表末尾时,第二个节点的位置就是我们需要的倒数第n个节点的值。
拓展:不管是顺数n个还是倒数n个,其实都是距离-标尺问题。标尺是一段距离可以用线段的两个端点来衡量,我们能够判断倒数第一个节点,因为他的next==NULL。如果我们用两个指针,并保持他们的距离为n,那么当这个线段的右端指向末尾节点时,左端节点就指向倒数第n个节点。
4、单链表逆序
代码:
node* ListReverse( node* head )
{
if( head ==NULL && head->next == NULL )
return head;
node* current,pnext, prev;
current = head->next;
pnext = current->next;
current->next = NULL;
while(pnext)
{
prev = pnext->next;
pnext->next = current;
current = pnext;
pnext = prev;
}
head->next = current;
return head;
}
5、给定两个单链表(head1,head2),检测两个链表是否有交点,如果有返回第一个交点。
如果head1==head2,那么显然相交,直接返回head1。
6、找出链表的中间元素
设置两个结点,第一个结点每次走一步,第二个结点每次走两步,遍历一遍后,当第二个结点到头时,第一个结点就到了中间结点了。
7、
两个有序链表的合并
node* mergeSortedList( node* head1, node* head2 )
{
if(head1 == null)
return head2;
if(head2 == null)
return head1;
node* p1 == head1;
node* p2 == head2;
node* mergeEnd,mergedNode;
if( p1 == q1 )
{
mergedNode = p1;
p1 = p1->next;
q1 = q1->next;
}
else
{
if( p1->value <=p2->value )
{
mergedNode = p1;
p1 = p1->next;
}
else
{
mergedNode = q1;
q1 = q1->next;
}
}
mergeEnd = mergedNode;
while( p1 != null && q1 != null )
{
if( p1=p2 )
{
mergeEnd->next = p1;
mergeEnd = p1;
p1 = p1->next;
q1 = q1->next;
}
else
{
if( p1->value <= q1->value )
{
mergeEnd->next = p1;
mergeEnd = p1;
p1 = p1->next;
}
else
{
mergeEnd->next = q1;
mergeEnd = q1;
q1 = q1->next;
}
}
}
mergeEnd-next = p1 ? p1 : q1;
return mergedNode;
}
//p1 q1 命名有时间更改下。。。见下面的归并排序算法
8、链表排序
思路:链表排序最好使用归并排序算法。
对单链表进行归并排序,单链表与数组相比只能顺序访问每个元素,因此在使用二路归并排序时关键在于找到链表的中间结点将链表一分为二:可以利用一个步长为2的指针和一个步长为1的指针同时遍历单链表,当步长为2的指针指向链表最后一个结点或者最后一个结点的下一个结点时,步长为1的指针即指向链表的中间结点。然后是两个有序单链表的合并问题。时间复杂度为O(N*logN),空间复杂度为O(1)。
==========
2014-09-18
归并排序算法采用递归思路,主要是写出三个函数,一个getMiddle(), 一个 Merge(),一个MergeSort()。
node* getMiddle( node*head )
{
node* midPre, midPost,*temp;
midPre = null;
temp = midPost = head;
while( temp != null && temp->next != null )
{
temp = temp->next->next;
midPre = midPost;
midPost = midPost->next;
}
midPre->next = null;
return midPost;
}
node* Merge( node* List1, node* List2 )
{
node* temp1 = List1;
node* temp2 = List2;
node* MergedList;
node* temp;
if( temp1->value <= temp2->value )
{
MergedList = temp = temp1;
temp1 = temp1->next;
}
else
{
MergedList = temp = temp2;
temp2 = temp2->next;
}
while( temp1 != null && temp2 != null )
{
if( temp1->value <= temp2->value )
{
temp ->next = temp1;
temp1 = temp1->next;
temp = temp->next;
}
else
{
temp->next = temp2;
temp2 = temp2->next;
temp = temp->next;
}
}
temp->next = temp1 ? temp1 : temp2;
return MergedList;
}
void MergeSort( node* head )
{
if( head == null || head->next == null )
return head;
node* NodePre, NodePost;
NodePost = getMiddle( head );
NodePre = head;
MergeSort( NodePre );
MergeSort( NodePost );
Merge( NodePre, NodePost );
head = NodePre;
}