面试题(链表1)

在链表中有许多经典的笔试面试题,这里就写几个常见的吧。


以下是头文件中的函数声明:

seqlink.h
#include<stdio.h>

#pragma once

typedef char LinkNodeType; 

typedef struct LinkNode { 
LinkNodeType data; 
struct LinkNode* next; 

} LinkNode; 


// 初始化链表 
void LinkListInit(LinkNode** node); 

void LinkListPrintChar(LinkNode* head,const char* msg);//打印


//尾插一个元素到链表中 
void LinkListPushBack(LinkNode** head, LinkNodeType value); 

void LinkListPopBack(LinkNode** head);//尾删


void LinkListInsert(LinkNode*pos,LinkNodeType value);
    //新节点插入在pos之后
void LinkListEarse(LinkNode** head,LinkNode* pos);//删除指定位置

LinkNode* LinkListFind(LinkNode* head,LinkNodeType value);//查找位置

void LinkListInsertBef(LinkNode** head,LinkNode* pos,LinkNodeType value);
//在pos之前插入元素


size_t LinkListSize(LinkNode* head);//元素个数

void LinkListReveserPrint(LinkNode* head);//逆序打印链表

LinkNode* LinkListReveser(LinkNode** head);//单链表逆置

void LinkListBubbleSort(LinkNode* head);//冒泡排序

LinkNode* LinkListMerge(LinkNode* head1, LinkNode* head2); //合并两个有序链表

LinkNode* HasCycle(LinkNode* head);//判断是否有环

LinkNode* FindMidNode(LinkNode* head); //找到中间节点

LinkNode* FindLastKNode(LinkNode* head, size_t K); //找到倒数第K个节点

void EraseLastKNode(LinkNode** head, size_t K); //删除倒数第K个节点

size_t GetCycleLen(LinkNode* head);//求环长度 

// 如果链表带环, 求出环的入口  
LinkNode* GetCycleEntry(LinkNode* head);

LinkNode* HasCross(LinkNode* head1, LinkNode* head2);//链表相交,且不带环

LinkNode* JosephCycle(LinkNode* head, size_t food); 
//约瑟夫环

LinkNode* HasCrossWithCycle(LinkNode* head1,LinkNode* head2);//链表相交,可能带环

一、单链表逆序打印

这里的逆序打印采用的是递归的方法,

void LinkListReveserPrint(LinkNode* head)
{
    if(head == NULL)
    {
        //空链表
        return;
    }
    LinkListReveserPrint(head->next);
    printf("[%c|%p]\t",head->data,head);
    return;
}

二、单链表的逆置

①:循环头插

这里分享一下思路



②:三指针循环




LinkNode* LinkListReveser(LinkNode** head)
{
    if(head == NULL)
    {
        //error
        return NULL;
    }
    if(*head == NULL)
    {
        //error
        return NULL;
    }
    if((*head)->next == NULL)
    {
       printf("只有一个元素\n");//只有一个元素
       return *head;
    }
    LinkNode* cur = *head;
    LinkNode* Next = *head;
    LinkNode* pre = NULL;
    while(cur)
    {
       Next = cur->next;
       cur->next = pre;
       pre = cur;
       cur = Next;
    }
    *head = pre;
    return *head;
}

三、冒泡排序


void LinkListBubbleSort(LinkNode* head)
{
    if(head == NULL)
    {
        //空链表
        return;
    }
    LinkNode* cur = head;
    LinkNode* pre = NULL;
    while(cur != pre)
    {
        while(cur->next != pre)
        {
            if(cur->data > cur->next->data)
            {
                LinkNodeType tmp = cur->data;
                cur->data = cur->next->data;
                cur->next->data = tmp;
            }
            cur = cur->next;
        }
        pre = cur;
    }
    cur = head;
}

四、合并两个有序链表


LinkNode* LinkListMerge(LinkNode* head1, LinkNode* head2)
{
    if(head1 == NULL)
    {
        //空链表head1
        return head2;
    }
    if(head2 == NULL)
    {
        //head2 == NULL
        return head1;
    }
    LinkNode* head = NULL;
    LinkNode* tail = NULL;
    LinkNode* cur1 = head1;
    LinkNode* cur2 = head2;
    if(cur1->data < cur2->data)
    {
        head = cur1;
        cur1 =cur1->next;
    }
    else
    {
        head = cur2;
        cur2 = cur2->next;
    }
    tail = head;
    while(cur1 != NULL && cur2 != NULL)
    {
        if(cur1->data < cur2->data)
        {
            tail->next = cur1;
            tail = cur1;
            cur1 = cur1->next;
        }
        else
        {
            tail->next = cur2;
            tail = cur2;
            cur2 = cur2->next;
        }
    }
    tail->next = (cur1 != NULL) ? cur1:cur2;
    return head;
}

五、判读是否有环

1.遍历

将头指针保存,从头遍历,时间复杂度On^2   空间复杂度On

2.快慢指针

快指针走2步,慢指针走1步,当2个指针相遇,代表有环

注意:当环长为1,怎么走都不会相遇。


LinkNode* HasCycle(LinkNode* head)
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* fast = head;
    LinkNode* slow = head;
    while(fast->next != NULL && fast->next->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(slow == fast)
        {
            return fast;
        }
    }
    return NULL;
}


六、找到中间节点、倒数第K个节点、删除倒数第K个节点


使用快慢指针的方法可以求解。

1.当快指针走到结尾,慢指针刚好到到中间节点。

2.先让快指针走K步,然后然慢指针和快指针开始同步走,当快指针到达结尾,慢指针就是倒数第K个节点

3.删除倒数第K个节点,首先求链表的长度,如果K刚好等于长度,则采用头删即可,若不是,则找到它之前的那个节点,就是倒数第K+1个节点。

LinkNode* FindMidNode(LinkNode* head)
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* fast = head;
    LinkNode* slow = head;
    while(fast!= NULL)
    {
        if(fast == NULL)
        {
           return slow; 
        }
        fast = fast->next->next;
        slow = slow->next;
    }
}
LinkNode* FindLastKNode(LinkNode* head, size_t K)
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* fast = head;
    LinkNode* slow = head;
    int i = 1;
    for(;i<=K;++i)
    {
        fast = fast->next;
    }
    while(fast->next != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}
void EraseLastKNode(LinkNode** head, size_t K)
{
    if(head == NULL)
    {
        return;
    }
    if(*head == NULL)
    {
        return;
    }
    LinkNode* pre = *head;
    LinkNode* cur = *head;
    size_t len = 0;
    while(cur != NULL)
    {
        len++;
        cur = cur->next;
    }
    if(K > len)
    {
        return;
    }
    if(K == len)
    {
        LinkListPopFourt(head);
    }
    int i = 0;
    for(;i < len-K;++i)
    {
        pre = pre->next;
    }
    LinkListEarse(head,pre);
    //pre->next = pre->next->next;
    //free(pre->next);
}

七、约瑟夫环

前提条件:尾指向头。

1.从头开始,先将第K个节点删除,

2.从删除节点的下一个开始,找到之后的第K个节点删除

LinkNode* JosephCycle(LinkNode* head, size_t food)
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* cur = head;
    while(cur->next != cur)
    {
        int i = 0;
        for (;i < food; ++i)
        {
            cur = cur->next;
        }
        LinkNode* node = cur->next;
        cur->data = node->data;
        cur->next = node->next;
        free(node);
    }
    return cur;
}

八、判断链表是否带环、环长度、环入口


LinkNode* HasCycle(LinkNode* head)//是否带环
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* fast = head;
    LinkNode* slow = head;
    while(fast->next != NULL && fast->next->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(slow == fast)
        {
            return fast;
        }
    }
    return NULL;
}

当快慢指针相遇时,必然有环。可以定义一个指针,从相遇点的下一个节点开始走,并且计数。当返回到相遇点时,查看计数就知道环的长度

size_t GetCycleLen(LinkNode* head)//环长度
{
    if(head == NULL)
    {
        return 0;
    }
    LinkNode* fast = head;
    LinkNode* slow = head;
    while(fast->next != NULL && fast->next->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            size_t count = 0;
            LinkNode* cur = slow->next;
            while(cur != slow)
            {
                ++count;
                cur = cur->next;
            }
            return count;
        }
    }
    return 0;
}
LinkNode* GetCycleEntry(LinkNode* head)//环入口
{
    if(head == NULL)
    {
        return NULL;
    }
    LinkNode* meet = HasCycle(head);//相遇点
    LinkNode* slow = head;//慢指针从头开始走
    LinkNode* fast = meet;//快指针从相遇点开始走
    while(fast != slow)   // 当两个指针相遇时,即入口点
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}





猜你喜欢

转载自blog.csdn.net/duckyloser/article/details/79881582