单链表的一些基础算法,做了好长时间,在这里做一个总结。
链表节点定义如下:
struct Node { Node * next; int data; }; |
本部分使用的链表都是带头结点的。判空的条件为h->next==0.
1.1.1单链表的就地反转
给定单链表的头节点head,求链表反转。
/** reverse list in place */
void Reverse(Node * h)
{
Node * pre=0,//当前处理的上一个节点
* curr = h->next;//正在处理的节点,需要将上一个节点链接在它后面
while(curr)
{
Node * t = curr->next;///保存当前处理节点的下一个节点,防止链断开
curr->next = pre;///将前一个节点挂接在当前节点
pre = curr;///节点后移
curr = t;///节点后移
}
h->next = pre;///将最后一个节点挂接在头节点,或者直接返回pre指针
}
不需要返回值,因为没有修改头指针。即使改变h的值,也没有改变头指针的值。对h是值传递。
2 链表相交(或求公共起始节点)(2012年研究生考试408算法题)
给定两个单链表,表头分别为head1和head2.判断两个链表是否相交,如果不相交返回NULL,如果相交,则给出相交的第一个交点。
对题目进行简单分析后不难得出,因为链表的特殊存储结构,使得其在存储结构上如果交叉则一定为“Y”型或者为“V”型,不可能为“X”型。所以相交只需求出第一个交点。
思路1:常规方法:对于其中一个链表中的每个节点,在另一个链表中检索一遍看是否存在,如果存在则返回该节点指针。时间复杂度O(n2).
思路2:先求出2个链表的长度x,y。然后先从长链表遍历|x-y|个,再同时遍历2个链表,如果指针相等,则返回该指针。复杂度O(m+n)
代码如下:
Node * commonNode(Node * ha, Node * hb)
{
Node * ta=ha, *tb=hb;
int la=0,lb=0;
while(ta->next)
{
ta = ta->next;
la++;
}
while(tb->next)
{
tb = tb->next;
lb++;
}
if(ta!=tb)/**最后一个节点不同,肯定没有交点*/
return 0;
/**ta:长链表,tb:短链表**/
la>lb?(ta=ha,tb=hb):(ta=hb,tb=ha);
int len = (la>lb?(la-lb):(lb-la));
while(len)
{//
ta = ta->next;
len--;
}
//寻找相等的指针,如果找到即返回
while(ta)
{
if(ta==tb)//
return ta;
ta = ta->next;
tb = tb->next;
}
return 0;
}
3 求链表倒数第n个节点
给定一个单链表的头节点,找出其倒数第n个节点。
思路1:遍历2次链表,第一次遍历求出链表长度m,第二次遍历到第(m-n)个节点即为所求节点。
思路2:只遍历1次链表。用2个辅助指针。第一个指针先从头开始遍历到第n个节点,然后第二个指针从头开始和第一个指针一起遍历,指导第一个指针到达末尾。此时第二个指针即为倒数第n个节点。
代码如下:
Node * inverseCount(Node*h, int n)
{
Node * p=h->next,*q=h->next;
while(n--)
{
if(!p) return 0;
p = p->next;
}
while(p)
{
q = q->next;
p = p->next;
}
return q;
}
4.删除单个节点
给定指向单链表的某个节点,保证不是最后一个节点,要求删除该节点。
思路:无法直接删除,因为根本不知道该节点的前一个节点。所以,可以复制下一个节点的信息到该节点,然后删除下一个节点。
代码如下:
/**删除给定的节点(不是最后一个)*/
void deleteOneNode(Node * n)
{
Node * t;
if(n)
{
n->data = n->next->data;//copy info
//delete node n->next
t = n->next;
n->next = t->next;
free(t);
}
}
5 链表是否有环
给定一个头节点,判断该链表是否存在环。
思路:如果存在环,则一直遍历,不会存在指针域为空;如果不存在环,则肯定会存在指针域为空的节点。定义2个指针,一个单步遍历,即每次走一步,另一个指针遍历的快,每次走2步或者更多,如果有环一定会相遇。
代码:
bool existLoop(Node * h)
{
Node * p=h, *q;
if(!p) return false;
q = h->next;
while(p&&q&&q->next)
{
if(p==q)
return true;
p = p->next;
q = q->next->next;
}
return false;
}
6.将2个递增的链表合并为递减链表
给定2个递增的链表ha,hb,合并为一个新的递减的链表hc。
思路:如果直接归并,可以合并为递增,如果使用头插法,将得到倒序链表。联合起来,一遍归并,归并后头插法插入即可。
代码如下:
Node* myMerge(Node* ha, Node* hb)
{
Node *p = ha->next, *q = hb->next;
Node * hc = (Node *)malloc(sizeof(Node));
hc->next = 0;
while(p&&q)
{
if(p->data < q->data)//p > q
{
Node * t = (Node *)malloc(sizeof(Node));//生成新的节点
t->data = p->data;//拷贝节点值
//插入在节点头部
t->next = hc->next;
hc->next = t;
p = p->next;//指针后移一步
}
else //q >= p
{
Node * t = (Node *)malloc(sizeof(Node));
t->data = q->data;
t->next = hc->next;
hc->next = t;
q = q->next;
}
}
while(p)//如果p没有到末尾,直接拷贝
{
Node * t = (Node *)malloc(sizeof(Node));
t->data = p->data;
t->next = hc->next;
hc->next = t;
p = p->next;
}
while(q) //如果q没有到末尾,直接拷贝
{
Node * t = (Node *)malloc(sizeof(Node));
t->data = q->data;
t->next = hc->next;
hc->next = t;
q = q->next;
}
return hc;
}
如果想直接下载调试通过的代码和测试用例,请点击这里。
=====================补充============================
7、已知链表有环,求环的入口
代码如下:
#include <iostream>
using namespace std;
struct Node
{
Node(int d):data(d),next(0) {}
int data;
Node* next;
};
bool HaveCircle(Node* h)
{
Node *pf=h, ///fast pointer
*ps=h;///snow pointer
while(ps&&pf&&pf->next)
{
ps = ps->next;
pf = pf->next->next;
if(ps==pf)
break;
}
if(pf==ps&&ps!=0)
{
cout<<"meet: "<<ps->data<<endl;
return true;
}
return false;
}
Node* ListEntrance(Node* h)
{
if(HaveCircle(h))///先判断有环
{
Node *pf=h, ///fast pointer
*ps=h;///snow pointer
while(ps&&pf&&pf->next)
{
ps = ps->next;
pf = pf->next->next;
if(ps==pf)
break;
}///ps,pf为相遇点
Node *q=h;///q重头开始走
while(q!=ps)
{
q = q->next;
ps= ps->next;///ps,q同时移动
}
return q;///相遇点即为入口点
}
else
return 0;
}
void PrintList(Node* h)
{
while(h)
{
cout<<h->data<<" ";
h=h->next;
}
cout<<endl;
}
int main()
{
Node n1(1),n2(2),n3(3),n4(4),n5(5),n6(6);
n1.next = &n2;
n2.next = &n3;
n3.next = &n4;
n4.next = &n5;
n5.next = &n6;
n6.next = &n1;
//PrintList(&n1);
cout<<HaveCircle(&n1)<<endl;
Node *t=ListEntrance(&n1);
if(t)
cout<<"ListEntrance :"<<t->data<<endl;
else
cout<<"NULL"<<endl;
return 0;
}