我与代码的日常:单链表面试题

1.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
解法思路:先求出单链表的长度length,再定义一个指针初识指向单链表的头部,让指针走length/2步,即为所求。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

int Getlength(struct ListNode* head)
{
    int count = 0;
    if(head==NULL)
    {
        return 0;
    }
    struct ListNode* cur=head;
    while(cur!=NULL)
    {
        count++;
        cur=cur->next;
    }
    return count;
}
struct ListNode* middleNode(struct ListNode* head) {
    if(head==NULL)
    {
        return NULL;
    }
    int k = Getlength(head);
    int step = k/2;
    struct ListNode* middle = head;
    while(step)
    {
        middle=middle->next;
        step--;
    }
    return middle;
}

2.删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
解法思路:定义一个指针cur指向链表的第二个节点,指针prev初始值为NULL,
只要cur不空,就遍历整个链表,同时cur 与 prev分别向后走一步。若遇到要删除的值,就进行单链表的删除操作。若还未遇到,就一直向后遍历,直到cur为NULL。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeElements(struct ListNode* head, int val) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* cur = head->next ;
    struct ListNode* prev = head;
    while(cur!=NULL)
    {
        if(cur->val != val)
        {
            prev = cur;
            cur = cur->next;
        }
        else
        {
            struct ListNode* old_cur = cur;
            cur = cur->next;  
            prev->next = cur;
            free(old_cur);     
        }
    }
    struct ListNode* newHead = head;
    if(head->val == val)
    {
        newHead = head->next;
        free(head) ;
    }
    return newHead;
}

3.反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
解法思路:可以对旧链表进行头删操作,然后将删除的节点头插在新链表中,最后返回新链表的头结点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* newHead = NULL;
    struct ListNode* node ;
    while(head != NULL)
    {
        //对旧链表做头删操作
        node = head ;
        head = head->next;
        //把删掉的节点头插在新链表中
        node->next = newHead;
        newHead = node ;
    }
    return newHead;
}

4.输入一个链表,输出该链表中倒数第k个结点。
解法思路:定义两个指针fast与slow,先让fast走k步,再让两个指针每次走一步,直到fast为NULL,则slow指向的即为所求。

  ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    ListNode* fast = pListHead;
    ListNode* slow = pListHead;
        //先让fast走
    while(k)
    {
        fast = fast->next;
        k--;
    }
    //再让两个指针每次走一步
    while(fast!=NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
        return slow;
    }

5.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
解法思路:定义两个指针与一个新链表的头尾指针。让两个指针分别指向两个链表。逐一的进行值比较,然后将节点插入新的链表的末端。最后返回新链表的头指针。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1 == NULL)
    {
        return l2;
    }
    if(l2 == NULL)
    {
        return l1;
    }
    struct ListNode* p1 = l1;
    struct ListNode* p2 = l2;
    struct ListNode* r = NULL; //新链表的头
    struct ListNode* rtail = NULL; //新链表的尾
    while(p1 && p2)
    {
        if(p1->val <= p2->val)
        {
            if(rtail == NULL)
            {
                r = rtail = p1;
            }
            else
            {
                rtail->next = p1;
                rtail = rtail->next;
            }
            p1 = p1->next;
        }
        else
        {
            if(rtail == NULL)
            {
                r = rtail = p2;
            }
            else
            {
                if(rtail == NULL)
                {
                    r = rtail = p2;
                }
                else
                {
                    rtail->next = p2;
                    rtail = p2;
                }
            }
            p2 = p2->next;
        }
    }
    if(p1 == NULL)
    {
        rtail->next = p2;
    }
    if(p2 == NULL)
    {
        rtail->next = p1;
    }
    return r ;
}

6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

给定一个链表的头指针 ListNode* pHead,请返回重新排列后的链表的头指针。注意:分割以后保持原来的数据顺序不变。
解法思路:将链表分割成三部分,然后进行遍历,按要求插入即可。

ListNode* partition(ListNode* pHead, int x) {
        // write code here
        if(pHead == NULL)
        {
            return NULL;
        }
        //将链表分割成三部分,小,等于,大
        ListNode* lt = NULL; //小的部分的头
        ListNode* ltail = NULL ;//小的部分的尾
        ListNode* eq = NULL ; //相等的部分的头
        ListNode* eqtail = NULL;//相等的部分的尾
        ListNode* gt = NULL; //大的部分的头
        ListNode* gtail = NULL ;//大的部分的尾
        ListNode* cur = pHead;
        while(cur != NULL)
        {
            if(cur->val < x)
            {
                if(lt == NULL)
                {
                    lt = ltail = cur;
                }
                else
                {
                    ltail->next = cur;
                    ltail = ltail->next;
                }
            }
           else if(cur->val == x)
            {
                if(eq == NULL)
                {
                    eq = eqtail = cur;
                }
                else
                {
                    eqtail->next = cur ;
                    eqtail = eqtail->next ;
                }
            }
            else
            {
                if(gt == NULL)
                {
                    gt = gtail = cur;
                }
                else
                {
                    gtail->next = cur;
                    gtail = gtail->next;
                }
            }
            cur = cur->next ;
        }
        return pHead;
    }

7.编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表:
在这里插入图片描述
在c1处相交,则返回c1的地址。
解法思路:分别求出两个链表的长度len1和len2,定义两个指针longer与shorter,先让长的走两者长度之差步,以保证两个指针的相对位置一样。然后循环遍历,只要节点的位置不同则继续向后走,直到节点地址相等时跳出循环并返回此相交节点的地址。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int Getlength(struct ListNode* head)
{
    int count = 0 ;
    struct ListNode* cur = head;
    while(cur!=NULL)
    {
        count++;
        cur = cur->next;
    }
    return count;
}
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA == NULL)
    {
        return NULL;
    }
    if(headB == NULL)
    {
        return NULL;
    }
    int len1 = Getlength(headA) ;
    int len2 = Getlength(headB) ;
    struct ListNode* longer ;
    struct ListNode* shorter;
    int dif; //长度之差
    if(len1 >= len2)
    {
        longer = headA;
        shorter = headB;
        dif = len1 - len2;
    }
    else
    {
        longer = headB;
        shorter = headA;
        dif = len2 - len1;
    }
    //先让长的走dif步保证两者相对距离一样
    while(dif)
    {
        longer = longer->next;
        dif--;
    }
    while(longer!=shorter)
    {
        longer = longer->next;
        shorter = shorter->next;
    }
    //注意,此处是地址相等即可
    if(longer == shorter)
    {
        return longer;
    }
    else
        return NULL;
    
}

8.给定一个链表,判断链表中是否有环。
解法思路:利用快慢指针法。具体解法请看以下代码。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    do{
        if(fast == NULL)
        {
            break;
        }
        fast = fast->next;
        if(fast == NULL)
        {
            break;
        }
        fast = fast->next;
        slow = slow->next;  
    }while(fast != slow);
    if(fast == NULL)
    {
        return false;
    }
    else
        return true;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章