链表——面试题(1)

删除链表中等于给定值val的所有节点
反转一个单链表
返回链表的中间结点
输入一个链表,输出该链表中倒数第k个结点
合成两个链表
返回链表开始入环的第一个节点
1、删除链表中等于给定值val的所有节点
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
思路:1,先判断链表是否为空,如果为空就返回空。2,从链表的第二个节点开始判断是否要删除。(循环)3,在判断链表的第一个节点是不是我们要删除的那个节点,如果是则要删除它。4,返回链表

struct ListNode* removeElements(struct ListNode* head, int val) {
    if(head==NULL){
        return NULL;
    }
    struct ListNode *cur=head;
    struct ListNode *node=head->next;
    while(node!=NULL){
        //考虑从第二个节点开始删除
        if(node->val==val){
            cur->next=node->next;
            free(node);
            node=cur->next;
        }
        else{
            cur=node;
            node=node->next;
        }
    }
    //考虑第一节点就是我们要删除的结点
    if(head->val==val){
        struct ListNode *old_head=head;
        head=head->next;
        free(old_head);
    }
    return head;
}

方法二、递归解决

 ListNode* removeElements(ListNode* head, int val) {
        if(head==NULL)
        {
            return head;
        }
        head->next=removeElements(head->next,val);
        return head->val==val?head->next:head;
    }

2、反转一个单链表
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
方法一、1,先判断链表是否为空,如果为空就返回NULL。2,用两个指针一个进行头删,一个进行头插,然后返回开始指向的空链表。

 ListNode* reverseList(ListNode* head) {
        if(head==NULL){
            return NULL;
        }
        struct ListNode *cur=NULL;
        while(head!=NULL){
            //头删
            struct ListNode *node=head;
            head=head->next;
            //头插
            node->next=cur;
            cur=node;
        }
        return cur;
    }

方法二、1,先判断链表是否为空,如果为空就返回空。2,用三个指针,一个指向NULL,一个指向头结点,一个指向头结点的下一个结点。3,然后,在进行反转链表的箭头。

struct ListNode* reverseList(struct ListNode* head) {
    if(head==NULL){
        return NULL;
    }
    struct ListNode *prev=NULL;
    struct ListNode *node=head;
    struct ListNode *cur=head->next;
    while(node!=NULL)
    {
        node->next=prev;
        prev=node;
        node=cur;
        if(cur!=NULL){
            cur=cur->next;
        }
    }
    return prev;
}

3、返回链表的中间结点
问题描述:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
思路:1、先判断链表是否为空,如果等于空就返回空。2、用两个指针来遍历链表,一个fast=fast->next,slow=slow->next,fast=fast->next。当fast为空的时候就跳出循环,然后返回跑到慢的结点。

struct ListNode* middleNode(struct ListNode* head) {
    if(head==NULL){
        return NULL;
    }
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    while(1){
        fast=fast->next;
        if(fast==NULL){
            break;
        }
        slow=slow->next;
        fast=fast->next;
        if(fast==NULL){
            break;
        }
    }
    return slow;
}

方法二

struct ListNode* middleNode(struct ListNode *head)
{
	struct ListNode *fast = head;
	struct ListNode *slow = head;
	while (fast != NULL&&fast->next != NULL)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	return slow;
}

4、输入一个链表,输出该链表中倒数第k个结点
思路:1、先判断链表是否为空,如果为空就返回空。2、用一个指针将链表先跑k个结点(这个时候要注意如果先判断结点是否为空,在进行往下面跑)。3、然后继续用哪个指针和头结点的指针进行循环,当跑的快的那个指针跑完,就返回跑到慢的指针。

ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
    if(pListHead==NULL){
        return NULL;
    }
        struct ListNode *cur=pListHead;
        for(int i=0;i<k;i++){
        //这里先判断,然后在向下一个结点编译循环,不然编译器就会通过不了
            if(cur==NULL){
                return NULL;
            }
            cur=cur->next;
        }
        while(cur!=NULL){
            pListHead=pListHead->next;
            cur=cur->next;
        }
        return pListHead;
    }

5、将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
思路:1,如果一个链表为空就返回另一个链表。2,用一个新的链表,其有两个指针一个指向这个新链表,一个指向这个链表的尾。3,用一个循环,当这两个链表都不为空的时候去遍历这两个链表。4,循环结束,说明一个链表已经遍历完毕,此时在判断遍历没有跑完的那个链表,最后返回那个新链表。

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1==NULL){
        return l2;
    }
    if(l2==NULL){
        return l1;
    }
    struct ListNode *node=l1;
    struct ListNode *cur=l2;
    struct ListNode *result=NULL;
    struct ListNode *result_last=NULL;
    while(node!=NULL&&cur!=NULL){
        if(node->val<=cur->val){
            if(result!=NULL){
                result_last->next=node;
                result_last=node;
            }
            else{
                result_last=node;
                result=node;
            }
            node=node->next;
        }
        else{
            if(result!=NULL){
                result_last->next=cur;
                result_last=cur;
            }
            else{
                result_last=cur;
                result=cur;
            }
            cur=cur->next;
        }
    }
    //走到这里说明一个链表已经遍历完毕
    if(node==NULL){
        result_last->next=cur;
    }
    else{
        result_last->next=node;
    }
    return result;
}

方法二、递归版本

struct ListNode* mergeTwoLists(struct ListNode* l1, struct ListNode* l2) {
    if(l1==NULL){
        return l2;
    }
    if(l2==NULL){
        return l1;
    }
    else{
        if(l1->val<l2->val){
            l1->next=mergeTwoLists(l1->next,l2);
            return l1;
        }
        else{
            l2->next=mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
}

6、给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
思路:首先用两个指针遍历的方法,一个一次走两步,一个走一步。然后遇到两个指针相遇的地方,跳出循环。然后在用两个指针一个指向开头位置,一个指向相遇的位置。然后开始同时走,当这两个相遇的时候就是入环点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==nullptr){
            return nullptr;
        }
        //用两个指针遍历的方法,让第一个指针一次走两步,第二个指针走一步。然后两个指针相遇的位置跳出
        ListNode *fast=head;
        ListNode *slow=head;
        while(1){
            fast=fast->next;
            if(fast==nullptr){
                return nullptr;
            }
            fast=fast->next;
            if(fast==nullptr){
                return nullptr;
            }
            slow=slow->next;
            if(fast==slow){
                break;
            }
        }
        //用两个指针,一个指针指向开始的位置,然另一个指针指向相遇的时候,让他们同时都走一步,直到两个相遇
        ListNode *p1=head;
        ListNode *p2=slow;
        while(p1!=p2){
            p1=p1->next;
            p2=p2->next;
        }
        return p1;
    }
};

猜你喜欢

转载自blog.csdn.net/weixin_43198968/article/details/88710348