单链表的操作合集

单链表的一些基础算法,做了好长时间,在这里做一个总结。


链表节点定义如下:

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值