面试中常见链表题型整理

面试中常见链表题型如下:

1.单链表的反序

2.给单链表建环

3.检测单链表是否有环,返回入口点

4.给单链表解环

5.检测两条链表是否相交,返回交点

6.不输入头节点,删除单链表的指定节点(只给定待删除节点指针)

7.合并两个有序链表


1、单链表的逆序

复制代码
//逆转链表,并返回逆转后的头节点  
node* reverse(node *head)  
{  
    if(head == NULL || head->next == NULL)  
    {  
        return head;  
    }  
    node *cur = head;  
    node *pre = NULL;  
    node *tmp;  
    while(cur->next)  
    {  
        tmp = pre;  
        pre = cur;  
        cur = cur->next;  
        pre->next = tmp;                  //操作pre的next逆转  
    }  
    cur->next = pre;                     //结束时,操作cur的next逆转  
    return cur;  
}  
复制代码
复制代码
//方法二
node *reverse(node *head)
{
    node *p, *q, *r;
    
    p = head;
    q = p->next;
    
    while(q != NULL)
    {
        r = q->next;
        q->next = p;
        p = q;
        q = r;
    }

    head->next = NULL;
    head = p;
    
    return head;
}
复制代码

 

2、给单链表建环

复制代码
//给单链表建环,让尾指针,指向第num个节点,若没有,返回false  
bool bulid_looplink(node *head, int num)  
{  
    node *cur = head;  
    node *tail = NULL;  
    int i = 0;  
    if(num <= 0 || head == NULL)  
    {  
        return false;  
    }  
    for(i = 1; i < num; ++i)  
    {  
        if(cur == NULL)  
        {  
            return false;  
        }  
        cur = cur->next;  
    }  
    tail = cur;  
    while(tail->next)  
    {  
        tail = tail->next;  
    }  
    tail->next = cur;  
    return true;  
}
复制代码

 

3、检测单链表是否有环,如果有则返回入口结点

算法思想:用两个指针p1,p2同时指向链表的头部,p1一次移动一步,p2一次移动两步,如果最终p1和p2重合则说明链表有环,如果p2走到空指针(链表的结尾)则说明链表无环。如果最终p1和p2重合,使p2重新指向链表的头结点,然后p1和p2同时一次移动一步,当p1和p2再次重合时该节点指针就是环的入口节点指针。

证明思路:在p2和p1第一次相遇的时候,假定p1走了n步,环路的入口是在p步的时候经过的,那么有p1走的路径: p+c = n;(c为p1和p2相交点,距离环路入口的距离)p2走的路径: p+c+k*L = 2*n;(L为环路的周长,k是整数)显然,如果从p+c点开始,p1再走n步骤的话,还可以回到p+c这个点,同时p2从头开始走的话,经过n步,也会达到p+c这点。显然在这个步骤当中p1和p2只有前p步骤走的路径不同,所以当p1和p2再次重合的时候,必然是在链表的环路入口点上。

判断链表是否有环

复制代码
bool  IsExitsLoop(slist * head)
{
    slist * slow = head ,  * fast = head;
    while  ( fast  &&  fast -> next ) 
{
     slow  =  slow -> next;
     fast  =  fast -> next -> next;
     if  ( slow  ==  fast )  break ;
}
     return   ! (fast  ==  NULL  ||  fast -> next  ==  NULL);
}
复制代码

如果有环返回入口结点

复制代码
slist *  FindLoopPort(slist * head)
{
   slist * slow  =  head,  * fast  =  head;
   while  ( fast  &&  fast -> next ) 
{
    slow  =  slow -> next;
    fast  =  fast -> next -> next;
    if  ( slow  ==  fast )  break ;
}

    if  (fast  ==  NULL  ||  fast -> next  ==  NULL)
    return  NULL;
    slow  =  head;
    while  (slow  !=  fast)
{
    slow  =  slow -> next;
    fast  =  fast -> next;
}

    return  slow;
}
复制代码

 

4、给单链表建环

复制代码
//找到有环节点,并解环,返回true,无环,返回false  
//思路:先找到环节点:被2个节点指向的节点(一定有环的条件)ps:不考虑中间环,因为只有一个next节点,只可能是尾环  
bool unloop_link(node *head)  
{  
    set<node *> node_bitmap;        //node的地址位图  
    unsigned int num = 0;  
    node *cur = head, *pre = NULL;  
    while(cur != NULL)  
    {  
        if(!node_bitmap.count(cur) )              //该节点未被遍历过  
        {  
            node_bitmap.insert(cur);  
            ++num;  
        }  
        else                               //指向已被遍历过的节点,此时pre节点为尾节点  
        {  
            pre->next = NULL;  
            return true;  
        }  
        pre = cur;  
        cur = cur->next;  
    }  
    return false;  
}
复制代码

 

5、检测两条链表是否相交,有则返回交点

常见思路:

一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。

二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。

复制代码
#include <iostream>
using namespace std;

/*节点的类定义*/
class Node
{
public:
    int data;
    Node * next;
    Node(int data)
    {
        this->data=data;
    }
};

/*链表的类定义*/
class List
{
public:
    Node * head;//头结点指针
    Node * tail;//尾结点指针

    /*用一个整形数组作为参数的构造函数*/
    List(int array[],int length)
    {
        head=new Node(array[0]);
        Node * temp=head;
        int i;
        for(i=1;i<length;i++)
        {
            temp->next=new Node(array[i]);
            temp=temp->next;
        }
        temp->next=NULL;
        tail=temp;
    }

    /*查找指定位置的节点,并返回指向该节点的指针*/
    Node * FindNode(int index)
    {
        Node * temp=head;
        while(--index)
            temp=temp->next;
        return temp;
    }
};

/*判断链表是否有环,如果有环则返回环的首结点指针,否则返回NULL值*/
Node * FindCircle(List list)
{
    Node * p1,* p2;
    p1=list.head;
    p2=list.head;

    /*判断链表是否有环,当p1=p2时说明链表有环,程序跳出循环。如果p2一直走到链表尽头则说明没有环。*/
    do
    {
        if(p2->next!=NULL&&p2->next->next!=NULL)
        {
            p2=p2->next->next;
            p1=p1->next;
        }
        else
            return NULL;
    }
    while(p1!=p2);

    /*求出环的起点节点,并将其返回*/
    p2=list.head;
    while(p1!=p2)
    {
        p2=p2->next;
        p1=p1->next;
    }
    return p1;
}

/*判断两个链表是否交叉,如果交叉返回交叉节点,否则返回NULL。*/
Node * FindCross(List list1,List list2)
{
    list1.tail->next=list1.head;//将list1变成有环链表

    Node * p1,* p2;
    p1=list2.head;
    p2=list2.head;

    /*判断链表是否有环,当p1=p2时说明链表有环,程序跳出循环。如果p2一直走到链表尽头则说明没有环。*/
    do
    {
        if(p2->next!=NULL&&p2->next->next!=NULL)
        {
            p2=p2->next->next;
            p1=p1->next;
        }
        else
            return NULL;
    }
    while(p1!=p2);

    /*求出环的起点节点,并将其返回*/
    p2=list2.head;
    while(p1!=p2)
    {
        p2=p2->next;
        p1=p1->next;
    }
    return p1;
}

int main()
{
    /*构造一个有环链表,并查找环的起始节点*/
    int array[8]={1,2,3,4,5,6,7,8};
    List list(array,sizeof(array)/sizeof(int));//构造一个链表
    list.tail->next=list.FindNode(5);//将该链表构造成一个有环链表
    Node * temp=FindCircle(list);//查找环的起始节点
    if(temp==NULL)
        cout<<"No cirle exists in the list."<<endl;
    else
        cout<<"There is a circle in the list , and the value of the join_point is "<<temp->data<<endl;

    /*构造两个链表,然后使这两个链表交叉。最后查找交叉节点。*/
    int array1[8]={1,2,3,4,5,6,7,8};
    int array2[5]={9,10,11,12,13};
    List list1(array1,sizeof(array1)/sizeof(int));//构造链表list1
    List list2(array2,sizeof(array2)/sizeof(int));//构造链表list2
    list2.tail->next=list.FindNode(3);//使这两个链表交叉
    temp=FindCross(list,list2);//查找这两个链表的交叉节点
    if(temp==NULL)
        cout<<"These two lists dose not cross."<<endl;
    else
        cout<<"These two lists cross with each other , and the value of the corss_point is "<<temp->data<<endl;
    return 0;
}
复制代码



复制代码
//检测两条链表是否相交,是则返回第一个交点,否则返回NULL  
//思路:把2个链表各遍历一遍,记下长度length1和length2,若2者的尾节点指针相等,则相交。  
//       之后再把长的链表从abs(len1-len2)的位置开始遍历,第一个相等的指针为目标节点  
node* detect_intersect_links(node *first_link, node *second_link)  
{  
    int legnth1 = 1, length2 = 1, pos = 0;  
    node *cur = NULL, *longer_link = first_link, *shorter_link = second_link;  
    if(first_link == NULL || second_link == NULL)  
    {  
        return NULL;  
    }  
    while(first_link->next || second_link->next)     //遍历2个链表  
    {  
        if(first_link->next)  
        {  
            first_link = first_link->next;  
            ++legnth1;  
        }
        if(second_link->next)
        {
            second_link = second_link->next;
            ++length2;
        }
    }
    if(first_link != second_link)                 //比较尾节点  
    {
        return NULL;
    }
    pos = legnth1 - length2;  
    if(legnth1 < length2)                  //保证 longer_link为长链表  
    {  
        pos = length2 - legnth1;  
        cur = longer_link;  
        longer_link = shorter_link;  
        shorter_link = cur;  
    }  
    while(pos-- > 0)  
        longer_link = longer_link->next;  
    while(longer_link || shorter_link)  
    {  
        if(longer_link == shorter_link)                  //找到第一个交点  
        {  
            return longer_link;  
        }  
        longer_link = longer_link->next;  
        shorter_link = shorter_link->next;  
    }  
    return NULL;  
}
复制代码


6、不输入头节点,删除单链表的指定节点(只给定待删除节点指针)

复制代码
//无头节点,随机给出单链表中一个非头节点,删除该节点,当传入空节点,或者尾节点时,返回false  
//思路:由于没有头节点,非循环单链表,无法获取目标节点的前节点,所以只能把它的next节点数据前移,并删除next节点  
//ps:当传入节点为尾节点,无法用此方法删除  
bool withouthead_delete_node(node *target_node)  
{  
    node *cur = NULL;  
    if(target_node == NULL || target_node->next == NULL)   //空节点或者尾节点,失败  
    {  
        return false;  
    }  
    cur = target_node->next;  
    target_node->name = cur->name;  
    target_node->next = cur->next;  
    delete cur;  
    return true;  
}
复制代码

 

7、合并两个有序链表

按 Ctrl+C 复制代码
/*
递归实现:
①算法思想:
递归终止条件:若head1为空,返回head2指针(head);若head2为空,返回head1指针(head)
递归过程:
 若head1->data>head2->data;  head 指针应该指向head2所指向的节点,而且head->next应该指向head1和head2->next两个链表的合成序列的头指针;
 否则head 指针应该指向head1所指向的节点,而且head->next应该指向head->next和head2两个链表的合成序列的头指针;
*/
#include <iostream>
using namespace std;


/*节点的类定义*/
class Node
{
public:
    int data;
    Node *next;
    Node(int data)
    {
        this->data = data;
    }
};


/*链表的类定义*/
class LinkedList
{
public:
    Node *head;


    /*用一个整形数组作为参数的构造函数*/
    LinkedList(int array[])
    {
        head = new Node(array[0]);
        Node *temp = head;
        int i;
        for( i = 1; i < 3; i++ )
        {
            temp->next = new Node(array[i]);
            temp = temp->next;
        }
        
        temp->next = NULL;
    }
};


/*递归的合并两个有序链表*/
Node * mergeLinkedList(Node *head1, Node *head2)
{
    Node *p = NULL;
    
    if(head1 == NULL && head2 == NULL)
        return p;
    else if(head1 == NULL)
        return head2;
    else if(head2 == NULL)
        return head1;
    else
    {
        if(head1->data < head2->data)
        {
            p = head1;
            p->next = mergeLinkedList(head1->next, head2);
        }
        else
        {
            p = head2;
            p->next = mergeLinkedList(head1, head2->next);
        }
        
        return p;
    }
}


/*打印链表的所有元素*/
void printList(Node *head)
{
    Node *temp = head;
    while(temp != NULL)
    {
        cout<<temp->data<<"";
        temp = temp->next;
    }
}


int main()
{
    int array1[3] = {2,5,8};
    int array2[3] = {1,6,7};
    
    /*构造两个有序链表--list1和list2*/
    LinkedList list1(array1);
    LinkedList list2(array2);


    /*递归的将这两个有序链表合并成一个有序链表*/
    Node *new_head = mergeLinkedList(list1.head, list2.head);


    /*打印有序链表*/
    printList(new_head);


    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值