C++面试总结之算法(三):链表

1. 怎样判断一个链表有环

(1)最常用方法

定义两个指针,同时从链表的头节点出发,一个指针一次走一步,另一个指针一次走两步。如果走得快的指针追上了走得慢的指针,那么链表就是环形链表;如果走得快的指针走到了链表的末尾(next指向 NULL)都没有追上第一个指针,那么链表就不是环形链表。

bool IsLoop(NODE *head) {// 假设为带头节点的单链表

    if (head == NULL)
        return false;

    node *slow = head->next; // 初始时,慢指针从头节点开始走1步
    if (slow == NULL)
        return false;

    node *fast = slow->next; // 初始时,快指针从头节点开始走2步
    while (fast!=NULL && slow!=NULL){ //当单链表没有环时,循环到链表尾结束
        if (fast == slow)
            return true;
        slow = slow->next; // 慢指针每次走一步
        fast = fast->next;
        if (fast != NULL)
            fast = fast->next;
    }
    return false;
}

(2)通过使用STL库中的map表进行映射

首先定义 map<NODE *, int> m; 将一个 NODE * 指针映射成数组的下标,并赋值为一个 int 类型的数值。然后从链表的头指针开始往后遍历,每次遇到一个指针p,就判断 m[p] 是否为0。如果为0,则将m[p]赋值为1,表示该节点第一次访问;而如果m[p]的值为1,则说明这个节点已经被访问过一次了,于是就形成了环。

map<NODE *, int> m;

bool IsLoop_2(NODE *head){
    if (head == NULL)
        return false;

    NODE *p = head;
    while (p) {
        if (m[p] == 0) // 一般默认值都是0
            m[p] = 1;
        else if (m[p] == 1)
            return true;
        p = p->next;
    }

     return false;
}

2. 若单链表有环,如何找出环的入口节点?

(1)定义两个指针p1和p2,在初始化时都指向链表的头节点。 
(2)如果链表中的环有n个节点,指针p1先在链表上向前移动n步。 
(3)然后指针p1和p2以相同的速度在链表上向前移动直到它们相遇。 
(4)它们相遇的节点就是环的入口节点。 

// 1、先求出环中的任一节点(slow)

NODE *MeetingNode(NODE *head) {
    if (head == NULL)
        return NULL;

    node *slow = head->next; // 初始时,慢指针从头节点开始走1步
    if (slow == NULL)
        return NULL;

    node *fast = slow->next; // 初始时,快指针从头节点开始走2步
    while (fast != NULL && slow != NULL) {
        if (fast == slow)
            return fast;

        slow = slow->next; // 慢指针每次走一步
        fast = fast->next;
        if (fast != NULL)
            fast = fast->next;
    }

    return NULL;
}

// 2、从已找到的那个环中节点出发,一边继续向前移动,一边计数,当再次回到这个节点时,就可得到环中的节点数了。

NODE *EntryNodeOfLoop(NODE *head){
    NODE *meetingNode = MeetingNode(head); // 先找出环中的任一节点
    if (meetingNode == NULL)
        return NULL;

    int count = 1; // 计算环中的节点数
    node *p = meetingNode;

    while (p != meetingNode) {
        p = p->next;
        ++count;
    }

    // p和q以相同的速度向前移动,当q指向环的入口节点时,p已经围绕着环走了一圈又回到了入口节点。

    p = head;
    for (int i = 0; i < count; i++)        
        p = p->next;

    node *q = head; // q从头节点开始
    while (q != p) {
        q = q->next;
        p = p->next;
    }

    return p;
}

3. 两个有序的单链表如何合并成一个有序的单链表

(1)方法1(局部引用)

       这种方法避免使用虚拟节点(dummy node),而是使用一个指向指针的指针,struct node** lastPtrRef,这个指针指向结果链表的最后一个节点。在这个方法中,所有由虚拟节点完成的工作都有lastPtrRef完成。

struct node* SortedMerge(struct node* a, struct node* b)  {  
    struct node* result = NULL;  

    /*point to the last result pointer */  
    struct node** lastPtrRef = &result;  
    while(1) {   
        if(a == NULL) {  
            *lastPtrRef = b;  
            break;  
        }  
        else if(b == NULL) {  
            *lastPtrRef = a;  
            break;  
        }  

        if(a->data <= b->data) {  
            MoveNode(lastPtrRef, &a);  
        }   
        else {  
            MoveNode(lastPtrRef, &b);  
        }  

        /*tricky:advance to point to the next ".next" field */  
        lastPtrRef = &((*lastPtrRef)->next);  
    }  

    return (result);  
}  

(2)方法2(递归)

        合并操作是非常适合用递归来完成的一类操作,递归实现将会比迭代实现更加清晰且易于理解。尽管如此,你可能也不愿意使用递归来实现这个操作,因为递归方法所使用的栈空间与链表的长度成正比。
 

struct node* SortedMerge(struct node* a, struct node* b) {  
    struct node* result = NULL;  
    /*Base cases*/  
    if(a == NULL)  
        return (b);  
    else if(b == NULL)  
        return (a);  

    /*Pick either a or b, and recur */  
    if(a->data <= b->data) {  
        result = a;   
        result->next = SortedMerge(a->next, b);  
    }  
    else  {  
        result = b;  
        result->next = SortedMerge(a, b->next);  
    }  

    return (result);  
}  

4. 双向链表排序用什么排序算法比较好

归并排序

5.求一个单链表的中间节点,要求安全检查。

//查找单链表的中间节点,要求只能遍历一次链表

SListNode * FindMidNode(SListNode * phead) {
    SListNode *fast = phead;
    SListNode *slow = phead;

    while (fast) {
        if (fast->next != NULL)
            fast = fast->next->next;
        else
            break;
        slow = slow->next;
    }

    return slow;
}   

猜你喜欢

转载自blog.csdn.net/lxin_liu/article/details/89307397